docs: updates and improvements to JS SDK guides (#12026)
This commit is contained in:
@@ -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:
|
||||

|
||||
|
||||
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.
|
||||
|
||||
+88
-125
@@ -21,18 +21,18 @@ Assuming you already set up the [Auth Module Provider](../../../commerce-modules
|
||||
|
||||

|
||||
|
||||
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>
|
||||
|
||||
Reference in New Issue
Block a user