docs: fix authentication callback descriptions to include all query parameters (#10932)
This commit is contained in:
@@ -43374,9 +43374,7 @@ paths:
|
||||
operationId: PostActor_typeAuth_providerCallback
|
||||
summary: Validate Authentication Callback
|
||||
description: |
|
||||
This API route is used by your dashboard or frontend application when a third-party provider redirects to it after authentication.
|
||||
|
||||
It validates the authentication with the third-party provider and, if successful, returns an authentication token.
|
||||
This API route is used by your dashboard or frontend application when a third-party provider redirects to it after authentication. It validates the authentication with the third-party provider and, if successful, returns an authentication token. All query parameters received from the third-party provider, such as `code`, `state`, and `error`, must be passed as query parameters to this route.
|
||||
|
||||
You can decode the JWT token using libraries like [react-jwt](https://www.npmjs.com/package/react-jwt) in the frontend. If the decoded data doesn't have an `actor_id` property, then you must create a user, typically using the Accept Invite route passing the token in the request's Authorization header.
|
||||
externalDocs:
|
||||
|
||||
@@ -3,11 +3,11 @@ post:
|
||||
summary: Validate Authentication Callback
|
||||
description: >
|
||||
This API route is used by your dashboard or frontend application when a
|
||||
third-party provider redirects to it after authentication.
|
||||
|
||||
|
||||
It validates the authentication with the third-party provider and, if
|
||||
successful, returns an authentication token.
|
||||
third-party provider redirects to it after authentication. It validates the
|
||||
authentication with the third-party provider and, if successful, returns an
|
||||
authentication token. All query parameters received from the third-party
|
||||
provider, such as `code`, `state`, and `error`, must be passed as query
|
||||
parameters to this route.
|
||||
|
||||
|
||||
You can decode the JWT token using libraries like
|
||||
|
||||
@@ -219,10 +219,7 @@ paths:
|
||||
operationId: PostActor_typeAuth_providerCallback
|
||||
summary: Validate Authentication Callback
|
||||
description: |
|
||||
This API route is used by your storefront or frontend application when a third-party provider redirects to it after authentication.
|
||||
|
||||
It validates the authentication with the third-party provider and, if successful, returns an authentication token.
|
||||
|
||||
This API route is used by your storefront or frontend application when a third-party provider redirects to it after authentication. It validates the authentication with the third-party provider and, if successful, returns an authentication token. All query parameters received from the third-party provider, such as `code`, `state`, and `error`, must be passed as query parameters to this route.
|
||||
You can decode the JWT token using libraries like [react-jwt](https://www.npmjs.com/package/react-jwt) in the storefront. If the decoded data doesn't have an `actor_id` property, then you must register the customer using the Create Customer API route passing the token in the request's Authorization header.
|
||||
externalDocs:
|
||||
url: https://docs.medusajs.com/v2/storefront-development/customers/third-party-login
|
||||
|
||||
+5
-6
@@ -3,12 +3,11 @@ post:
|
||||
summary: Validate Authentication Callback
|
||||
description: >
|
||||
This API route is used by your storefront or frontend application when a
|
||||
third-party provider redirects to it after authentication.
|
||||
|
||||
|
||||
It validates the authentication with the third-party provider and, if
|
||||
successful, returns an authentication token.
|
||||
|
||||
third-party provider redirects to it after authentication. It validates the
|
||||
authentication with the third-party provider and, if successful, returns an
|
||||
authentication token. All query parameters received from the third-party
|
||||
provider, such as `code`, `state`, and `error`, must be passed as query
|
||||
parameters to this route.
|
||||
|
||||
You can decode the JWT token using libraries like
|
||||
[react-jwt](https://www.npmjs.com/package/react-jwt) in the storefront. If
|
||||
|
||||
@@ -144,7 +144,7 @@ const { success, authIdentity, location } = await authModuleService.authenticate
|
||||
// passed to auth provider
|
||||
{
|
||||
// ...
|
||||
callback_url: "example.com"
|
||||
callback_url: "example.com",
|
||||
}
|
||||
)
|
||||
```
|
||||
@@ -176,6 +176,12 @@ if (success) {
|
||||
}
|
||||
```
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
For providers like Google, the `query` object contains the query parameters from the original callback URL, such as the `code` and `state` parameters.
|
||||
|
||||
</Note>
|
||||
|
||||
If the returned `success` property is `true`, the authentication with the third-party provider was successful.
|
||||
|
||||

|
||||
|
||||
@@ -41,7 +41,7 @@ module.exports = defineConfig({
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
@@ -60,7 +60,7 @@ module.exports = defineConfig({
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
@@ -63,7 +63,7 @@ module.exports = defineConfig({
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
@@ -59,7 +59,7 @@ It requires the following steps:
|
||||
1. Authenticate the user with the [Auth Route](#login-route).
|
||||
2. The auth route returns a URL to authenticate with third-party service, such as login with Google. The frontend (such as a 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 frontend with a `code` query parameter. So, make sure your third-party service is configured to redirect to your frontend page after successful authentication.
|
||||
4. The frontend sends a request to the [Callback Route](#callback-route) passing the `code` query parameter.
|
||||
4. The frontend sends a request to the [Validate Callback Route](#validate-callback-route) passing it the query parameters received from the third-party service, such as the `code` and `state` query parameters.
|
||||
5. If the callback validation is successful, the frontend 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.
|
||||
@@ -221,7 +221,7 @@ Redirect to that URL in the frontend to continue the authentication process with
|
||||
The Medusa application defines an API route at `/auth/{actor_type}/{provider}/callback` that's useful for validating the authentication callback or redirect from third-party services like Google.
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:9000/auth/{actor_type}/{providers}/callback?code=123
|
||||
curl -X POST http://localhost:9000/auth/{actor_type}/{providers}/callback?code=123&state=456
|
||||
```
|
||||
|
||||
<Note title="Tip">
|
||||
@@ -239,7 +239,7 @@ Its path parameters are:
|
||||
|
||||
### Query Parameters
|
||||
|
||||
This route accepts a `code` query parameter, which is the code received from the third-party provider.
|
||||
This route accepts all the query parameters that the third-party service sends to the frontend after the user completes the authentication process, such as the `code` and `state` query parameters.
|
||||
|
||||
### Response Fields
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ export const multiPartsHighlights1 = [
|
||||
```ts highlights={multiPartsHighlights1}
|
||||
import {
|
||||
createInventoryItemsWorkflow,
|
||||
useQueryGraphStep
|
||||
useQueryGraphStep,
|
||||
} from "@medusajs/medusa/core-flows"
|
||||
import { createWorkflow } from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
@@ -66,8 +66,8 @@ export const createMultiPartProductsWorkflow = createWorkflow(
|
||||
entity: "stock_location",
|
||||
fields: ["*"],
|
||||
filters: {
|
||||
name: "European Warehouse"
|
||||
}
|
||||
name: "European Warehouse",
|
||||
},
|
||||
})
|
||||
|
||||
const inventoryItems = createInventoryItemsWorkflow.runAsStep({
|
||||
@@ -79,9 +79,9 @@ export const createMultiPartProductsWorkflow = createWorkflow(
|
||||
location_levels: [
|
||||
{
|
||||
stocked_quantity: 100,
|
||||
location_id: stockLocations[0].id
|
||||
}
|
||||
]
|
||||
location_id: stockLocations[0].id,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
sku: "WHEEL",
|
||||
@@ -89,9 +89,9 @@ export const createMultiPartProductsWorkflow = createWorkflow(
|
||||
location_levels: [
|
||||
{
|
||||
stocked_quantity: 100,
|
||||
location_id: stockLocations[0].id
|
||||
}
|
||||
]
|
||||
location_id: stockLocations[0].id,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
sku: "SEAT",
|
||||
@@ -99,12 +99,12 @@ export const createMultiPartProductsWorkflow = createWorkflow(
|
||||
location_levels: [
|
||||
{
|
||||
stocked_quantity: 100,
|
||||
location_id: stockLocations[0].id
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
location_id: stockLocations[0].id,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
// TODO create the product
|
||||
@@ -127,11 +127,11 @@ export const multiPartHighlights2 = [
|
||||
```ts highlights={multiPartHighlights2}
|
||||
import {
|
||||
// ...
|
||||
transform
|
||||
transform,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
import {
|
||||
// ...
|
||||
createProductsWorkflow
|
||||
createProductsWorkflow,
|
||||
} from "@medusajs/medusa/core-flows"
|
||||
|
||||
export const createMultiPartProductsWorkflow = createWorkflow(
|
||||
@@ -140,7 +140,7 @@ export const createMultiPartProductsWorkflow = createWorkflow(
|
||||
// ...
|
||||
|
||||
const inventoryItemIds = transform({
|
||||
inventoryItems
|
||||
inventoryItems,
|
||||
}, (data) => {
|
||||
return data.inventoryItems.map((inventoryItem) => {
|
||||
return {
|
||||
@@ -161,24 +161,24 @@ export const createMultiPartProductsWorkflow = createWorkflow(
|
||||
prices: [
|
||||
{
|
||||
amount: 100,
|
||||
currency_code: "usd"
|
||||
}
|
||||
currency_code: "usd",
|
||||
},
|
||||
],
|
||||
options: {
|
||||
"Default Option": "Default Variant"
|
||||
"Default Option": "Default Variant",
|
||||
},
|
||||
inventory_items: inventoryItemIds
|
||||
}
|
||||
inventory_items: inventoryItemIds,
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
title: "Default Option",
|
||||
values: ["Default Variant"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
values: ["Default Variant"],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -235,21 +235,21 @@ export const createBundledProducts = createWorkflow(
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "usd"
|
||||
}
|
||||
currency_code: "usd",
|
||||
},
|
||||
],
|
||||
options: {
|
||||
"Default Option": "Default Variant"
|
||||
"Default Option": "Default Variant",
|
||||
},
|
||||
manage_inventory: true
|
||||
}
|
||||
manage_inventory: true,
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
title: "Default Option",
|
||||
values: ["Default Variant"]
|
||||
}
|
||||
]
|
||||
values: ["Default Variant"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Pants",
|
||||
@@ -259,21 +259,21 @@ export const createBundledProducts = createWorkflow(
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "usd"
|
||||
}
|
||||
currency_code: "usd",
|
||||
},
|
||||
],
|
||||
options: {
|
||||
"Default Option": "Default Variant"
|
||||
"Default Option": "Default Variant",
|
||||
},
|
||||
manage_inventory: true
|
||||
}
|
||||
manage_inventory: true,
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
title: "Default Option",
|
||||
values: ["Default Variant"]
|
||||
}
|
||||
]
|
||||
values: ["Default Variant"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Shoes",
|
||||
@@ -283,24 +283,24 @@ export const createBundledProducts = createWorkflow(
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "usd"
|
||||
}
|
||||
currency_code: "usd",
|
||||
},
|
||||
],
|
||||
options: {
|
||||
"Default Option": "Default Variant"
|
||||
"Default Option": "Default Variant",
|
||||
},
|
||||
manage_inventory: true
|
||||
}
|
||||
manage_inventory: true,
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
title: "Default Option",
|
||||
values: ["Default Variant"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
values: ["Default Variant"],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
// TODO re-retrieve with inventory
|
||||
@@ -321,10 +321,10 @@ export const bundledHighlights2 = [
|
||||
```ts highlights={bundledHighlights2}
|
||||
import {
|
||||
// ...
|
||||
transform
|
||||
transform,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
import {
|
||||
useQueryGraphStep
|
||||
useQueryGraphStep,
|
||||
} from "@medusajs/medusa/core-flows"
|
||||
|
||||
export const createBundledProducts = createWorkflow(
|
||||
@@ -332,7 +332,7 @@ export const createBundledProducts = createWorkflow(
|
||||
() => {
|
||||
// ...
|
||||
const productIds = transform({
|
||||
products
|
||||
products,
|
||||
}, (data) => data.products.map((product) => product.id))
|
||||
|
||||
// @ts-ignore
|
||||
@@ -340,19 +340,19 @@ export const createBundledProducts = createWorkflow(
|
||||
entity: "product",
|
||||
fields: [
|
||||
"variants.*",
|
||||
"variants.inventory_items.*"
|
||||
"variants.inventory_items.*",
|
||||
],
|
||||
filters: {
|
||||
id: productIds
|
||||
}
|
||||
id: productIds,
|
||||
},
|
||||
})
|
||||
|
||||
const inventoryItemIds = transform({
|
||||
productsWithInventory
|
||||
productsWithInventory,
|
||||
}, (data) => {
|
||||
return data.productsWithInventory.map((product) => {
|
||||
return {
|
||||
inventory_item_id: product.variants[0].inventory_items?.[0]?.inventory_item_id
|
||||
inventory_item_id: product.variants[0].inventory_items?.[0]?.inventory_item_id,
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -387,24 +387,24 @@ export const createBundledProducts = createWorkflow(
|
||||
prices: [
|
||||
{
|
||||
amount: 30,
|
||||
currency_code: "usd"
|
||||
}
|
||||
currency_code: "usd",
|
||||
},
|
||||
],
|
||||
options: {
|
||||
"Default Option": "Default Variant"
|
||||
"Default Option": "Default Variant",
|
||||
},
|
||||
inventory_items: inventoryItemIds,
|
||||
}
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
title: "Default Option",
|
||||
values: ["Default Variant"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
values: ["Default Variant"],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}).config({ name: "create-bundled-product" })
|
||||
}
|
||||
)
|
||||
|
||||
@@ -36,15 +36,15 @@ For example:
|
||||
<CodeTab label="Using Workflow" value="workflow">
|
||||
|
||||
```ts
|
||||
import { createPaymentCollectionForCartWorkflow } from "@medusajs/medusa/core-flows";
|
||||
import { createPaymentCollectionForCartWorkflow } from "@medusajs/medusa/core-flows"
|
||||
|
||||
// ...
|
||||
|
||||
await createPaymentCollectionForCartWorkflow(req.scope)
|
||||
.run({
|
||||
input: {
|
||||
cart_id: "cart_123"
|
||||
}
|
||||
cart_id: "cart_123",
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -77,7 +77,7 @@ For example:
|
||||
<CodeTab label="Using Workflow" value="workflow">
|
||||
|
||||
```ts
|
||||
import { createPaymentSessionsWorkflow } from "@medusajs/medusa/core-flows";
|
||||
import { createPaymentSessionsWorkflow } from "@medusajs/medusa/core-flows"
|
||||
|
||||
// ...
|
||||
|
||||
@@ -86,7 +86,7 @@ const { result: paymentSesion } = await createPaymentSessionsWorkflow(req.scope)
|
||||
input: {
|
||||
payment_collection_id: "paycol_123",
|
||||
provider_id: "stripe",
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -124,13 +124,13 @@ For example:
|
||||
<CodeTab label="Using Step" value="workflow">
|
||||
|
||||
```ts
|
||||
import { authorizePaymentSessionStep } from "@medusajs/medusa/core-flows";
|
||||
import { authorizePaymentSessionStep } from "@medusajs/medusa/core-flows"
|
||||
|
||||
// ...
|
||||
|
||||
authorizePaymentSessionStep({
|
||||
id: "payses_123",
|
||||
context: {}
|
||||
context: {},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -140,7 +140,7 @@ authorizePaymentSessionStep({
|
||||
```ts
|
||||
const payment = authorizePaymentSessionStep({
|
||||
id: "payses_123",
|
||||
context: {}
|
||||
context: {},
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
@@ -273,7 +273,7 @@ import { defineLink } from "@medusajs/framework/utils"
|
||||
export default defineLink(
|
||||
{
|
||||
linkable: DigitalProductModule.linkable.digitalProduct,
|
||||
deleteCascade: true
|
||||
deleteCascade: true,
|
||||
},
|
||||
ProductModule.linkable.productVariant
|
||||
)
|
||||
@@ -1534,17 +1534,17 @@ export const retrieveDigitalProductsToDeleteStep = createStep(
|
||||
const query = container.resolve("query")
|
||||
|
||||
const productVariants = await productService.listProductVariants({
|
||||
product_id: product_id
|
||||
product_id: product_id,
|
||||
}, {
|
||||
withDeleted: true
|
||||
withDeleted: true,
|
||||
})
|
||||
|
||||
const { data } = await query.graph({
|
||||
entity: DigitalProductVariantLink.entryPoint,
|
||||
fields: ["digital_product.*"],
|
||||
filters: {
|
||||
product_variant_id: productVariants.map((v) => v.id)
|
||||
}
|
||||
product_variant_id: productVariants.map((v) => v.id),
|
||||
},
|
||||
})
|
||||
|
||||
const digitalProductIds = data.map((d) => d.digital_product.id)
|
||||
@@ -1612,9 +1612,9 @@ You can now create the workflow that executes those steps.
|
||||
Create the file `src/workflows/delete-product-digital-products/index.ts` with the following content:
|
||||
|
||||
```ts title="src/workflows/delete-product-digital-products/index.ts"
|
||||
import { createWorkflow, WorkflowResponse } from "@medusajs/framework/workflows-sdk";
|
||||
import { deleteDigitalProductsSteps } from "./steps/delete-digital-products";
|
||||
import { retrieveDigitalProductsToDeleteStep } from "./steps/retrieve-digital-products-to-delete";
|
||||
import { createWorkflow, WorkflowResponse } from "@medusajs/framework/workflows-sdk"
|
||||
import { deleteDigitalProductsSteps } from "./steps/delete-digital-products"
|
||||
import { retrieveDigitalProductsToDeleteStep } from "./steps/retrieve-digital-products-to-delete"
|
||||
|
||||
type DeleteProductDigitalProductsInput = {
|
||||
id: string
|
||||
@@ -1624,11 +1624,11 @@ export const deleteProductDigitalProductsWorkflow = createWorkflow(
|
||||
"delete-product-digital-products",
|
||||
(input: DeleteProductDigitalProductsInput) => {
|
||||
const digitalProductsToDelete = retrieveDigitalProductsToDeleteStep({
|
||||
product_id: input.id
|
||||
product_id: input.id,
|
||||
})
|
||||
|
||||
deleteDigitalProductsSteps({
|
||||
ids: digitalProductsToDelete
|
||||
ids: digitalProductsToDelete,
|
||||
})
|
||||
|
||||
return new WorkflowResponse({})
|
||||
@@ -1656,10 +1656,10 @@ So, you'll listen to the `product.deleted` event in a subscriber, and execute th
|
||||
Create the file `src/subscribers/handle-product-deleted.ts` with the following content:
|
||||
|
||||
```ts title="src/subscribers/handle-product-deleted.ts"
|
||||
import { SubscriberArgs, SubscriberConfig } from "@medusajs/framework";
|
||||
import { SubscriberArgs, SubscriberConfig } from "@medusajs/framework"
|
||||
import {
|
||||
deleteProductDigitalProductsWorkflow
|
||||
} from "../workflows/delete-product-digital-products";
|
||||
deleteProductDigitalProductsWorkflow,
|
||||
} from "../workflows/delete-product-digital-products"
|
||||
|
||||
export default async function handleProductDeleted({
|
||||
event: { data },
|
||||
@@ -1711,7 +1711,7 @@ import {
|
||||
FulfillmentDTO,
|
||||
FulfillmentItemDTO,
|
||||
FulfillmentOption,
|
||||
FulfillmentOrderDTO
|
||||
FulfillmentOrderDTO,
|
||||
} from "@medusajs/framework/types"
|
||||
|
||||
class DigitalProductFulfillmentService extends AbstractFulfillmentProviderService {
|
||||
@@ -1750,7 +1750,7 @@ class DigitalProductFulfillmentService extends AbstractFulfillmentProviderServic
|
||||
// No data is being sent anywhere
|
||||
return {
|
||||
data,
|
||||
labels: []
|
||||
labels: [],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2606,7 +2606,7 @@ In `src/types/global.ts`, add the following types that you’ll use in your cust
|
||||
```ts title="src/types/global.ts"
|
||||
import {
|
||||
// other imports...
|
||||
StoreProductVariant
|
||||
StoreProductVariant,
|
||||
} from "@medusajs/types"
|
||||
|
||||
// ...
|
||||
@@ -2668,7 +2668,7 @@ export const listProducts = async ({
|
||||
query: {
|
||||
// ...
|
||||
fields: "*variants.calculated_price,+variants.inventory_quantity,+metadata,+tags,*variants.calculated_price,*variants.digital_product",
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
// ...
|
||||
@@ -2707,7 +2707,7 @@ export const getDigitalProductPreview = async function ({
|
||||
{
|
||||
headers,
|
||||
next,
|
||||
cache: "force-cache"
|
||||
cache: "force-cache",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -72,8 +72,8 @@ export const fetchHighlights = [
|
||||
cart_id: cart.id,
|
||||
data: {
|
||||
// pass any data useful for calculation with third-party provider.
|
||||
}
|
||||
})
|
||||
},
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
)
|
||||
@@ -160,7 +160,7 @@ export const highlights = [
|
||||
import { useCart } from "../../../providers/cart"
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
|
||||
export default function CheckoutShippingStep () {
|
||||
export default function CheckoutShippingStep() {
|
||||
const { cart, setCart } = useCart()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [shippingOptions, setShippingOptions] = useState<
|
||||
@@ -171,7 +171,7 @@ export const highlights = [
|
||||
>({})
|
||||
const [
|
||||
selectedShippingOption,
|
||||
setSelectedShippingOption
|
||||
setSelectedShippingOption,
|
||||
] = useState<string | undefined>()
|
||||
|
||||
useEffect(() => {
|
||||
@@ -211,8 +211,8 @@ export const highlights = [
|
||||
cart_id: cart.id,
|
||||
data: {
|
||||
// pass any data useful for calculation with third-party provider.
|
||||
}
|
||||
})
|
||||
},
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
)
|
||||
@@ -253,8 +253,8 @@ export const highlights = [
|
||||
data: {
|
||||
// TODO add any data necessary for
|
||||
// fulfillment provider
|
||||
}
|
||||
})
|
||||
},
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(({ cart: updatedCart }) => {
|
||||
|
||||
@@ -50,12 +50,12 @@ export const fetchHighlights = [
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
password
|
||||
})
|
||||
password,
|
||||
}),
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
@@ -72,12 +72,12 @@ export const fetchHighlights = [
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
password
|
||||
})
|
||||
password,
|
||||
}),
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
@@ -166,12 +166,12 @@ export const highlights = [
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
password
|
||||
})
|
||||
password,
|
||||
}),
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
@@ -188,12 +188,12 @@ export const highlights = [
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
password
|
||||
})
|
||||
password,
|
||||
}),
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
@@ -218,13 +218,13 @@ export const highlights = [
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${token}`,
|
||||
"x-publishable-api-key": process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY || "temp"
|
||||
"x-publishable-api-key": process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY || "temp",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
email
|
||||
})
|
||||
email,
|
||||
}),
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
|
||||
+15
-20
@@ -21,8 +21,8 @@ To login a customer with a third-party service, such as Google or GitHub, you mu
|
||||
|
||||
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 a `code` query parameter. 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 `code` query parameter.
|
||||
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.
|
||||
@@ -194,8 +194,7 @@ Then, in a new page in your storefront that will be used as the callback / redir
|
||||
<CodeTab label="Fetch API" value="fetch">
|
||||
|
||||
export const sendCallbackFetchHighlights = [
|
||||
["6", "code", "The code received from Google as a query parameter."],
|
||||
["7", "state", "The state received from Google as a query parameter."],
|
||||
["6", "queryParams", "The query parameters received from Google, such as `code` and `state`."],
|
||||
["10", "fetch", "Send a request to the Validate Authentication Callback API route"],
|
||||
["18", "!token", "If the token isn't returned, the authentication has failed."],
|
||||
]
|
||||
@@ -205,13 +204,12 @@ import { decodeToken } from "react-jwt"
|
||||
|
||||
// ...
|
||||
|
||||
const queryParams = new URLSearchParams(window.location.search)
|
||||
const code = queryParams.get("code")
|
||||
const state = queryParams.get("state")
|
||||
const searchParams = new URLSearchParams(window.location.search)
|
||||
const queryParams = Object.fromEntries(searchParams.entries())
|
||||
|
||||
const sendCallback = async () => {
|
||||
const { token } = await fetch(
|
||||
`http://localhost:9000/auth/customer/google/callback?code=${code}&state=${state}`,
|
||||
`http://localhost:9000/auth/customer/google/callback?${new URLSearchParams(queryParams).toString()}`,
|
||||
{
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
@@ -250,17 +248,14 @@ export default function GoogleCallback() {
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [customer, setCustomer] = useState<HttpTypes.StoreCustomer>()
|
||||
// for other than Next.js
|
||||
const { code, state } = useMemo(() => {
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
return {
|
||||
code: queryParams.get("code"),
|
||||
state: queryParams.get("state"),
|
||||
};
|
||||
}, []);
|
||||
const queryParams = useMemo(() => {
|
||||
const searchParams = new URLSearchParams(window.location.search)
|
||||
return Object.fromEntries(searchParams.entries())
|
||||
}, [])
|
||||
|
||||
const sendCallback = async () => {
|
||||
const { token } = await fetch(
|
||||
`http://localhost:9000/auth/customer/google/callback?code=${code}&state=${state}`,
|
||||
`http://localhost:9000/auth/customer/google/callback?${new URLSearchParams(queryParams).toString()}`,
|
||||
{
|
||||
credentials: "include",
|
||||
method: "POST",
|
||||
@@ -289,7 +284,7 @@ export default function GoogleCallback() {
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
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 the `code` received from Google.
|
||||
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 the query parameters received from Google.
|
||||
|
||||
Then, replace the `TODO` with the following:
|
||||
|
||||
@@ -566,12 +561,12 @@ export default function GoogleCallback() {
|
||||
const [customer, setCustomer] = useState<HttpTypes.StoreCustomer>()
|
||||
// for other than Next.js
|
||||
const { code, state } = useMemo(() => {
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
const queryParams = new URLSearchParams(window.location.search)
|
||||
return {
|
||||
code: queryParams.get("code"),
|
||||
state: queryParams.get("state"),
|
||||
};
|
||||
}, []);
|
||||
}
|
||||
}, [])
|
||||
|
||||
const sendCallback = async () => {
|
||||
const { token } = await fetch(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export const generatedEditDates = {
|
||||
"app/commerce-modules/auth/auth-providers/emailpass/page.mdx": "2025-01-07T12:47:24.380Z",
|
||||
"app/commerce-modules/auth/auth-providers/emailpass/page.mdx": "2025-01-13T11:31:35.361Z",
|
||||
"app/commerce-modules/auth/auth-providers/page.mdx": "2024-10-08T07:27:21.859Z",
|
||||
"app/commerce-modules/auth/authentication-route/page.mdx": "2025-01-07T09:26:27.809Z",
|
||||
"app/commerce-modules/auth/authentication-route/page.mdx": "2025-01-13T11:31:17.572Z",
|
||||
"app/commerce-modules/auth/examples/page.mdx": "2024-10-15T15:02:13.794Z",
|
||||
"app/commerce-modules/auth/module-options/page.mdx": "2025-01-07T12:47:35.073Z",
|
||||
"app/commerce-modules/auth/page.mdx": "2025-01-09T13:41:05.476Z",
|
||||
@@ -47,7 +47,7 @@ export const generatedEditDates = {
|
||||
"app/commerce-modules/payment/module-options/page.mdx": "2024-10-15T12:51:40.574Z",
|
||||
"app/commerce-modules/payment/payment/page.mdx": "2024-10-09T10:59:08.463Z",
|
||||
"app/commerce-modules/payment/payment-collection/page.mdx": "2024-10-09T10:56:49.510Z",
|
||||
"app/commerce-modules/payment/payment-flow/page.mdx": "2025-01-08T12:51:39.100Z",
|
||||
"app/commerce-modules/payment/payment-flow/page.mdx": "2025-01-13T11:31:35.361Z",
|
||||
"app/commerce-modules/payment/payment-provider/stripe/page.mdx": "2024-12-16T13:21:03.554Z",
|
||||
"app/commerce-modules/payment/payment-provider/page.mdx": "2024-10-09T11:07:27.269Z",
|
||||
"app/commerce-modules/payment/payment-session/page.mdx": "2024-10-09T10:58:00.960Z",
|
||||
@@ -112,7 +112,7 @@ export const generatedEditDates = {
|
||||
"app/nextjs-starter/page.mdx": "2025-01-06T12:19:31.143Z",
|
||||
"app/recipes/b2b/page.mdx": "2024-10-03T13:07:44.153Z",
|
||||
"app/recipes/commerce-automation/page.mdx": "2024-10-16T08:52:01.585Z",
|
||||
"app/recipes/digital-products/examples/standard/page.mdx": "2025-01-07T14:39:31.335Z",
|
||||
"app/recipes/digital-products/examples/standard/page.mdx": "2025-01-13T11:31:35.362Z",
|
||||
"app/recipes/digital-products/page.mdx": "2025-01-06T11:19:35.623Z",
|
||||
"app/recipes/ecommerce/page.mdx": "2024-10-22T11:01:01.218Z",
|
||||
"app/recipes/integrate-ecommerce-stack/page.mdx": "2024-12-09T13:03:35.846Z",
|
||||
@@ -147,14 +147,14 @@ export const generatedEditDates = {
|
||||
"app/storefront-development/checkout/email/page.mdx": "2024-12-19T16:30:40.122Z",
|
||||
"app/storefront-development/checkout/payment/stripe/page.mdx": "2024-12-19T16:30:39.173Z",
|
||||
"app/storefront-development/checkout/payment/page.mdx": "2025-01-02T08:47:20.531Z",
|
||||
"app/storefront-development/checkout/shipping/page.mdx": "2024-12-26T15:34:51.431Z",
|
||||
"app/storefront-development/checkout/shipping/page.mdx": "2025-01-13T11:31:35.361Z",
|
||||
"app/storefront-development/checkout/page.mdx": "2024-06-12T19:46:06+02:00",
|
||||
"app/storefront-development/customers/addresses/page.mdx": "2025-01-06T16:59:53.878Z",
|
||||
"app/storefront-development/customers/context/page.mdx": "2024-12-19T16:38:43.703Z",
|
||||
"app/storefront-development/customers/log-out/page.mdx": "2024-12-19T16:31:28.347Z",
|
||||
"app/storefront-development/customers/login/page.mdx": "2024-12-19T16:31:34.194Z",
|
||||
"app/storefront-development/customers/profile/page.mdx": "2024-12-19T16:31:43.978Z",
|
||||
"app/storefront-development/customers/register/page.mdx": "2025-01-07T09:28:42.723Z",
|
||||
"app/storefront-development/customers/register/page.mdx": "2025-01-13T11:31:35.362Z",
|
||||
"app/storefront-development/customers/retrieve/page.mdx": "2025-01-06T16:07:12.542Z",
|
||||
"app/storefront-development/customers/page.mdx": "2024-06-13T12:21:54+03:00",
|
||||
"app/storefront-development/products/categories/list/page.mdx": "2024-12-19T16:33:06.547Z",
|
||||
@@ -192,7 +192,7 @@ export const generatedEditDates = {
|
||||
"app/usage/page.mdx": "2024-05-13T18:55:11+03:00",
|
||||
"app/page.mdx": "2025-01-09T11:40:57.826Z",
|
||||
"app/commerce-modules/auth/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||
"app/commerce-modules/auth/auth-flows/page.mdx": "2025-01-07T09:29:52.153Z",
|
||||
"app/commerce-modules/auth/auth-flows/page.mdx": "2025-01-13T11:31:35.361Z",
|
||||
"app/commerce-modules/auth/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||
"app/commerce-modules/auth/auth-identity-and-actor-types/page.mdx": "2025-01-07T09:02:27.235Z",
|
||||
"app/commerce-modules/api-key/page.mdx": "2025-01-09T13:41:04.492Z",
|
||||
@@ -861,9 +861,9 @@ export const generatedEditDates = {
|
||||
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminClaimPreviewResponse/page.mdx": "2025-01-07T12:54:21.225Z",
|
||||
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminOrderEditPreviewResponse/page.mdx": "2025-01-07T12:54:21.543Z",
|
||||
"references/types/interfaces/types.BaseClaim/page.mdx": "2025-01-07T12:54:20.996Z",
|
||||
"app/commerce-modules/auth/auth-providers/github/page.mdx": "2025-01-07T12:47:30.196Z",
|
||||
"app/commerce-modules/auth/auth-providers/google/page.mdx": "2025-01-07T12:47:32.501Z",
|
||||
"app/storefront-development/customers/third-party-login/page.mdx": "2025-01-06T16:05:41.159Z",
|
||||
"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-01-13T11:31:35.362Z",
|
||||
"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",
|
||||
@@ -5788,7 +5788,7 @@ export const generatedEditDates = {
|
||||
"references/types/StockLocationTypes/interfaces/types.StockLocationTypes.FilterableStockLocationAddressProps/page.mdx": "2025-01-07T12:54:23.060Z",
|
||||
"references/types/StockLocationTypes/types/types.StockLocationTypes.UpdateStockLocationAddressInput/page.mdx": "2025-01-07T12:54:23.057Z",
|
||||
"references/types/StockLocationTypes/types/types.StockLocationTypes.UpsertStockLocationAddressInput/page.mdx": "2025-01-07T12:54:23.058Z",
|
||||
"app/commerce-modules/inventory/inventory-kit/page.mdx": "2025-01-09T09:39:50.221Z",
|
||||
"app/commerce-modules/inventory/inventory-kit/page.mdx": "2025-01-13T11:31:35.362Z",
|
||||
"app/commerce-modules/api-key/workflows/page.mdx": "2025-01-09T13:41:46.573Z",
|
||||
"app/commerce-modules/api-key/js-sdk/page.mdx": "2025-01-09T12:04:39.787Z",
|
||||
"app/commerce-modules/auth/js-sdk/page.mdx": "2025-01-09T13:27:54.016Z",
|
||||
|
||||
+2
-4
@@ -3,10 +3,8 @@
|
||||
* operationId: PostActor_typeAuth_providerCallback
|
||||
* summary: Validate Authentication Callback
|
||||
* description: >
|
||||
* This API route is used by your dashboard or frontend application when a third-party provider redirects to it after authentication.
|
||||
*
|
||||
*
|
||||
* It validates the authentication with the third-party provider and, if successful, returns an authentication token.
|
||||
* This API route is used by your dashboard or frontend application when a third-party provider redirects to it after authentication. It validates the authentication with the third-party provider and, if successful, returns an authentication token.
|
||||
* All query parameters received from the third-party provider, such as `code`, `state`, and `error`, must be passed as query parameters to this route.
|
||||
*
|
||||
*
|
||||
* You can decode the JWT token using libraries like [react-jwt](https://www.npmjs.com/package/react-jwt) in the frontend. If the decoded data doesn't
|
||||
|
||||
+2
-5
@@ -3,11 +3,8 @@
|
||||
* operationId: PostActor_typeAuth_providerCallback
|
||||
* summary: Validate Authentication Callback
|
||||
* description: >
|
||||
* This API route is used by your storefront or frontend application when a third-party provider redirects to it after authentication.
|
||||
*
|
||||
*
|
||||
* It validates the authentication with the third-party provider and, if successful, returns an authentication token.
|
||||
*
|
||||
* This API route is used by your storefront or frontend application when a third-party provider redirects to it after authentication. It validates the authentication with the third-party provider and, if successful, returns an authentication token.
|
||||
* All query parameters received from the third-party provider, such as `code`, `state`, and `error`, must be passed as query parameters to this route.
|
||||
*
|
||||
* You can decode the JWT token using libraries like [react-jwt](https://www.npmjs.com/package/react-jwt) in the storefront. If the decoded data doesn't
|
||||
* have an `actor_id` property, then you must register the customer using the Create Customer API route passing the token in the request's Authorization header.
|
||||
|
||||
Reference in New Issue
Block a user