next-auth icon indicating copy to clipboard operation
next-auth copied to clipboard

Shopify Provider

Open aimproxy opened this issue 2 years ago • 1 comments

Description 📓

I was implementing a Shopify Provider so that customers can sign in/up with their Shopify Store! More on Shopify OAuth flow: https://shopify.dev/apps/auth/oauth/getting-started

Step 1: Authorise a Shop

https://{shop}.myshopify.com/admin/oauth/authorize?client_id={api_key}&scope={scopes}&redirect_uri={redirect_uri}&state={nonce}&grant_options[]={access_mode}

Shopify responds with an URL like this::

https://example.org/some/redirect/uri?code={authorization_code}&hmac=da9d83c171400a41f8db91a950508985&host={base64_encoded_hostname}&shop={shop_origin}&state={nonce}&timestamp=1409617544

Step 2: Get an Access Token

POST https://{shop}.myshopify.com/admin/oauth/access_token?client_id={API_KEY}&client_secret={API_SECRET_KEY}&code={authorization_code}
Parameter Description
client_id The API key for the app, as defined in the Partner Dashboard.
client_secret The API secret key for the app, as defined in the Partner Dashboard.
code The authorization code provided in the redirect. (Received in Step 1)

Step 3: Get Shop Configuration

GET https://${shop}/admin/api/${apiVersion}/shop.json

Headers

X-Shopify-Access-Token | The access token received in Step 2

More on that mather: https://shopify.dev/api/admin-rest/2022-07/resources/shop

aimproxy avatar Aug 27 '22 10:08 aimproxy

@balazsorban44 I left some comments on the commit I would like some help!

aimproxy avatar Aug 27 '22 11:08 aimproxy

@aimproxy Did you have any luck setting up Shopify as a provider?

heresyrj avatar Nov 17 '22 19:11 heresyrj

Just chiming in to see if any progress was made here. Seems like you can't dynamically setup a subdomain for a given provider's authorizationUrl.

jimjeffers avatar Mar 23 '23 19:03 jimjeffers

@jimjeffers I was able to implement this using advanced initialization

[...nextauth].js:

import NextAuth from 'next-auth';

export default async function auth(req, res) {
  const { shopifyShopName } = req.query;

  // Do whatever you want here, before the request is passed down to `NextAuth`
  return await NextAuth(req, res, {
    providers: [
      {
        id: 'shopify',
        name: 'Shopify',
        type: 'oauth',
        version: '2.0',
        clientId: process.env.SHOPIFY_CLIENT_ID,
        clientSecret: process.env.SHOPIFY_CLIENT_SECRET,
        authorization: {
          url: `https://${shopifyShopName}.myshopify.com/admin/oauth/authorize`,
          params: {
            scope: 'read_orders, read_all_orders',
          },
        },
      }
      // ...add more providers here
    ],
  });
}

Calling signIn:

   signIn(
      "shopify",
      {
        redirect: false,
        callbackUrl: "...",
      },
      { shopifyShopName: "some-cool-shop-name },
   );

foobarnes avatar May 18 '23 18:05 foobarnes

@foobarnes yeah thats it, will you submit a PR? Have you tested it?

aimproxy avatar May 19 '23 19:05 aimproxy

@aimproxy I'm still waiting on approval from Shopify for the permissions, but it does correctly route to the authorization url.

I may be wrong, but I don't think this requires a PR. This is how to address @jimjeffers's comment.

foobarnes avatar May 22 '23 18:05 foobarnes

Just adding a iteration to the solution that provides the ability to use the Shopify OAuth along side other providers.

[...nextauth]/route.ts

import { authOptions } from "@/lib/auth";
import NextAuth from "next-auth/next";
import { NextRequest } from "next/server";

const handler = async (req: NextRequest, res: any) => {
  const shopifyShopName = req.nextUrl.searchParams.get("shopifyShopName") || "";

  authOptions.providers[
    authOptions.providers.length - 1
    // @ts-ignore
  ].authorization.url = `https://${shopifyShopName}/admin/oauth/authorize`;

  // @ts-ignore
  return await NextAuth(req, res, authOptions);
};

export { handler as GET, handler as POST };

lib/auth.ts

export const authOptions = {
  providers: [
    CredentialsProvider({ ... }),
    GoogleProvider({ ... }),
    {
      id: "shopify",
      name: "Shopify",
      type: "oauth",
      version: "2.0",
      clientId: process.env.SHOPIFY_CLIENT_ID,
      clientSecret: process.env.SHOPIFY_CLIENT_SECRET,
      authorization: {
        params: {
          scope:
            "read_products, read_orders, read_all_orders, read_customers, <or whichever scopes you are interested in>",
          redirect_uri: `${process.env.NEXTAUTH_URL}/api/auth/provider/shopify`,
        },
      },
    },
  ],
  callbacks: { ... },
  pages: { ... },
}; 

For context, my solution provides a redirect to an exposed API under /api/auth/provider/shopify which handles some post processing of the access token after retrieving it from Shopify.

xCoBaLTz avatar Nov 14 '23 16:11 xCoBaLTz