diff --git a/www/apps/resources/app/storefront-development/customers/third-party-login/page.mdx b/www/apps/resources/app/storefront-development/customers/third-party-login/page.mdx
index d2ff310bb9..4b920ba3c7 100644
--- a/www/apps/resources/app/storefront-development/customers/third-party-login/page.mdx
+++ b/www/apps/resources/app/storefront-development/customers/third-party-login/page.mdx
@@ -18,45 +18,38 @@ export const metadata = {
In this guide, you'll learn how to implement third-party or social login in your storefront. You'll implement the flow using Google as an example.
-## Third-Party Login Flow in Storefront
+## Summary
-Assuming you already set up the [Auth Module Provider](../../../commerce-modules/auth/auth-providers/page.mdx) in your Medusa application, you can login a customer with a third-party service, such as Google or GitHub, using the following flow:
+By following the steps in this guide, you'll learn how to:
-
+- Create a login page with a button that starts the third-party login process. This redirects customers to the third-party service for authentication.
+- Create a callback page that the third-party service redirects to after authentication. This page receives query parameters from the third-party service and uses them to validate the authentication in Medusa.
-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.
-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.
+These are the pages you need in your storefront to allow customers to log in or create an account using a third-party service.
-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.
+
-## JS SDK Authentication Configuration
+## Step 1: Login Button in Storefront
-Before implementing the third-party 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.
+In your storefront, you'll have a login page with different login options. One of those options will be a button that starts the third-party login process. For example, a "Login with Google" button.
-Learn more about the authentication methods and how to configure them in the [Login Customer](../login/page.mdx) guide.
-
----
-
-## Step 1: Authenticate Customer in Medusa
-
-When the customer clicks on a "Login with Google" button, send a request to the [Authenticate Customer API route](!api!/store#auth_postactor_typeauth_provider).
+When the customer clicks the "Login with Google" button, send a request to the [Authenticate Customer API route](!api!/store#auth_postactor_typeauth_provider). This returns the URL to redirect the customer to Google for authentication.
For example:
@@ -71,9 +64,9 @@ Learn how to install and configure the JS SDK in the [JS SDK documentation](../.
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."],
- ["23", "retrieve", "Retrieve the customer's details as an example of testing authentication."]
+ ["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", "retrieve", "Retrieve the customer's details as an example of testing authentication"]
]
```tsx highlights={reactHighlights}
@@ -98,6 +91,7 @@ export default function Login() {
return
}
+ // Customer was previously authenticated, and its token is now stored in the JS SDK.
// all subsequent requests are authenticated
const { customer } = await sdk.store.customer.retrieve()
@@ -117,9 +111,9 @@ export default function Login() {
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."],
- ["18", "retrieve", "Retrieve the customer's details as an example of testing authentication."]
+ ["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", "retrieve", "Retrieve the customer's details as an example of testing authentication"]
]
```ts highlights={jsSdkHighlights}
@@ -139,6 +133,7 @@ const loginWithGoogle = async () => {
return
}
+ // Customer was previously authenticated, and its token is now stored in the JS SDK.
// all subsequent requests are authenticated
const { customer } = await sdk.store.customer.retrieve()
@@ -152,14 +147,15 @@ const loginWithGoogle = async () => {
You define a `loginWithGoogle` function that:
- 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 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).
+ - If you're using a provider other than Google, replace `google` in the `login` method with your provider ID.
+- If the response is an object with a `location` property, redirect to the returned page for authentication with the third-party service.
+- If the response is not an object or a string, the authentication has failed.
+- If the response is a string, it's the customer's authentication token. This means the customer has been authenticated before.
+ - All subsequent requests by the JS SDK are now authenticated. As an example, you can retrieve the customer's details using the `store.customer.retrieve` method.
-If you're using a provider other than Google, or if you've configured the Google provider with an ID other than `google`, replace `google` in the parameter `login("customer", "google", {})` with your provider ID.
+The JS SDK sets and passes authentication headers or session cookies automatically based on your [configured authentication method](../../../js-sdk/auth/overview/page.mdx). 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).
@@ -167,37 +163,33 @@ If you're using a provider other than Google, or if you've configured the Google
## Step 2: Callback Page in Storefront
-In the previous step, you implemented as part of the login flow redirecting the customer to the third-party service for authentication.
+After the customer clicks the "Login with Google" button, they're redirected to Google to authenticate. Once they authenticate with Google, they're redirected back to your storefront to the callback page. You set this page's URL in Google's OAuth credentials configurations.
-Once the customer authenticates with the third-party service, the service redirects the customer back to your storefront with query parameters such as `code` and `state`.
+
-The next step is to create the page in your storefront that the customer is redirected to after they authenticate with Google. You'll use this page's URL as the Redirect Uri in your Google settings, and set it in the `callbackUrl` of your [Google Auth Module Provider](../../../commerce-modules/auth/auth-providers/google/page.mdx)'s configurations.
+In this step, you'll create the callback page that handles the response from Google and creates or retrieves the customer account. You'll implement the page step-by-step to explain the different parts of the flow. You can copy the full page code from the [Full Code Example for Third-Party Login Callback Page](#full-code-example-for-third-party-login-callback-page) section.
-
+### a. Install the React-JWT Library
-The callback page is implemented step-by-step to explain the different parts of the flow. You can copy the full page code in the [Full Code Example for Third-Party Login Callback Page](#full-code-example-for-third-party-login-callback-page) section, and then add the functions one by one to test the flow.
-
-
-
-### Install the React-JWT Library
-
-First, install the [react-jwt library](https://www.npmjs.com/package/react-jwt) in your storefront to use it for decoding the token received from Google:
+First, install the [react-jwt library](https://www.npmjs.com/package/react-jwt) in your storefront:
```bash npm2yarn
npm install react-jwt
```
-### Implement the Callback Page
+You'll use it to decode the token that Medusa returns after validating the authentication callback.
-Then, in a new page in your storefront that will be used as the callback / redirect uri destination, add the following:
+### b. Implement the Callback Page
+
+Then, create a new page in your storefront that will be used as the callback/redirect URI destination:
export const sendCallbackReactHighlights = [
- ["12", "queryParams", "The query parameters received from Google, such as `code` and `state`."],
+ ["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."],
+ ["28", "catch", "If an error occurs, show an alert and exit execution"]
]
```tsx highlights={sendCallbackReactHighlights}
@@ -252,9 +244,9 @@ export default function GoogleCallback() {
export const sendCallbackFetchHighlights = [
- ["6", "queryParams", "The query parameters received from Google, such as `code` and `state`."],
+ ["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."]
+ ["19", "catch", "If an error occurs, show an alert and exit execution"]
]
```ts highlights={sendCallbackFetchHighlights}
@@ -291,13 +283,19 @@ 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.
+You add a new page. In the page's component, you define the `sendCallback` function that sends a request to the [Validate Callback API route](!api!/store#auth_postactor_typeauth_providercallback), passing it all query parameters received from Google. These include the `code` and `state` parameters.
-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
+The JS SDK stores the JWT token returned by the Validate Callback API route automatically. It attaches this token to subsequent requests. If you're building this authentication flow without the JS SDK, you need to pass it manually to the next requests.
-Next, replace the `TODO` after the `sendCallback` function with the following:
+
+
+### c. Create Customer Function
+
+Next, you'll add to the page a function that creates a customer. You'll use this function if the customer is authenticating with the third-party service for the first time.
+
+Replace the `TODO` after the `sendCallback` function with the following:
export const createCustomerHighlights = [
["3", "create", "Create a customer"]
@@ -314,16 +312,22 @@ const createCustomer = async () => {
// TODO add more functions...
```
-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.
+You add the function `createCustomer` which creates a customer when 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. 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
+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 implementing 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](!api!/store#authentication).
-Next, replace the new `TODO` with the following:
+
+
+### d. Refresh Token Function
+
+Next, you'll add to the page a function that refreshes the authentication token after creating the customer. This is necessary to ensure that the token includes the created customer's details.
+
+Replace the new `TODO` with the following:
export const refreshTokenHighlights = [
- ["3", "refresh", "Fetch a new token for the created customer."]
+ ["3", "refresh", "Fetch a new token for the created customer"]
]
```ts highlights={refreshTokenHighlights} title="JS SDK / React Applicable"
@@ -335,25 +339,31 @@ const refreshToken = async () => {
// TODO add more functions...
```
-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.
+You add the function `refreshToken` that sends a request to the [Refresh Token API route](!api!/store#auth_postadminauthtokenrefresh) to retrieve a new token for 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. So, if you're implemeting this flow without using the JS SDK, make sure to pass the token in the authorization Bearer header.
+The `refreshToken` method also updates the token stored by the JS SDK, ensuring that subsequent requests use that token.
-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
+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 implementing this flow without using the JS SDK, make sure to pass the token in the [authorization Bearer header](!api!/store#authentication). Make sure to also update the token stored in your application after refreshing it.
-Finally, add in the place of the new `TODO` the `validateCallback` function that runs when the page first loads to validate the authentication:
+
+
+### e. Validate Callback Function
+
+Finally, you'll add to the page a function that validates the authentication callback in Medusa and creates or retrieves the customer account. It will use the functions added earlier.
+
+Add in the place of the new `TODO` the `validateCallback` function that runs when the page first loads to validate the authentication:
export const validateReactHighlights = [
["2", "sendCallback", "Validate the callback in Medusa and retrieve the authentication token"],
- ["4", "shouldCreateCustomer", "Check if the decoded token has an `actor_id` property to decide whether a customer to be created."],
- ["7", "createCustomer", "Create a customer if the decoded token doesn't have `actor_id`."],
- ["9", "refreshToken", "Fetch a new token for the created customer."],
- ["13", "retrieve", "Retrieve the customer's details as an example of testing authentication."]
+ ["4", "shouldCreateCustomer", "Check if the decoded token has an `actor_id` property to decide whether a customer needs to be created"],
+ ["7", "createCustomer", "Create a customer if the decoded token doesn't have `actor_id`"],
+ ["9", "refreshToken", "Fetch a new token for the created customer"],
+ ["13", "retrieve", "Retrieve the customer's details as an example of testing authentication"]
]
```tsx highlights={validateReactHighlights}
@@ -376,13 +386,7 @@ const validateCallback = async () => {
}
-useEffect(() => {
- if (!loading) {
- return
- }
-
- validateCallback()
-}, [loading])
+// TODO run validateCallback when the page loads
```
@@ -390,10 +394,10 @@ useEffect(() => {
export const validateFetchHighlights = [
["2", "sendCallback", "Validate the callback in Medusa and retrieve the authentication token"],
- ["4", "shouldCreateCustomer", "Check if the decoded token has an `actor_id` property to decide whether a customer to be created."],
- ["7", "createCustomer", "Create a customer if the decoded token doesn't have `actor_id`."],
- ["9", "refreshToken", "Fetch a new token for the created customer."],
- ["13", "retrieve", "Retrieve the customer's details as an example of testing authentication."]
+ ["4", "shouldCreateCustomer", "Check if the decoded token has an `actor_id` property to decide whether a customer needs to be created"],
+ ["7", "createCustomer", "Create a customer if the decoded token doesn't have `actor_id`"],
+ ["9", "refreshToken", "Fetch a new token for the created customer"],
+ ["13", "retrieve", "Retrieve the customer's details as an example of testing authentication"]
]
```ts highlights={validateFetchHighlights}
@@ -414,24 +418,55 @@ const validateCallback = async () => {
setCustomer(customerData)
setLoading(false)
}
+
+// TODO run validateCallback when the page loads
```
-The `validateCallback` function uses the functions added earlier to:
+The `validateCallback` function uses the functions added earlier to implement the following flow:
-1. Send a request to the [Validate Callback API route](!api!/store#auth_postactor_typeauth_providercallback), which returns an authentication token.
- - The `sendCallback` function also sets the token in the JS SDK to be passed in subsequent requests.
-2. Decodes the token to check if it has an `actor_id` property.
- - If so, then the customer is previously registered, and the authentication token can be used for subsequent authenticated requests.
- - If not, this is the first time the customer is authenticating with the third-party service, so:
- 1. Create a customer using the [Create Customer API route](!api!/store#customers_postcustomers).
- 2. Refetch the customer's authentication token after it's created using the [Refresh Token API route](!api!/store#auth_postactor_typeauth_providerrefresh).
+1. Send a request to the [Validate Callback API route](!api!/store#auth_postactor_typeauth_providercallback). This returns an authentication token.
+ - The `sendCallback` function also sets the token in the JS SDK. This passes the token in subsequent requests.
+2. Decode the token to check if it has an `actor_id` property.
+ - If the property exists, the customer is already registered. The authentication token can be used for subsequent authenticated requests.
+ - If the property doesn't exist, this is the first time the customer is authenticating with the third-party service. So:
+ 1. Create a customer.
+ 2. Refetch the customer's authentication token.
3. Use the token for subsequent authenticated requests.
3. Retrieve the customer's details as an example of testing authentication.
-The customer is now authenticated, and you can redirect them to the home page or the page they were trying to access before logging in.
+### f. Run the Callback Validation on Page Load
+
+Finally, you need to run the `validateCallback` function when the page first loads. In React, you can do that using the `useEffect` hook.
+
+For example, replace the last `TODO` with the following:
+
+```tsx
+useEffect(() => {
+ if (!loading) {
+ return
+ }
+
+ validateCallback()
+}, [loading])
+```
+
+This runs the `validateCallback` function when the page first loads. If the validation is successful, the customer is authenticated.
+
+You can show a success message or redirect the customer to another page. For example, use another `useEffect` hook to redirect the customer to the homepage after successful authentication:
+
+```tsx
+useEffect(() => {
+ if (!customer) {
+ return
+ }
+
+ // redirect to homepage after successful authentication
+ window.location.href = "/"
+}, [customer])
+```
### Full Code Example for Third-Party Login Callback Page
@@ -513,6 +548,14 @@ export default function GoogleCallback() {
validateCallback()
}, [loading])
+ useEffect(() => {
+ if (!customer) {
+ return
+ }
+
+ // TODO redirect to homepage after successful authentication
+ }, [customer])
+
return (
{loading && Loading...}
@@ -588,3 +631,25 @@ const validateCallback = async () => {
+
+---
+
+## Deep Dive: Third-Party Login Flow in Storefront
+
+In this section, you'll find a general overview of the third-party login flow in the storefront. This is useful if you're not using the JS SDK or want to understand the flow better.
+
+If you already set up the [Auth Module Provider](../../../commerce-modules/auth/auth-providers/page.mdx) in your Medusa application, you can log in a customer with a third-party service, such as Google or GitHub, using the following flow:
+
+
+
+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 a third-party service, such as Google. When you receive this property, redirect to the returned location.
+ - A token in a `token` property. In this case, the customer was previously logged in with the third-party service. No additional actions are required. You can use the token to send subsequent authenticated requests.
+2. Once authentication with the third-party service finishes, it redirects back to the storefront with query parameters such as `code` and `state`. 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). Pass 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, the customer is already registered. Use this token for subsequent authenticated requests.
+ - If not, follow the rest of the steps.
+5. The customer is not registered yet. 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](!api!/store#auth_postadminauthtokenrefresh) to retrieve a new token for the customer. Pass the token from the Validate Authentication Callback API in the header.
+7. Use the token for subsequent authenticated requests, such as retrieving the customer's details using the [Retrieve Customer API route](!api!/store#getactor_typecustomersme).
\ No newline at end of file
diff --git a/www/apps/resources/generated/edit-dates.mjs b/www/apps/resources/generated/edit-dates.mjs
index e411c39844..95d50b3a56 100644
--- a/www/apps/resources/generated/edit-dates.mjs
+++ b/www/apps/resources/generated/edit-dates.mjs
@@ -832,7 +832,7 @@ export const generatedEditDates = {
"references/types/interfaces/types.BaseClaim/page.mdx": "2025-09-12T14:10:39.219Z",
"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-28T09:31:36.450Z",
+ "app/storefront-development/customers/third-party-login/page.mdx": "2025-09-17T11:42:53.434Z",
"references/types/HttpTypes/types/types.HttpTypes.AdminWorkflowRunResponse/page.mdx": "2024-12-09T13:21:34.761Z",
"references/types/HttpTypes/types/types.HttpTypes.BatchResponse/page.mdx": "2025-04-11T09:04:46.523Z",
"references/types/WorkflowsSdkTypes/types/types.WorkflowsSdkTypes.Acknowledgement/page.mdx": "2024-12-09T13:21:35.873Z",