Authenticate with SSO in a Custom Frontend

EnterpriseBeta

In this guide, you'll learn how to authenticate users with your identity provider in your own frontend, such as a storefront or a custom dashboard, using the OIDC Auth Module Provider.

Looking for social login? SSO with OIDC is intended for the users of your organization, such as admin users authenticating with your company's identity provider.To let customers log in to your storefront with a social account, use the Google or GitHub Auth Module Providers instead. They don't require an enterprise license.

Overview#

The Medusa Admin dashboard implements the SSO flow out of the box, as explained in the Admin SSO guide. In your own frontend, you implement the same flow with the JS SDK.

The steps in this guide apply to any actor type, so replace the user actor type in the examples with customer, or with a custom actor type, as your use case requires.

Tip: Refer to the How SSO Works guide for the requests that make up the flow, and for what Medusa validates in each of them. If a login fails, refer to the Troubleshoot SSO section for the errors that the OIDC provider can return.

Step 1: Render a Button for Each Provider#

Retrieve the providers that the actor type can authenticate with, then render a button for each one that authenticates through a redirect:

Each provider has an id to use in the authentication requests, and a display_name to label its button with. In the React example, startLogin is the function you implement in the next step.

A provider's flow is either credentials, such as the Emailpass provider, or redirect, which is what the OIDC provider uses. So, filtering by flow separates the providers that need a redirect from the ones that accept credentials in a form.

If no provider is returned, render your own login form instead, since no identity provider is available for the actor type.

Note: The route only returns the providers allowed for the actor type you pass, so you don't have to filter them by your authMethodsPerActor configuration.

Step 2: Start the Authentication#

When the user clicks a provider's button, start the authentication and redirect the browser to the returned URL:

Tip: If you followed the React example, replace its // TODO start the authentication comment with the code below.
Code
const result = await sdk.auth.login("user", providerId, {
  callback_url: "https://storefront.example.com/callback",
})

if (typeof result === "object" && result.location) {
  sessionStorage.setItem("sso_provider", providerId)
  window.location.href = result.location
  return
}

The callback_url is the page of your frontend that handles the redirect back from the identity provider, which you implement in the next step. You can omit it, in which case Medusa uses the provider's callback_url option.

Use your frontend's local URL, such as http://localhost:8000/callback, while you develop, and its public URL in production. Register the URL of every environment with your identity provider, since identity providers match redirect URIs exactly.

You also store the provider's ID before redirecting, since the callback page needs it to send the callback request to the same provider.

Warning: Add the callback URL to the redirect URIs of the application you registered with your identity provider, and to the allowed_callback_urls option if you set it. Otherwise, the login fails.

Step 3: Validate the Callback#

The identity provider redirects the user back to your callback URL with a code and a state query parameter. On that page, send the parameters to Medusa:

Medusa validates the identity provider's response, then returns a JWT token for the authenticated identity.

Note: Send the callback request once. The state value is single-use, so a retry with the same parameters fails. That's what the validated ref guards against in the React example, since React remounts the component in strict mode.

Step 4: Create the Actor of the Authenticated Identity#

An auth identity isn't an admin user or a customer. So, the token that the callback returned has an empty actor_id when the user authenticates for the first time, and you must create the record it belongs to before the user can access your application.

If you followed the React example, replace its // TODO create the actor of the identity comment with the code of the section that applies to your actor type.

For Admin Users#

Medusa creates or links the admin user for you through the /auth/{provider_id}/user API route:

The example below decodes the token with the react-jwt package, so install it first:

Then, create or link the admin user:

Code
import { decodeToken } from "react-jwt"

const { actor_id } = decodeToken(token) as {
  actor_id: string
}

if (!actor_id) {
  await sdk.auth.createUser(providerId, {
    Authorization: `Bearer ${token}`,
  })

  await sdk.auth.refresh({
    Authorization: `Bearer ${token}`,
  })
}

Refresh the token after creating the user, since the token you passed isn't bound to it. Learn what the route does, and the conditions it enforces, in the Admin SSO guide.

For Other Actor Types#

For any other actor type, create the record yourself with the token from the callback.

For example, to create a customer:

Code
if (!actor_id) {
  await sdk.store.customer.create({
    email: "customer@example.com",
  }, {}, {
    Authorization: `Bearer ${token}`,
  })

  await sdk.auth.refresh({
    Authorization: `Bearer ${token}`,
  })
}
Warning: Never let the user choose the email of the created record. Read it from the authenticated identity's metadata instead, which holds the claims that your identity provider returned. Otherwise, anyone your identity provider lets through can create a record with another person's email.

For a custom actor type, create your record in a custom API route, then link it to the auth identity. Refer to the Create an Actor Type guide for the full implementation.


Step 5: Use the Token#

Once the token is bound to a record, use it to send authenticated requests. The JS SDK stores it for you, so its methods use it automatically.

Learn more about handling the token, including the jwt and session authentication types, in the JS SDK Authentication guide.

Was this page helpful?
Ask Bloom
For assistance in your development, use Claude Code Plugins or Docs MCP server in Cursor, VSCode, etc...FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break
⇧↵