Authenticate with SSO in a Custom Frontend
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.
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.
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.
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:
// TODO start the authentication comment with the code below.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.
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.
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:
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:
if (!actor_id) {
await sdk.store.customer.create({
email: "customer@example.com",
}, {}, {
Authorization: `Bearer ${token}`,
})
await sdk.auth.refresh({
Authorization: `Bearer ${token}`,
})
}
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.