docs: updates and improvements to JS SDK guides (#12026)

This commit is contained in:
Shahed Nasser
2025-03-28 12:45:45 +02:00
committed by GitHub
parent b1b3b48474
commit 3fa19ae4f1
15 changed files with 18707 additions and 17423 deletions
+3 -35
View File
@@ -136,7 +136,7 @@ If authenticated successfully, an object is returned in the response with the pr
To use the JWT token, pass it in the authorization bearer header.
If you're using the JS SDK, call the `client.setToken` method, passing it the token. The JS SDK will automatically pass the token in the authorization header for subsequent requests.
If you're using the JS SDK, the `login` method automatically sets the token for you and passes it to subsequent requests. You can also set the token manually using the `client.setToken` method.
<Note>
@@ -148,26 +148,10 @@ Make sure you've set the `auth.type` configuration of the JS SDK to `jwt` to use
<DividedMarkdownCode>
<CodeTabs group="request-examples">
<CodeTab label="JS SDK" value="js-sdk">
```js title="Use JWT token"
sdk.client.setToken(jwt_token)
```
</CodeTab>
<CodeTab label="cURL" value="curl">
```bash title="Use JWT token"
Authorization: Bearer {jwt_token}
```
</CodeTab>
</CodeTabs>
</DividedMarkdownCode>
</DividedMarkdownLayout>
@@ -321,7 +305,7 @@ To obtain a cookie session ID, you must have a [JWT token for bearer authenticat
<DividedMarkdownContent>
Then, if you're using the JS SDK, make sure the `auth.type` configuration is set to `session`, as explained in the [JS SDK configurations](!resources!/js-sdk#js-sdk-configurations) guide. After that, you only need to set the token using the `client.setToken` method, and the JS SDK will handle the rest.
Then, if you're using the JS SDK, make sure the `auth.type` configuration is set to `session`, as explained in the [JS SDK configurations](!resources!/js-sdk#js-sdk-configurations) guide. The `auth.login` method will handle setting the session cookie and passing it in subsequent requests.
If you're not using the JS SDK, send a request to the [session authentication API route](#auth_postsession). To view the cookie session ID, pass the `-v` option to the `curl` command.
@@ -329,27 +313,11 @@ If you're not using the JS SDK, send a request to the [session authentication AP
<DividedMarkdownCode>
<CodeTabs group="request-examples">
<CodeTab label="JS SDK" value="js-sdk">
```js title="Obtain cookie session"
sdk.client.setToken(jwt_token)
```
</CodeTab>
<CodeTab label="cURL" value="curl">
```bash title="Obtain cookie session"
curl -v -X POST '{backend_url}/auth/session' \
--header 'Authorization: Bearer {jwt_token}'
```
</CodeTab>
</CodeTabs>
</DividedMarkdownCode>
</DividedMarkdownLayout>
@@ -379,7 +347,7 @@ Set-Cookie: connect.sid=s%3A2Bu8B...;
#### How to Use the Cookie Session ID in cURL
If you're using the JS SDK, it will pass the cookie session with every request automatically after you use the `client.setToken` method.
If you're using the JS SDK, it will pass the cookie session with every request automatically after you use the `auth.login` method.
If you're not using the JS SDK, copy the value after `connect.sid` (without the `;` at the end) and pass
it as a cookie in subsequent requests.
+3 -35
View File
@@ -136,7 +136,7 @@ If authenticated successfully, an object is returned in the response with the pr
To use the JWT token, pass it in the authorization bearer header.
If you're using the JS SDK, call the `client.setToken` method, passing it the token. The JS SDK will automatically pass the token in the authorization header for subsequent requests.
If you're using the JS SDK, the `login` method automatically sets the token for you and passes it to subsequent requests. You can also set the token manually using the `client.setToken` method.
<Note>
@@ -148,26 +148,10 @@ Make sure you've set the `auth.type` configuration of the JS SDK to `jwt` to use
<DividedMarkdownCode>
<CodeTabs group="request-examples">
<CodeTab label="JS SDK" value="js-sdk">
```js title="Use JWT token"
sdk.client.setToken(jwt_token)
```
</CodeTab>
<CodeTab label="cURL" value="curl">
```bash title="Use JWT token"
Authorization: Bearer {jwt_token}
```
</CodeTab>
</CodeTabs>
</DividedMarkdownCode>
</DividedMarkdownLayout>
@@ -192,7 +176,7 @@ To obtain a cookie session ID, you must have a [JWT token for bearer authenticat
<DividedMarkdownContent>
Then, if you're using the JS SDK, make sure the `auth.type` configuration is set to `session`, as explained in the [JS SDK configurations](!resources!/js-sdk#js-sdk-configurations) guide. After that, you only need to set the token using the `client.setToken` method, and the JS SDK will handle the rest.
Then, if you're using the JS SDK, make sure the `auth.type` configuration is set to `session`, as explained in the [JS SDK configurations](!resources!/js-sdk#js-sdk-configurations) guide. The `auth.login` method will handle setting the session cookie and passing it in subsequent requests.
If you're not using the JS SDK, send a request to the [session authentication API route](#auth_postsession). To view the cookie session ID, pass the `-v` option to the `curl` command.
@@ -200,27 +184,11 @@ If you're not using the JS SDK, send a request to the [session authentication AP
<DividedMarkdownCode>
<CodeTabs group="request-examples">
<CodeTab label="JS SDK" value="js-sdk">
```js title="Obtain cookie session"
sdk.client.setToken(jwt_token)
```
</CodeTab>
<CodeTab label="cURL" value="curl">
```bash title="Obtain cookie session"
curl -v -X POST '{backend_url}/auth/session' \
--header 'Authorization: Bearer {jwt_token}'
```
</CodeTab>
</CodeTabs>
</DividedMarkdownCode>
</DividedMarkdownLayout>
@@ -250,7 +218,7 @@ Set-Cookie: connect.sid=s%3A2Bu8B...;
#### How to Use the Cookie Session ID in cURL
If you're using the JS SDK, it will pass the cookie session with every request automatically after you use the `client.setToken` method.
If you're using the JS SDK, it will pass the cookie session with every request automatically after you use the `auth.login` method.
If you're not using the JS SDK, copy the value after `connect.sid` (without the `;` at the end) and pass
it as a cookie in subsequent requests.
@@ -20,6 +20,8 @@ Long-running workflows are useful if:
- A task takes too long. For example, you're importing data from a CSV file.
- The workflow's steps wait for an external action to finish before resuming execution. For example, before you import the data from the CSV file, you wait until the import is confirmed by the user.
- You want to retry workflow steps after a long period of time. For example, you want to retry a step that processes a payment every day until the payment is successful.
- Refer to the [Retry Failed Steps chapter](../retry-failed-steps/page.mdx) for more information.
---
@@ -74,11 +76,12 @@ The second step has in its configuration object `async` set to `true` and it doe
So, when you execute the `hello-world` workflow, it continues its execution in the background once it reaches the second step.
<Note>
### When is a Workflow Considered Long-Running?
A workflow is also considered long-running if one of its steps has their `retryInterval` option set as explained in [this chapter](../retry-failed-steps/page.mdx).
A workflow is also considered long-running if:
</Note>
- One of its steps has its `async` configuration set to `true` and doesn't return a step response.
- One of its steps has its `retryInterval` option set as explained in the [Retry Failed Steps chapter](../retry-failed-steps/page.mdx).
---
@@ -6,6 +6,18 @@ export const metadata = {
In this chapter, youll learn how to configure steps to allow retrial on failure.
## What is a Step Retrial?
A step retrial is a mechanism that allows a step to be retried automatically when it fails. This is useful for handling transient errors, such as network issues or temporary unavailability of a service.
When a step fails, the workflow engine can automatically retry the step a specified number of times before marking the workflow as failed. This can help improve the reliability and resilience of your workflows.
You can also configure the interval between retries, allowing you to wait for a certain period before attempting the step again. This is useful when the failure is due to a temporary issue that may resolve itself after some time.
For example, if a step captures a payment, you may want to retry it the next day until the payment is successful or the maximum number of retries is reached.
---
## Configure a Steps Retrial
By default, when an error occurs in a step, the step and the workflow fail, and the execution stops.
@@ -81,8 +93,33 @@ const step1 = createStep(
)
```
In this example, if the step fails, it will be retried after two seconds.
### Maximum Retry Interval
The `retryInterval` property's maximum value is [Number.MAX_SAFE_INTEGER](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER). So, you can set a very long wait time before the step is retried, allowing you to retry steps after a long period.
For example, to retry a step after a day:
```ts title="src/workflows/hello-world.ts" highlights={[["5"]]}
const step1 = createStep(
{
name: "step-1",
maxRetries: 2,
retryInterval: 86400, // 1 day
},
async () => {
// ...
}
)
```
In this example, if the step fails, it will be retried after `86400` seconds (one day).
### Interval Changes Workflow to Long-Running
By setting `retryInterval` on a step, a workflow becomes a [long-running workflow](../long-running-workflow/page.mdx) that runs asynchronously in the background. So, you won't receive its result or errors immediately when you execute the workflow.
By setting `retryInterval` on a step, a workflow that uses that step becomes a [long-running workflow](../long-running-workflow/page.mdx) that runs asynchronously in the background. This is useful when creating workflows that may fail and should run for a long time until they succeed, such as waiting for a payment to be captured or a shipment to be delivered.
However, since the long-running workflow runs in the background, you won't receive its result or errors immediately when you execute the workflow.
Instead, you must subscribe to the workflow's execution using the Workflow Engine Module Service. Learn more about it in [this chapter](../long-running-workflow/page.mdx#access-long-running-workflow-status-and-result).
+2 -2
View File
@@ -4,7 +4,7 @@ export const generatedEditDates = {
"app/learn/deployment/page.mdx": "2025-03-11T14:53:25.540Z",
"app/learn/page.mdx": "2025-03-11T10:17:39.318Z",
"app/learn/fundamentals/modules/commerce-modules/page.mdx": "2024-12-09T10:46:29.339Z",
"app/learn/fundamentals/workflows/retry-failed-steps/page.mdx": "2024-12-04T07:37:59.823Z",
"app/learn/fundamentals/workflows/retry-failed-steps/page.mdx": "2025-03-28T07:15:19.388Z",
"app/learn/fundamentals/workflows/workflow-hooks/page.mdx": "2024-12-09T10:44:33.781Z",
"app/learn/debugging-and-testing/logging/page.mdx": "2024-09-30T08:43:53.135Z",
"app/learn/storefront-development/page.mdx": "2024-12-10T09:11:04.993Z",
@@ -32,7 +32,7 @@ export const generatedEditDates = {
"app/learn/fundamentals/workflows/conditions/page.mdx": "2025-01-27T08:45:19.027Z",
"app/learn/fundamentals/modules/module-link-directions/page.mdx": "2024-07-24T09:16:01+02:00",
"app/learn/fundamentals/admin/page.mdx": "2025-03-21T08:25:13.754Z",
"app/learn/fundamentals/workflows/long-running-workflow/page.mdx": "2025-03-18T08:02:14.085Z",
"app/learn/fundamentals/workflows/long-running-workflow/page.mdx": "2025-03-28T07:02:34.467Z",
"app/learn/fundamentals/workflows/constructor-constraints/page.mdx": "2025-02-12T13:55:33.437Z",
"app/learn/fundamentals/data-models/write-migration/page.mdx": "2025-03-24T06:41:48.915Z",
"app/learn/fundamentals/data-models/manage-relationships/page.mdx": "2025-03-18T15:09:18.688Z",
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,414 @@
import { Table, CodeTabs, CodeTab } from "docs-ui"
export const metadata = {
title: `Authentication in JS SDK`,
}
# {metadata.title}
In this guide, you'll learn about the default authentication setup when using the JS SDK, how to customize it, and how to send authenticated requests to Medusa's APIs.
## Default Authentication Settings in JS SDK
The JS SDK facilitates authentication by storing and managing the necessary authorization headers or sessions for you.
There are three types of authentication:
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>
Method
</Table.HeaderCell>
<Table.HeaderCell>
Description
</Table.HeaderCell>
<Table.HeaderCell>
When to use
</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
JWT token (default)
</Table.Cell>
<Table.Cell>
When you log in a user, the JS SDK stores the JWT for you and automatically includes it in the headers of all requests to the Medusa API. This means you don't have to manually set the authorization header for each request. When the user logs out, the SDK clears the stored JWT.
</Table.Cell>
<Table.Cell>
- You need stateless authentication. For example, you're building a mobile storefront with React Native.
- Keep in mind: when logging out, the JS SDK clears the token from storage. However, if the user still has access to the token, they can still send authenticated requests.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
Cookie session
</Table.Cell>
<Table.Cell>
When you log in a user, the JS SDK stores the session cookie for you and automatically includes it in the headers of all requests to the Medusa API. This means you don't have to manually set the authorization header for each request. When the user logs out, the SDK destroys the session cookie using Medusa's API.
</Table.Cell>
<Table.Cell>
- You need stateful authentication. For example, you're building a web storefront with Next.js or customizations in Medusa Admin.
- You want to ensure the session is revoked when the user logs out.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
Secret API Key
</Table.Cell>
<Table.Cell>
Only available for admin users. You pass the API key in the JS SDK configurations, and it's always passed in the headers of all requests to the Medusa API.
</Table.Cell>
<Table.Cell>
- You're authenticating the admin user.
- Keep in mind: the API key must be stored securely and not exposed to the client-side code.
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
---
## JS SDK Authentication Configurations
The JS SDK provides a set of configurations to customize the authentication method and storage. You can set these configurations when initializing the SDK.
<Note title="Tip">
For a full list of JS SDK configurations and their possible values, check out the [JS SDK Overview](../../page.mdx#js-sdk-configurations) documentation.
</Note>
### Authentication Type
By default, the JS SDK uses JWT token (`jwt`) authentication. You can change the authentication method or type by setting the `auth.type` configuration to `session`.
For example:
```ts
import Medusa from "@medusajs/js-sdk"
export const sdk = new Medusa({
// ...
auth: {
type: "session",
},
})
```
To use a secret API key instead, pass it in the `apiKey` configuration instead:
```ts
import Medusa from "@medusajs/js-sdk"
export const sdk = new Medusa({
// ...
apiKey: "your-api-key",
})
```
The provided API key will be passed in the headers of all requests to the Medusa API.
### Change JWT Authentication Storage
By default, the JS SDK stores the JWT token in the `localStorage` under the `medusa_auth_token` key.
Some environments or use cases may require a different storage method or `localStorage` may not be available. For example, if you're building a mobile app with React Native, you might want to use `AsyncStorage` instead of `localStorage`.
You can change the storage method by setting the `auth.jwtTokenStorageMethod` configuration to one of the following values:
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>
Value
</Table.HeaderCell>
<Table.HeaderCell>
Description
</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
`local` (default)
</Table.Cell>
<Table.Cell>
Uses `localStorage` to store the JWT token.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`session`
</Table.Cell>
<Table.Cell>
Uses `sessionStorage` to store the JWT token. This means the token will be cleared when the user closes the browser tab or window.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`memory`
</Table.Cell>
<Table.Cell>
Uses a memory storage method. This means the token will be cleared when the user refreshes the page or closes the browser tab or window. This is also useful when using the JS SDK in a server-side environment.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`custom`
</Table.Cell>
<Table.Cell>
Uses a custom storage method. This means you can provide your own implementation of the storage method. For example, you can use `AsyncStorage` in React Native or any other storage method that fits your use case.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`nostore`
</Table.Cell>
<Table.Cell>
Does not store the JWT token. This means you have to manually set the authorization header for each request. This is useful when you want to use a different authentication method or when you're using the JS SDK in a server-side environment.
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
#### Custom Authentication Storage in JS SDK
To use a custom storage method, you need to set the `auth.jwtTokenStorageMethod` configuration to `custom` and provide your own implementation of the storage method in the `auth.storage` configuration.
The object or class passed to `auth.storage` configuration must have the following methods:
- `setItem`: A function that accepts a key and value to store the JWT token.
- `getItem`: A function that accepts a key to retrieve the JWT token.
- `removeItem`: A function that accepts a key to remove the JWT token from storage.
For example, to use `AsyncStorage` in React Native:
```ts
import AsyncStorage from "@react-native-async-storage/async-storage"
import Medusa from "@medusajs/js-sdk"
let MEDUSA_BACKEND_URL = "http://localhost:9000"
if (process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL) {
MEDUSA_BACKEND_URL = process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL
}
export const sdk = new Medusa({
baseUrl: MEDUSA_BACKEND_URL,
debug: process.env.NODE_ENV === "development",
publishableKey: process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY,
auth: {
type: "jwt",
jwtTokenStorageMethod: "custom",
storge: AsyncStorage,
},
})
```
In this example, you specify the `jwtTokenStorageMethod` as `custom` and set the `storage` configuration to `AsyncStorage`. This way, the JS SDK will use `AsyncStorage` to store and manage the JWT token instead of `localStorage`.
### Change Cookie Session Credentials Options
By default, if you set the `auth.type` configuration in the JS SDK to `session`, the JS SDK will pass the `credentials: include` option in the underlying [fetch requests](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#including_credentials).
However, some platforms or environments may not support passing this option. For example, if you're using the JS SDK in a server-side environment or a mobile app, you might want to set the `credentials` option to `same-origin` or `omit`.
You can change the `credentials` option by setting the `auth.fetchCredentials` configuration to one of the following values:
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>
Value
</Table.HeaderCell>
<Table.HeaderCell>
Description
</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
`include` (default)
</Table.Cell>
<Table.Cell>
Passes the `credentials: include` option in the fetch requests. This means the JS SDK will include cookies and authorization headers in the requests to the Medusa API.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`same-origin`
</Table.Cell>
<Table.Cell>
Passes the `credentials: same-origin` option in the fetch requests. This means the JS SDK will include cookies and authorization headers in the requests to the Medusa API only if the request is made to the same origin as the current page.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`omit`
</Table.Cell>
<Table.Cell>
Passes the `credentials: omit` option in the fetch requests. This means the JS SDK will not include cookies or authorization headers in the requests to the Medusa API.
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
For example:
```ts
import Medusa from "@medusajs/js-sdk"
export const sdk = new Medusa({
// ...
auth: {
type: "session",
fetchCredentials: "same-origin",
},
})
```
In this example, you set the `fetchCredentials` configuration to `same-origin`, which means the JS SDK will include cookies and authorization headers in the requests to the Medusa API only if the request is made to the same origin as the current page.
---
## Sending Authenticated Requests in JS SDK
<Note>
If you're using an API key for authentication, you don't need to log in the user.
</Note>
The JS SDK has an `auth.login` method that allows you to login admin users, customers, or any [actor type](../../../commerce-modules/auth/auth-identity-and-actor-types/page.mdx) with any [auth provider](../../../commerce-modules/auth/auth-providers/page.mdx).
Not only does this method log in the user, but it also stores the JWT token or session cookie for you and automatically includes it in the headers of all requests to the Medusa API. This means you don't have to manually set the authorization header for each request.
For example:
<CodeTabs group="auth-actor-type">
<CodeTab label="Admin User" value="admin">
```ts
sdk.auth.login("user", "emailpass", {
email,
password,
})
.then((data) => {
if (typeof data === "object" && data.location){
// authentication requires more actions
}
// user is authenticated
})
.catch((error) => {
// authentication failed
})
```
</CodeTab>
<CodeTab label="Customer" value="customer">
```ts
sdk.auth.login("customer", "emailpass", {
email,
password,
})
.then((data) => {
if (typeof data === "object" && data.location){
// authentication requires more actions
}
// customer is authenticated
})
.catch((error) => {
// authentication failed
})
```
</CodeTab>
<CodeTab label="Custom" value="custom">
```ts
sdk.auth.login("manager", "emailpass", {
email,
password,
})
.then((data) => {
if (typeof data === "object" && data.location){
// authentication requires more actions
}
// manager is authenticated
})
.catch((error) => {
// authentication failed
})
```
</CodeTab>
</CodeTabs>
In this example, you call the `sdk.auth.login` method passing it the actor type (for example, `user`), the provider (`emailpass`), and the credentials.
If the authentication is successful, there are two types of returned data:
- An object with a `location` property: This means the authentication requires more actions, which happens when using third-party authentication providers, such as [Google](../../../commerce-modules/auth/auth-providers/google/page.mdx). In that case, you need to redirect the customer to the location to complete their authentication.
- Refer to the [Third-Party Login in Storefront](../../../storefront-development/customers/third-party-login/page.mdx) guide for an example implementation.
- A string: This means the authentication was successful, and the user is logged in. The JS SDK automatically stores the JWT token or session cookie for you and includes it in the headers of all requests to the Medusa API. All requests you send afterwards will be authenticated with the stored token or session cookie.
If the authentication fails, the `catch` block will be executed, and you can handle the error accordingly.
You can learn more about this method in the [auth.login reference](/references/js-sdk/auth/login).
### Manually Set JWT Token
If you need to set the JWT token manually, you can use the `sdk.client.setToken` method. All subsequent requests will be authenticated with the provided token.
For example:
```ts
sdk.client.setToken("your-jwt-token")
// all requests sent after this will be authenticated with the provided token
```
You can also clear the token manually as explained in the [Manually Clearing JWT Token](#manually-clearing-jwt-token) section.
---
## Logout in JS SDK
<Note>
If you're using an API key for authentication, you can't log out the user. You'll have to unset the API key in the JS SDK configurations.
</Note>
The JS SDK has an `auth.logout` method that allows you to log out the currently authenticated user.
If the JS SDK's authentication type is `jwt`, the method will only clear the stored JWT token from the local storage. If the authentication type is `session`, the method will destroy the session cookie using Medusa's `/auth/session` API route.
Any request sent after logging out will not be authenticated, and you will need to log in again to authenticate the user.
For example:
```ts
sdk.auth.logout()
.then(() => {
// user is logged out
})
```
You can learn more about this method in the [auth.logout reference](/references/js-sdk/auth/logout).
### Manually Clearing JWT Token
If you need to clear the JWT token manually, you can use the `sdk.client.clearToken` method. This will remove the token from the local storage and all subsequent requests will not be authenticated.
For example:
```ts
sdk.client.clearToken()
// all requests sent after this will not be authenticated
```
+118 -48
View File
@@ -209,7 +209,7 @@ The `Medusa` initializer accepts as a parameter an object with the following pro
- `getItem`: A function that accepts a key to retrieve the JWT token.
- `removeItem`: A function that accepts a key to remove the JWT token from storage.
Learn more in [this section](#use-custom-storage).
Learn more in the [Authentication](./auth/overview/page.mdx#custom-authentication-storage-in-js-sdk) guide.
</Table.Cell>
<Table.Cell>
@@ -239,7 +239,7 @@ The `Medusa` initializer accepts as a parameter an object with the following pro
</Table.Cell>
<Table.Cell>
`local`
`include`
</Table.Cell>
</Table.Row>
@@ -321,6 +321,14 @@ The `Medusa` initializer accepts as a parameter an object with the following pro
---
## Manage Authentication in JS SDK
The JS SDK supports different types of authentication methods and allow you to flexibly configure them.
To learn more about configuring authentication in the JS SDK and sending authenticated requests, refer to the [Authentication](./auth/overview/page.mdx) guide.
---
## Send Requests to Custom Routes
The sidebar shows the different methods that you can use to send requests to Medusa's API routes.
@@ -375,6 +383,114 @@ The method returns a Promise that, when resolved, has the data returned by the r
---
## Handle Errors
If an error occurs in a request, the JS SDK throws a `FetchError` object. This object has the following properties:
- `status`: The HTTP status code of the response.
- `statusText`: The error code. For example, `Unauthorized`.
- `message`: The error message. For example, `Invalid credentials`.
You can use these properties to handle errors in your application.
For example:
<CodeTabs group="request-type">
<CodeTab label="Promise" value="promise">
```ts
sdk.store.customer.listAddress()
.then(({ addresses, count, offset, limit }) => {
// no errors occurred
// do something with the data
console.log(addresses)
})
.catch((error) => {
const fetchError = error as FetchError
if (fetchError.statusText === "Unauthorized") {
// redirect to login page
} else {
// handle other errors
}
})
```
</CodeTab>
<CodeTab label="Async/Await" value="async-await">
```ts
try {
const {
addresses,
count,
offset,
limit
} = await sdk.store.customer.listAddress()
// no errors occurred
// do something with the data
console.log(addresses)
} catch (error) {
const fetchError = error as FetchError
if (fetchError.statusText === "Unauthorized") {
// redirect to login page
} else {
// handle other errors
}
}
```
</CodeTab>
</CodeTabs>
In the example above, you handle errors in two ways:
- Since the JS SDK's methods return a Promise, you can use the `catch` method to handle errors.
- You can use the `try...catch` statement to handle errors when using `async/await`. This is useful when you're executing the methods as part of a larger function.
In the `catch` method or statement, you have access to the error object of type `FetchError`.
An example of handling the error is to check if the error's `statusText` is `Unauthorized`. If so, you can redirect the customer to the login page. Otherwise, you can handle other errors by showing an alert, for example.
---
## Pass Headers in Requests
There are two ways to pass custom headers in requests when using the JS SDK:
1. Using the `globalHeaders` configuration: This is useful when you want to pass the same headers in all requests. For example, if you want to pass a custom header for tracking purposes:
```ts
const sdk = new Medusa({
// ...
globalHeaders: {
"x-tracking-id": "123456789",
},
})
```
2. Using the headers parameter of a specific method. Every method has as a last parameter a headers parameter, which is an object of headers to pass in the request. This is useful when you want to pass a custom header in specific requests. For example, to disable HTTP compression for specific requests:
```ts
sdk.store.product.list({
limit,
offset,
}, {
"x-no-compression": "false",
})
```
In the example above, you pass the `x-no-compression` header in the request to disable HTTP compression. You pass it as the last parameter of the `sdk.store.product.list` method.
<Note title="Tip">
The JS SDK appends request-specific headers to authentication headers and headers configured in the `globalHeaders` configuration. So, in the example above, the `x-no-compression` header is passed in the request along with the authentication headers and any headers configured in the `globalHeaders` configuration.
</Note>
---
## Medusa JS SDK Tips
### Use Tanstack (React) Query in Admin Customizations
@@ -509,49 +625,3 @@ revalidateTag("products")
```
Learn more in the [Next.js documentation](https://nextjs.org/docs/app/building-your-application/caching#fetch-optionsnexttags-and-revalidatetag).
### Use Custom Storage
<Note>
The `auth.storage` configuration is only available after Medusa v2.5.1.
</Note>
If you're using the JS SDK in an environment where Local Storage or Session Storage isn't available, such as in a React Native application, you can define custom logic to store the JWT token.
To do that, set the `auth.jwtTokenStorageMethod` configuration to `custom` and define the `auth.storage` configuration with the custom logic to store the JWT token.
For example, if you're using React Native's `AsyncStorage`:
```ts title="config.ts"
import AsyncStorage from "@react-native-async-storage/async-storage"
import Medusa from "@medusajs/js-sdk"
let MEDUSA_BACKEND_URL = "http://localhost:9000"
if (process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL) {
MEDUSA_BACKEND_URL = process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL
}
export const sdk = new Medusa({
baseUrl: MEDUSA_BACKEND_URL,
debug: process.env.NODE_ENV === "development",
publishableKey: process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY,
auth: {
type: "jwt",
jwtTokenStorageMethod: "custom",
storge: AsyncStorage,
},
})
```
In the `auth` configuration, you specify the `type` as `jwt`, the `jwtTokenStorageMethod` as `custom`, and the `storage` as `AsyncStorage`. So, the SDK uses `AsyncStorage` to store the JWT token.
#### Custom Storage Methods
The object or class passed to `auth.storage` configuration must have the following methods:
- `setItem`: A function that accepts a key and value to store the JWT token.
- `getItem`: A function that accepts a key to retrieve the JWT token.
- `removeItem`: A function that accepts a key to remove the JWT token from storage.
@@ -25,62 +25,21 @@ This guide covers login using email and password. For authentication with third-
There are two ways to login a customer in your storefront:
1. [Using a JWT token](#1-using-a-jwt-token). This JWT token is obtained from the `/auth/customer/emailpass` API route and is used as a bearer token in the authorization header of all requests.
- When using the JS SDK, you can set the token using the `client.setToken` method. Then, the JS SDK will use that token in the authorization header of all subsequent requests.
2. [Using a cookie session](#2-using-a-cookie-session). This method uses the `/auth/session` API route to set the authenticated session ID in the cookies.
- When using the JS SDK, you can configure it to use sessions to manage authentication and pass the session ID cookie in all requests.
1. Using a JWT token. This JWT token is obtained from the `/auth/customer/emailpass` API route and is used as a bearer token in the authorization header of all requests.
2. Using a cookie session. This method uses the `/auth/session` API route to set the authenticated session ID in the cookies.
The next sections explain how to implement each method.
The JS SDK simplifies the login approach in a single `auth.login` method. The upcoming sections explain the authentication approach whether you're using the JS SDK or not.
### Which Method Should You Use?
### Which Authentication Method Should You Use?
The authentication method you choose depends on your use case and the type of storefront you're building.
When making a choice, consider the following:
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>
Method
</Table.HeaderCell>
<Table.HeaderCell>
When to use
</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
JWT token
</Table.Cell>
<Table.Cell>
- You need stateless authentication. For example, you're building a mobile storefront with React Native.
- Keep in mind: when logging out, you must clear the token in the JS SDK using the `client.clearToken` method. If the user still has access to the token, they can still send authenticated requests.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
Cookie session
</Table.Cell>
<Table.Cell>
- You need stateful authentication. For example, you're building a web storefront with Next.js.
- You want to ensure the session is revoked when the user logs out.
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
Refer to the [JS SDK Authentication](../../../js-sdk/auth/overview/page.mdx) guide to learn more about the differences between JWT and session authentication and which one is best for your use case.
### JS SDK Authentication Configuration
Before implementing the login flow, you need to configure in the JS SDK the authentication method you're using in your storefront. This defines how the JS SDK will handle sending authenticated requests after the customer is authenticated.
<Note>
If you're not using the JS SDK, the next sections explain the general approach of how to pass the necessary authentication headers or cookies in your requests.
</Note>
For example, add the following configuration to your JS SDK initialization:
<Note title="Tip">
@@ -118,26 +77,23 @@ export const sdk = new Medusa({
The JS SDK will now pass the JWT token or the session ID cookie in the authorization header of all subsequent requests based on the authentication method you've configured.
Refer to the [JS SDK Authentication](../../../js-sdk/auth/overview/page.mdx#custom-authentication-storage-in-js-sdk) guide for more information about these configurations, as well as other authentication configurations.
<Note title="Tip">
By default, when you choose the `jwt` method, the JWT token is stored in the browser's `localStorage`. However, you can change how the token is stored, which is useful in environments where `localStorage` is not available. For example, in React Native.
To learn how to change the storage method with an example for a React Native storefront, refer to the [JS SDK documentation](../../../js-sdk/page.mdx#custom-storage-methods).
To learn how to change the storage method with an example for a React Native storefront, refer to the [JS SDK Authentication](../../../js-sdk/auth/overview/page.mdx#custom-authentication-storage-in-js-sdk) guide.
</Note>
---
## 1. Using a JWT Token
## Authentication with JS SDK
The first authentication approach is to pass an authenticated JWT token in the authorization header of all requests. You can do that by:
The JS SDK provides an `auth.login` method that handles all authentication steps based on the configured authentication method. Then, all subsequent requests will have the necessary authentication headers or cookies.
- Retrieving a JWT token from the `/auth/customer/emailpass` API route.
- Passing the token in the authorization header of all subsequent requests, as explained in the [API reference](!api!/store#1-bearer-authorization-with-jwt-tokens).
The JS SDK simplifies passing the JWT token by passing it in the authorization header of all subsequent requests if you configure the JS SDK to use JWT authentication.
After [configuring the JS SDK](#js-sdk-authentication-configuration) to use JWT authentication, you can now implement the following login flow:
For example, to implement the login flow in your storefront with the JS SDK:
<CodeTabs group="store-request">
<CodeTab label="React" value="react">
@@ -146,11 +102,10 @@ export const highlights = [
["24", "login", "Send a request to obtain a JWT token."],
["28", "catch", "If an error occurs, show an alert and exit execution."],
["33", "", "If the token is not a string, show an alert and exit execution."],
["40", "setToken", "Set the token in the JS SDK to pass it in the header of subsequent requests."],
["43", "retrieve", "Retrieve the customer's details as an example of testing authentication."],
["39", "retrieve", "Retrieve the customer's details as an example of testing authentication."],
]
```tsx highlights={highlights} collapsibleLines="55-79" expandButtonLabel="Show form"
```tsx highlights={highlights} collapsibleLines="45-69" expandButtonLabel="Show form"
"use client" // include with Next.js 13+
import { useState } from "react"
@@ -188,11 +143,7 @@ export const highlights = [
return
}
// use token in the authorization header of
// all follow up requests.
sdk.client.setToken(token)
// the next request will be authenticated
// all next requests will be authenticated
const { customer } = await sdk.store.customer.retrieve()
console.log(customer)
@@ -233,8 +184,7 @@ export const fetchHighlights = [
["5", "login", "Send a request to obtain a JWT token."],
["9", "catch", "If an error occurs, show an alert and exit execution."],
["14", "", "If the token is not a string, show an alert and exit execution."],
["21", "setToken", "Set the token in the JS SDK to pass it in the header of subsequent requests."],
["24", "retrieve", "Retrieve the customer's details as an example of testing authentication."],
["20", "retrieve", "Retrieve the customer's details as an example of testing authentication."],
]
```ts highlights={fetchHighlights}
@@ -256,11 +206,7 @@ export const fetchHighlights = [
return
}
// use token in the authorization header of
// all follow up requests.
sdk.client.setToken(token)
// the next request will be authenticated
// all next requests will be authenticated
const { customer } = await sdk.store.customer.retrieve()
console.log(customer)
@@ -273,164 +219,82 @@ export const fetchHighlights = [
In the example above, you:
1. Create a `handleLogin` function that logs in a customer.
2. In the function, you obtain a JWT token by sending a request to the `/auth/customer/emailpass`.
2. In the function, you log in the customer using the `sdk.auth.login` method.
- If an error occurs, show an alert and exit execution.
- The request may return an object with a `location` property. This occurs when using third-party authentication providers. Learn more about implementing third-party authentication in the [Third-Party Login](../third-party-login/page.mdx) guide.
3. To use the token in the authorization header of subsequent requests, you must set the token in the JS SDK using the `client.setToken` method.
4. All subsequent requests are now authenticated. As an example, you send a request to obtain the logged-in customer's details.
- The method may return an object with a `location` property. This occurs when using third-party authentication providers. Learn more about implementing third-party authentication in the [Third-Party Login](../third-party-login/page.mdx) guide.
- Otherwise, the authentication was successful.
3. All subsequent requests are now authenticated. As an example, you send a request to obtain the logged-in customer's details.
---
## 2. Using a Cookie Session
## Authentication without JS SDK
If you're not using the JS SDK, the next sections cover the general flow for authenticating a customer in your storefront for both methods.
## 1. Using a JWT Token
The first authentication approach is to pass an authenticated JWT token in the authorization header of all requests. You can do that by:
1. Retrieving a JWT token from the `/auth/customer/emailpass` [Authenticate Customer API route](!api!/store#auth_postactor_typeauth_provider):
```bash
curl -X POST '{backend_url}/auth/customer/emailpass' \
-H 'Content-Type: application/json' \
--data-raw '{
"email": "customer@gmail.com",
"password": "supersecret"
}'
```
2. Passing the token in the authorization header of all subsequent requests, as explained in the [API reference](!api!/store#1-bearer-authorization-with-jwt-tokens):
```bash
Authorization: Bearer {jwt_token}
```
You can store the obtained JWT token based on your use case. For example, you can store it in the browser's `localStorage` or `sessionStorage`. This way, you can retrieve it later and pass it in the authorization header of all requests.
### 2. Using a Cookie Session
The second authentication approach is to authenticate the customer with a cookie session. You do that by:
- Retrieving a JWT token from the `/auth/customer/emailpass` API route.
- Sending a request to the `/auth/session` API route passing in the authorization header the token as a Bearer token. This sets the authenticated session ID in the cookies.
1. Retrieving a JWT token from the `/auth/customer/emailpass` [Authenticate Customer API route](!api!/store#auth_postactor_typeauth_provider):
Then, you must ensure that all subsequent requests include the session ID cookie.
```bash
curl -X POST '{backend_url}/auth/customer/emailpass' \
-H 'Content-Type: application/json' \
--data-raw '{
"email": "customer@gmail.com",
"password": "supersecret"
}'
```
The JS SDK simplifies this by passing the session ID cookie in all requests if you configure the JS SDK to use sessions.
2. Sending a request to the `/auth/session` [Authentication Session API route](!api!/store#auth_postsession) passing in the authorization header the token as a Bearer token. This sets the authenticated session ID in the cookies:
After [configuring the JS SDK](#js-sdk-authentication-configuration) to use sessions, you can now implement the following login flow:
```bash
curl -X POST '{backend_url}/auth/session' \
-H 'Authorization: Bearer {jwt_token}'
```
<CodeTabs group="store-request">
<CodeTab label="React" value="react">
3. Passing the cookie session ID in all subsequent requests:
export const sessionHighlights = [
["24", "login", "Send a request to obtain a JWT token."],
["28", "catch", "If an error occurs, show an alert and exit execution."],
["33", "", "If the token is not a string, show an alert and exit execution."],
["39", "setToken", "Set the token in the JS SDK, which will retrieve and pass the session ID cookie in all subsequent requests."],
["43", "retrieve", "Retrieve the customer's details as an example of testing authentication."],
]
```tsx highlights={sessionHighlights} collapsibleLines="68-92" expandButtonLabel="Show form"
"use client" // include with Next.js 13+
import { useState } from "react"
import { sdk } from "@/lib/sdk"
export default function Login() {
const [loading, setLoading] = useState(false)
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const handleLogin = async (
e: React.MouseEvent<HTMLButtonElement, MouseEvent>
) => {
e.preventDefault()
if (!email || !password) {
return
}
setLoading(true)
let token: string | { location: string }
try {
token = await sdk.auth.login("customer", "emailpass", {
email,
password,
})
} catch (error) {
alert(`An error occured while logging in: ${error}`)
return
}
if (typeof token !== "string") {
alert("Authentication requires more actions, which isn't supported by this flow.")
return
}
// set session
sdk.client.setToken(token)
// customer is now authenticated using the
// cookie session. For example
const { customer } = await sdk.store.customer.retrieve()
console.log(customer)
setLoading(false)
}
return (
<form>
<input
type="email"
name="email"
value={email}
placeholder="Email"
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="password"
name="password"
value={password}
placeholder="Password"
onChange={(e) => setPassword(e.target.value)}
/>
<button
disabled={loading}
onClick={handleLogin}
>
Login
</button>
</form>
)
}
```
<CodeTabs group="request-type">
<CodeTab label="cURL" value="curl">
```bash
curl '{backend_url}/store/products' \
-H 'Cookie: connect.sid={sid}'
```
</CodeTab>
<CodeTab label="JS SDK" value="js-sdk">
export const fetchSessionHighlights = [
["5", "login", "Send a request to obtain a JWT token."],
["9", "catch", "If an error occurs, show an alert and exit execution."],
["14", "", "If the token is not a string, show an alert and exit execution."],
["20", "setToken", "Set the token in the JS SDK, which will retrieve and pass the session ID cookie in all subsequent requests."],
["24", "retrieve", "Retrieve the customer's details as an example of testing authentication."],
]
```ts highlights={fetchSessionHighlights}
const handleLogin = async () => {
let token: string | { location: string }
try {
token = await sdk.auth.login("customer", "emailpass", {
email,
password,
})
} catch (error) {
alert(`An error occured while logging in: ${error}`)
return
}
if (typeof token !== "string") {
alert("Authentication requires more actions, which isn't supported by this flow.")
return
}
// set session
sdk.client.setToken(token)
// customer is now authenticated using the
// cookie session. For example
const { customer } = await sdk.store.customer.retrieve()
console.log(customer)
}
```
<CodeTab label="Fetch" value="fetch">
```ts highlights={["2", "", "Passes the cookie session ID in your request."]}
fetch(`<BACKEND_URL>/store/products`, {
credentials: "include",
})
```
</CodeTab>
</CodeTabs>
In the example above, you:
1. Create a `handleLogin` function that logs in a customer.
2. In the function, you obtain a JWT token by sending a request to the `/auth/customer/emailpass` API route.
- If an error occurs, show an alert and exit execution.
- The request may return an object with a `location` property. This occurs when using third-party authentication providers. Learn more about implementing third-party authentication in the [Third-Party Login](../third-party-login/page.mdx) guide.
3. To obtain the session ID cookie and ensure it's included in all subsequent requests, you must use the JS SDK's `client.setToken` method, passing it the JWT token.
- Because you set the JS SDK's authentication type to `session`, the `client.setToken` method will send a request to the `/auth/session` API route passing in the authorization header the token as a Bearer token. This sets the authenticated session ID in the cookies, which are then included in all subsequent requests.
4. All subsequent requests are now authenticated. As an example, you send a request to obtain the logged-in customer's details.
@@ -24,19 +24,15 @@ To register a customer, you implement the following steps:
![A diagram illustrating the flow of the register customer flow](https://res.cloudinary.com/dza7lstvk/image/upload/v1743086184/Medusa%20Resources/register-flow_yv5uw2.jpg)
1. Show the customer a form to enter their details.
2. Send a `POST` request to the `/auth/customer/emailpass/register` API route to obtain a registration JWT token.
2. Send a `POST` request to the `/auth/customer/emailpass/register` [Get Registration Token](!api!/store#auth_postactor_typeauth_provider_register) API route to obtain a registration JWT token.
3. Send a request to the [Create Customer API route](!api!/store#customers_postcustomers) passing the registration JWT token in the header.
However, a customer may enter an email that's already used either by an admin user, another customer, or a [custom actor type](../../../commerce-modules/auth/auth-identity-and-actor-types/page.mdx). To handle this scenario:
- Try to obtain a login token by sending a `POST` request to the `/auth/customer/emailpass` API route. The customer is only allowed to register if their email and password match the existing identity. This allows admin users to log in or register as customers.
- Try to obtain a login token by sending a `POST` request to the `/auth/customer/emailpass` [Authenticate Customer](!api!/store#auth_postactor_typeauth_provider) API route. The customer is only allowed to register if their email and password match the existing identity. This allows admin users to log in or register as customers.
- If you obtained the login token successfully, create the customer using the login JWT token instead of the registration token. This will not remove the existing identity. So, for example, an admin user can also become a customer.
---
## How to Implement the Register Customer Flow
An example implemetation of the registration flow in a storefront:
When you're using the JS SDK, this flow is simplified with quick registration and login methods. The rest of this guide uses the JS SDK to demonstrate the registration flow. However, if you're not using the JS SDK, you can still implement the same flow using the API routes.
<Note title="Tip">
@@ -44,24 +40,27 @@ Learn how to install and configure the JS SDK in the [JS SDK documentation](../.
</Note>
---
## How to Implement the Register Customer Flow
An example implemetation of the registration flow in a storefront:
<CodeTabs group="store-request">
<CodeTab label="React" value="react">
export const highlights = [
["26", "register", "Send a request to obtain a registration JWT token."],
["30", "catch", "Maybe another identity exists with the same email."],
["33", "", "If an unexpected error occurs, exit the flow."],
["40", "login", "Try to obtain a login JWT token."],
["43", "catch", "The existing account belongs to another customer, so authentication failed."],
["56", "registrationToken", "Set the token to the login JWT token"],
["59", "setToken", "Set the token in the JS SDK to pass it in the header of subsequent requests."],
["63", "create", "Send a request to create the customer."],
["72", "clearToken", "Clear the token in the JS SDK so that it's not used in subsequent requests."],
["75", "TODO", "Redirect the customer to the log in page."],
["76", "catch", "Handle registration failure"],
["24", "register", "Send a request to set the registration token in the JS SDK."],
["28", "catch", "Maybe another identity exists with the same email."],
["31", "", "If an unexpected error occurs, exit the flow."],
["38", "login", "Try to obtain a login JWT token."],
["41", "catch", "The existing account belongs to another customer, so authentication failed."],
["57", "create", "Send a request to create the customer."],
["66", "TODO", "Redirect the customer to the log in page."],
["67", "catch", "Handle registration failure"],
]
```tsx highlights={highlights} collapsibleLines="83-121" expandButtonLabel="Show form"
```tsx highlights={highlights} collapsibleLines="74-112" expandButtonLabel="Show form"
"use client" // include with Next.js 13+
import { useState } from "react"
@@ -84,10 +83,8 @@ export const highlights = [
}
setLoading(true)
let registrationToken = ""
try {
registrationToken = await sdk.auth.register("customer", "emailpass", {
await sdk.auth.register("customer", "emailpass", {
email,
password,
})
@@ -116,12 +113,8 @@ export const highlights = [
alert("Authentication requires more actions, which isn't supported by this flow.")
return
}
registrationToken = loginResponse
}
sdk.client.setToken(registrationToken)
// create customer
try {
const { customer } = await sdk.store.customer.create({
@@ -132,9 +125,6 @@ export const highlights = [
setLoading(false)
// clear the token
sdk.client.clearToken()
console.log(customer)
// TODO redirect to login page
} catch (error) {
@@ -189,16 +179,13 @@ export const highlights = [
<CodeTab label="JS SDK" value="js-sdk">
export const fetchHighlights = [
["10", "register", "Send a request to obtain a registration JWT token."],
["14", "catch", "Maybe another identity exists with the same email."],
["24", "login", "Try to obtain a login JWT token."],
["27", "catch", "The existing account belongs to another customer, so authentication failed."],
["40", "registrationToken", "Set the token to the login JWT token"],
["43", "setToken", "Set the token in the JS SDK to pass it in the header of subsequent requests."],
["47", "create", "Send a request to create the customer."],
["54", "clearToken", "Clear the token in the JS SDK so that it's not used in subsequent requests."],
["57", "TODO", "Redirect the customer to the log in page."],
["58", "catch", "Handle registration failure"],
["7", "register", "Send a request to obtain a registration JWT token."],
["11", "catch", "Maybe another identity exists with the same email."],
["21", "login", "Try to obtain a login JWT token."],
["24", "catch", "The existing account belongs to another customer, so authentication failed."],
["40", "create", "Send a request to create the customer."],
["47", "TODO", "Redirect the customer to the log in page."],
["48", "catch", "Handle registration failure"],
]
```ts highlights={fetchHighlights}
@@ -206,12 +193,9 @@ export const fetchHighlights = [
import { FetchError } from "@medusajs/js-sdk"
const handleRegistration = async () => {
let registrationToken = ""
// obtain registration JWT token
try {
registrationToken = await sdk.auth.register("customer", "emailpass", {
await sdk.auth.register("customer", "emailpass", {
email,
password,
})
@@ -240,12 +224,8 @@ export const fetchHighlights = [
alert("Authentication requires more actions, which isn't supported by this flow.")
return
}
registrationToken = loginResponse
}
sdk.client.setToken(registrationToken)
// create customer
try {
const { customer } = await sdk.store.customer.create({
@@ -254,9 +234,6 @@ export const fetchHighlights = [
email,
})
// clear the token
sdk.client.clearToken()
console.log(customer)
// TODO redirect to login page
} catch (error) {
@@ -272,15 +249,12 @@ export const fetchHighlights = [
In the above example, you create a `handleRegistration` function that:
- Obtains a registration JWT token from the `/auth/customer/emailpass/register` API route.
- If an error is thrown:
- If the error is an existing identity error, try retrieving the login JWT token from `/auth/customer/emailpass` API route. This will fail if the existing identity has a different password, which doesn't allow the customer from registering.
- Obtains a registration JWT token from the `/auth/customer/emailpass/register` API route using the `auth.register` method. If an error is thrown:
- If the error is an existing identity error, try retrieving the login JWT token from `/auth/customer/emailpass` API route using the `auth.login` method. This will fail if the existing identity has a different password, which doesn't allow the customer from registering.
- For other errors, show an alert and exit execution.
- In the JS SDK, set the registration or login token using the `client.setToken` method. Then, all subsequent requests will use that token in the request header.
- If you're not using the JS SDK, you must pass manually pass the registration or login JWT token as a Bearer token in the authorization header of the next request.
- The JS SDK automatically stores an re-uses the authentication headers or session in the `auth.register` and `auth.login` methods. So, if you're not using the JS SDK, make sure to pass the received authentication tokens as explained in the [API reference](!api!/store#1-bearer-authorization-with-jwt-tokens)
- Send a request to the [Create Customer API route](!api!/store#customers_postcustomers) to create the customer in Medusa.
- If an error occurs, show an alert and exit execution.
- Once the customer is registered successfully, you can either redirect the customer to the login page or log them in automatically.
- Make sure to clear the token in the JS SDK using the `client.clearToken` method so that it's not used in subsequent requests.
- As mentioned, the JS SDK automatically sends the authentication headers or session in all requests after registration or logging in. If you're not using the JS SDK, make sure to pass the received authentication tokens as explained in the [API reference](!api!/store#1-bearer-authorization-with-jwt-tokens).
- Once the customer is registered successfully, you can either redirect the customer to the login page or log them in automatically, as explained in the [Login](../login/page.mdx) guide.
Refer to the [Login guide](../login/page.mdx) to learn how to log in the customer manually or automatically.
@@ -21,18 +21,18 @@ Assuming you already set up the [Auth Module Provider](../../../commerce-modules
![Diagram illustrating the authentication flow between the storefront, Medusa application, and the third-party service.](https://res.cloudinary.com/dza7lstvk/image/upload/v1725531068/Medusa%20Resources/Social_Media_Graphics_third-party-auth-customer_kfn3k3.jpg)
1. Authenticate the customer with the [Authenticate Customer API route](!api!/store#auth_postactor_typeauth_provider).
2. The auth route returns a URL to authenticate with third-party service, such as login with Google. The storefront, when it receives a `location` property in the response, must redirect to the returned location.
3. Once the authentication with the third-party service finishes, it redirects back to the storefront with query parameters such as `code` and `state`. So, make sure your third-party service is configured to redirect to your storefront page after successful authentication.
4. The storefront sends a request to the [Validate Authentication Callback API route](!api!/store#auth_postactor_typeauth_providercallback) passing the query parameters received from the third-party service.
5. If the callback validation is successful, the storefront receives the authentication token.
6. Decode the received token in the frontend using tools like [react-jwt](https://www.npmjs.com/package/react-jwt).
- If the decoded data has an `actor_id` property, then the user is already registered. So, use this token for subsequent authenticated requests.
1. Authenticate the customer with the [Authenticate Customer API route](!api!/store#auth_postactor_typeauth_provider). It may return:
- A URL in a `location` property to authenticate with third-party service, such as login with Google. When you receive this property, you must redirect to the returned location.
- A token in a `token` property. In that case, the customer was previously logged in with the third-party service, such as Google, and no additional actions are required. You can use the token to send subsequent authenticated requests.
2. Once the authentication with the third-party service finishes, it must redirect back to the storefront with query parameters such as `code` and `state`. So, make sure your third-party service is configured to redirect to your storefront's callback page after successful authentication.
3. In the storefront's callback page, send a request to the [Validate Authentication Callback API route](!api!/store#auth_postactor_typeauth_providercallback) passing the query parameters (`code`, `state`, etc...) received from the third-party service.
4. If the callback validation is successful, you'll receive the authentication token. Decode the received token in the storefront using tools like [react-jwt](https://www.npmjs.com/package/react-jwt).
- If the decoded data has an `actor_id` property, then the customer is already registered. So, use this token for subsequent authenticated requests.
- If not, follow the rest of the steps.
7. The storefront uses the authentication token to create the customer using the [Create Customer API route](!api!/store#customers_postcustomers).
8. The storefront sends a request to the [Refresh Token Route](#add-the-function-to-refresh-the-token) to retrieve a new token for the customer.
5. The customer is not registered yet, so use the received token from the Validate Authentication Callback API route to create the customer using the [Create Customer API route](!api!/store#customers_postcustomers).
6. Send a request to the [Refresh Token Route](#add-the-function-to-refresh-the-token) to retrieve a new token for the customer, passing the token from the Validate Authentication Callback API in the header. You can then use the token returned by the Refresh Token request to send subsequent authenticated requests.
You'll implement the flow in this guide using Google as an example.
You'll implement the flow in this guide using Google as an example. The example snippets use the JS SDK, but you can follow a similar approach without it, as well.
<Prerequisites
items={[
@@ -70,8 +70,7 @@ export const reactHighlights = [
["7", "login", "Send a request to the Authenticate Customer API route"],
["9", "result.location", "If the request returns a location, redirect to that location to continue the authentication."],
["16", "", "If the token isn't returned, the authentication has failed."],
["24", "setToken", "Set the token in the client to be used in subsequent requests."],
["27", "retrieve", "Retrieve the customer's details as an example of testing authentication."]
["23", "retrieve", "Retrieve the customer's details as an example of testing authentication."]
]
```tsx highlights={reactHighlights}
@@ -96,11 +95,7 @@ export default function Login() {
return
}
// authentication successful
// set the token in the client to be used in subsequent requests
sdk.client.setToken(result)
// retrieve the customer using the token
// all subsequent requests are authenticated
const { customer } = await sdk.store.customer.retrieve()
console.log(customer)
@@ -121,8 +116,7 @@ export const jsSdkHighlights = [
["2", "login", "Send a request to the Authenticate Customer API route"],
["4", "", "If the request returns a location, redirect to that location to continue the authentication."],
["11", "", "If the token isn't returned, the authentication has failed."],
["19", "setToken", "Set the token in the client to be used in subsequent requests."],
["22", "retrieve", "Retrieve the customer's details as an example of testing authentication."]
["18", "retrieve", "Retrieve the customer's details as an example of testing authentication."]
]
```ts highlights={jsSdkHighlights}
@@ -142,11 +136,7 @@ const loginWithGoogle = async () => {
return
}
// authentication successful
// set the token in the client to be used in subsequent requests
sdk.client.setToken(result)
// retrieve the customer using the token
// all subsequent requests are authenticated
const { customer } = await sdk.store.customer.retrieve()
console.log(customer)
@@ -158,11 +148,11 @@ const loginWithGoogle = async () => {
You define a `loginWithGoogle` function that:
- Sends a request to the `/auth/customer/google` API route.
- Sends a request to the `/auth/customer/google` API route using the JS SDK's `auth.login` method.
- If the response is an object with a `location` property, then you redirect to the returned page for authentication with the third-party service.
- If the response is a string, then the customer has been authenticated before. You can use the token for subsequent authenticated request.
- To use the token for subsequent authenticated request, you must set it in the JS SDK using the `client.setToken` method.
- Now, subsequent requests are authenticated. As an example, you can retrieve the customer's details using the `store.customer.retrieve` method.
- If the response is a string, then the customer has been authenticated before and the method returns the customer's authentication token.
- Now, all subsequent requests are authenticated. As an example, you can retrieve the customer's details using the `store.customer.retrieve` method.
- Notice that the JS SDK sets and passes the authentication headers or session cookies (based on your [configured authentication method](../../../js-sdk/auth/overview/page.mdx)) automatically. If you're not using the JS SDK, make sure to pass the necessary headers in your request as explained in the [API reference](!api!/store#authentication).
<Note title="Tip">
@@ -205,7 +195,6 @@ export const sendCallbackReactHighlights = [
["12", "queryParams", "The query parameters received from Google, such as `code` and `state`."],
["21", "callback", "Send a request to the Validate Authentication Callback API route"],
["28", "catch", "If an error occurs, show an alert and exit execution."],
["36", "setToken", "Set the token in the client to be used in subsequent requests."]
]
```tsx highlights={sendCallbackReactHighlights}
@@ -242,10 +231,6 @@ export default function GoogleCallback() {
throw error
}
// set the token in the client
// to be used in subsequent requests
sdk.client.setToken(token)
return token
}
@@ -266,8 +251,7 @@ export default function GoogleCallback() {
export const sendCallbackFetchHighlights = [
["6", "queryParams", "The query parameters received from Google, such as `code` and `state`."],
["12", "callback", "Send a request to the Validate Authentication Callback API route"],
["19", "catch", "If an error occurs, show an alert and exit execution."],
["27", "setToken", "Set the token in the client to be used in subsequent requests."]
["19", "catch", "If an error occurs, show an alert and exit execution."]
]
```ts highlights={sendCallbackFetchHighlights}
@@ -295,10 +279,6 @@ const sendCallback = async () => {
throw error
}
// set the token in the client
// to be used in subsequent requests
sdk.client.setToken(token)
return token
}
@@ -310,7 +290,7 @@ const sendCallback = async () => {
This adds in the new page the function `sendCallback` which sends a request to the [Validate Callback API route](!api!/store#auth_postactor_typeauth_providercallback), passing it all query parameters received from Google. Those include the `code` and `state` parameters.
After that, you set the token in the JS SDK using the `client.setToken` method. This ensures that the token is passed to subsequent requests, such as the request to create the customer.
Notice that the JS SDK stores the JWT token returned by the Validate Callback API route automatically and attaches it to subsequent requests. If you're building this authentication flow without the JS SDK, you need to pass it manually to the next requests.
### Add the Function to Create a Customer
@@ -333,7 +313,7 @@ const createCustomer = async () => {
This adds to the page the function `createCustomer` which creates a customer if this is the first time the customer is authenticating with the third-party service.
Notice that this method assumes that the token received from the [Validate Callback API route](!api!/store#auth_postactor_typeauth_providercallback) is already set in the JS SDK, as done at the end of the `sendCallback` function. So, if you're implemeting this flow without using the JS SDK, make sure to pass the token in the authorization Bearer header.
Notice that this method assumes that the token received from the [Validate Callback API route](!api!/store#auth_postactor_typeauth_providercallback) is already set in the JS SDK. So, if you're implemeting this flow without using the JS SDK, make sure to pass the token received from the [Validate Callback API route](!api!/store#auth_postactor_typeauth_providercallback) in the authorization Bearer header.
### Add the Function to Refresh the Token
@@ -347,9 +327,6 @@ export const refreshTokenHighlights = [
const refreshToken = async () => {
// refresh the token
const result = await sdk.auth.refresh()
// set the new token
sdk.client.setToken(result)
}
// TODO add more functions...
@@ -357,9 +334,9 @@ const refreshToken = async () => {
This adds to the page the function `refreshToken` which is used after the new customer is created to refresh their authentication token. This ensures that the authentication token includes the details of the created customer.
Notice that this method assumes that the token received from the [Validate Callback API route](!api!/store#auth_postactor_typeauth_providercallback) is already set in the JS SDK, as done at the end of the `sendCallback` function. So, if you're implemeting this flow without using the JS SDK, make sure to pass the token in the authorization Bearer header.
Notice that this method assumes that the token received from the [Validate Callback API route](!api!/store#auth_postactor_typeauth_providercallback) is already set in the JS SDK. So, if you're implemeting this flow without using the JS SDK, make sure to pass the token in the authorization Bearer header.
Then, this method also sets the new token in the JS SDK to be used in subsequent authenticated requests.
The `refreshToken` method also updates the token stored by the JS SDK, ensuring that next requests use that token. So, if you're not using the JS SDK, make sure to pass the new token in the request header as explained in the [API reference](!api!/store#1-bearer-authorization-with-jwt-tokens).
### Add the Function to Validate the Callback
@@ -456,77 +433,6 @@ The customer is now authenticated, and you can redirect them to the home page or
### Full Code Example for Third-Party Login Callback Page
<CodeTabs group="authenticated-request">
<CodeTab label="JS SDK" value="js-sdk">
```ts
import { decodeToken } from "react-jwt"
// ...
const queryParams = new URLSearchParams(window.location.search)
const code = queryParams.get("code")
const state = queryParams.get("state")
const sendCallback = async () => {
let token = ""
try {
token = await sdk.auth.callback(
"customer",
"google",
// pass all query parameters received from the
// third party provider
queryParams
)
} catch (error) {
alert("Authentication Failed")
throw error
}
// set the token in the client
// to be used in subsequent requests
sdk.client.setToken(token)
return token
}
const createCustomer = async () => {
// create customer
await sdk.store.customer.create({
email: "example@medusajs.com",
})
}
const refreshToken = async () => {
// refresh the token
const result = await sdk.auth.refresh()
// set the new token
sdk.client.setToken(result)
}
const validateCallback = async () => {
const token = await sendCallback()
const shouldCreateCustomer = (decodeToken(token) as { actor_id: string }).actor_id === ""
if (shouldCreateCustomer) {
await createCustomer()
await refreshToken()
}
// use token to send authenticated requests
const { customer: customerData } = await sdk.store.customer.retrieve()
setCustomer(customerData)
setLoading(false)
}
```
</CodeTab>
<CodeTab label="React" value="react">
```tsx
@@ -563,10 +469,6 @@ export default function GoogleCallback() {
throw error
}
// set the token in the client
// to be used in subsequent requests
sdk.client.setToken(token)
return token
}
@@ -580,9 +482,6 @@ export default function GoogleCallback() {
const refreshToken = async () => {
// refresh the token
const result = await sdk.auth.refresh()
// set the new token
sdk.client.setToken(result)
}
const validateCallback = async () => {
@@ -596,7 +495,7 @@ export default function GoogleCallback() {
await refreshToken()
}
// use token to send authenticated requests
// all subsequent requests are authenticated
const { customer: customerData } = await sdk.store.customer.retrieve()
setCustomer(customerData)
@@ -618,6 +517,70 @@ export default function GoogleCallback() {
</div>
)
}
```
</CodeTab>
<CodeTab label="JS SDK" value="js-sdk">
```ts
import { decodeToken } from "react-jwt"
// ...
const queryParams = new URLSearchParams(window.location.search)
const code = queryParams.get("code")
const state = queryParams.get("state")
const sendCallback = async () => {
let token = ""
try {
token = await sdk.auth.callback(
"customer",
"google",
// pass all query parameters received from the
// third party provider
queryParams
)
} catch (error) {
alert("Authentication Failed")
throw error
}
return token
}
const createCustomer = async () => {
// create customer
await sdk.store.customer.create({
email: "example@medusajs.com",
})
}
const refreshToken = async () => {
// refresh the token
const result = await sdk.auth.refresh()
}
const validateCallback = async () => {
const token = await sendCallback()
const shouldCreateCustomer = (decodeToken(token) as { actor_id: string }).actor_id === ""
if (shouldCreateCustomer) {
await createCustomer()
await refreshToken()
}
// all subsequent requests are authenticated
const { customer: customerData } = await sdk.store.customer.retrieve()
setCustomer(customerData)
setLoading(false)
}
```
</CodeTab>
+6 -5
View File
@@ -150,9 +150,9 @@ export const generatedEditDates = {
"app/storefront-development/customers/addresses/page.mdx": "2025-03-27T14:47:14.252Z",
"app/storefront-development/customers/context/page.mdx": "2025-03-27T14:47:14.248Z",
"app/storefront-development/customers/log-out/page.mdx": "2025-03-27T14:45:23.360Z",
"app/storefront-development/customers/login/page.mdx": "2025-03-27T14:46:51.419Z",
"app/storefront-development/customers/login/page.mdx": "2025-03-28T08:38:37.694Z",
"app/storefront-development/customers/profile/page.mdx": "2025-03-27T14:47:14.251Z",
"app/storefront-development/customers/register/page.mdx": "2025-03-27T14:46:51.415Z",
"app/storefront-development/customers/register/page.mdx": "2025-03-28T09:30:47.216Z",
"app/storefront-development/customers/retrieve/page.mdx": "2025-03-27T14:41:39.996Z",
"app/storefront-development/customers/page.mdx": "2024-06-13T12:21:54+03:00",
"app/storefront-development/products/categories/list/page.mdx": "2025-03-27T14:46:51.437Z",
@@ -838,7 +838,7 @@ export const generatedEditDates = {
"references/types/interfaces/types.BaseClaim/page.mdx": "2025-02-24T10:48:36.876Z",
"app/commerce-modules/auth/auth-providers/github/page.mdx": "2025-01-13T11:31:35.361Z",
"app/commerce-modules/auth/auth-providers/google/page.mdx": "2025-01-13T11:31:35.361Z",
"app/storefront-development/customers/third-party-login/page.mdx": "2025-03-27T14:46:51.417Z",
"app/storefront-development/customers/third-party-login/page.mdx": "2025-03-28T09:31:36.450Z",
"references/types/HttpTypes/types/types.HttpTypes.AdminWorkflowRunResponse/page.mdx": "2024-12-09T13:21:34.761Z",
"references/types/HttpTypes/types/types.HttpTypes.BatchResponse/page.mdx": "2024-12-09T13:21:33.549Z",
"references/types/WorkflowsSdkTypes/types/types.WorkflowsSdkTypes.Acknowledgement/page.mdx": "2024-12-09T13:21:35.873Z",
@@ -2175,7 +2175,7 @@ export const generatedEditDates = {
"app/commerce-modules/store/links-to-other-modules/page.mdx": "2025-03-17T06:52:04.187Z",
"app/examples/page.mdx": "2025-03-18T15:19:46.808Z",
"app/medusa-cli/commands/build/page.mdx": "2024-11-11T11:00:49.665Z",
"app/js-sdk/page.mdx": "2025-02-05T09:12:11.479Z",
"app/js-sdk/page.mdx": "2025-03-28T09:50:07.617Z",
"references/js_sdk/admin/Admin/properties/js_sdk.admin.Admin.apiKey/page.mdx": "2025-01-13T18:05:58.463Z",
"references/js_sdk/admin/Admin/properties/js_sdk.admin.Admin.campaign/page.mdx": "2024-12-26T11:37:18.121Z",
"references/js_sdk/admin/Admin/properties/js_sdk.admin.Admin.claim/page.mdx": "2025-01-13T18:05:58.348Z",
@@ -6073,5 +6073,6 @@ export const generatedEditDates = {
"app/troubleshooting/workflow-errors/when-then/page.mdx": "2025-03-21T08:35:45.145Z",
"app/how-to-tutorials/tutorials/abandoned-cart/page.mdx": "2025-03-27T17:39:52.123Z",
"app/integrations/guides/algolia/page.mdx": "2025-03-27T18:00:49.330Z",
"app/integrations/guides/magento/page.mdx": "2025-03-28T06:46:40.800Z"
"app/integrations/guides/magento/page.mdx": "2025-03-28T06:46:40.800Z",
"app/js-sdk/auth/overview/page.mdx": "2025-03-28T08:05:32.622Z"
}
@@ -847,6 +847,10 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/integrations/page.mdx",
"pathname": "/integrations"
},
{
"filePath": "/www/apps/resources/app/js-sdk/auth/overview/page.mdx",
"pathname": "/js-sdk/auth/overview"
},
{
"filePath": "/www/apps/resources/app/js-sdk/page.mdx",
"pathname": "/js-sdk"
@@ -173,6 +173,14 @@ const generatedgeneratedToolsSidebarSidebar = {
"title": "Overview",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/js-sdk/auth/overview",
"title": "Authentication",
"children": []
},
{
"type": "separator"
},
@@ -180,7 +188,7 @@ const generatedgeneratedToolsSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "category",
"title": "Auth",
"title": "auth Methods",
"autogenerate_path": "/references/js_sdk/auth/Auth/methods",
"children": [
{
@@ -252,7 +260,7 @@ const generatedgeneratedToolsSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "category",
"title": "Store",
"title": "store Methods",
"autogenerate_path": "/references/js_sdk/store/Store/properties",
"children": [
{
@@ -342,7 +350,7 @@ const generatedgeneratedToolsSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "category",
"title": "Admin",
"title": "admin Methods",
"autogenerate_path": "/references/js_sdk/admin/Admin/properties",
"children": [
{
+8 -3
View File
@@ -59,22 +59,27 @@ export const toolsSidebar = [
path: "/js-sdk",
title: "Overview",
},
{
type: "link",
path: "/js-sdk/auth/overview",
title: "Authentication",
},
{
type: "separator",
},
{
type: "category",
title: "Auth",
title: "auth Methods",
autogenerate_path: "/references/js_sdk/auth/Auth/methods",
},
{
type: "category",
title: "Store",
title: "store Methods",
autogenerate_path: "/references/js_sdk/store/Store/properties",
},
{
type: "category",
title: "Admin",
title: "admin Methods",
autogenerate_path: "/references/js_sdk/admin/Admin/properties",
},
],