nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
24.13k stars 3.34k forks source link

Shopify Provider #5234

Open aimproxy opened 2 years ago

aimproxy commented 2 years ago

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 commented 2 years ago

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

heresyrj commented 1 year ago

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

jimjeffers commented 1 year ago

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.

foobarnes commented 1 year ago

@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 },
   );
aimproxy commented 1 year ago

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

foobarnes commented 1 year ago

@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.

xCoBaLTz commented 10 months ago

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.