Skip to content
32 changes: 23 additions & 9 deletions docs/storefront/headless/customers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,37 @@ This section covers different ways to associate customers with headless carts.

You can use the GraphQL Storefront API to sign in to a customer account using hosted client-side, headless client-side, or server-side code.

### Hosted client-side sign in
### Hosted client-side sign-in

To sign in a customer account with an email address and a password in client-side code on a hosted storefront, use the [login mutation](/docs/start/authentication/graphql-storefront#signing-customers-in).
To sign into a customer account with an email address and a password in client-side code on a hosted storefront, use the [login mutation](/docs/start/authentication/graphql-storefront#signing-customers-in).

When you sign in a customer using the login mutation, subsequent queries made to the GraphQL Storefront API will return customer-specific results, such as customer group pricing.
When you sign in a customer using the login mutation, subsequent queries to the GraphQL Storefront API will return customer-specific results, such as customer group pricing.

### Headless and server-side sign in
### Headless and server-side sign-in

To make queries from the perspective of a particular customer in headless or server-side code, use [customer impersonation tokens](/docs/start/authentication/graphql-storefront#customer-impersonation-tokens) rather than storefront tokens.
To make queries from the perspective of a particular customer using headless or server-side code, use [customer access tokens](/docs/start/authentication/graphql-storefront#customer-access-tokens) and [storefront tokens](/docs/start/authentication/graphql-storefront#storefront-tokens). Then, use the customer access token in the `X-Bc-Customer-Access-Token` header.

Depending on your implementation, you can use the login mutation or a combination of the [Current Customer API](/docs/start/authentication/current-customer), the [Customer Login API](/docs/start/authentication/customer-login), or the REST Management API's [customers feature](/docs/rest-management/customers) to authenticate the customer and get their customer ID. Pass the customer ID with your GraphQL Storefront requests using the `X-Bc-Customer-Id` header to interact with the store from the customer's perspective. The responses will reflect customer-specific pricing, product availability, account information, and more.
We recommend using the login mutation and customer access token because this combination is more secure. In addition, there is a seamless redirection and synchronization between headless storefronts and hosted checkouts, which allow for transferring session details, such as customer and cart data, across various contexts.

## Customer single sign-on

When a customer signs in to your headless storefront and your application redirects them to a BigCommerce-hosted page, such as checkout, you can use the [Customer Login API](/docs/start/authentication/customer-login) to sign them in to the hosted session.
If using the customer access token, then you just need to use the `createCartRedirectUrls` mutation and use the redirectUrl provided there. It will be a session sync link that will copy data from the headless storefront to the BigCommerce-hosted page. This approach simplifies session synchronization and offers consistent login states.

You can sign a customer in to an embedded checkout by setting `redirect_to` in the Customer Login JWT payload to the relative path of the `embedded_checkout_url` generated using the [REST Management API carts feature](/docs/rest-management/carts) or the GraphQL Storefront API [createCartRedirectUrls mutation](/graphql-storefront/reference#definition-CartMutations).
```js "Generate redirectUrl example" showLineNumbers copy
mutation createRedirectUrl($input: CreateCartRedirectUrlsInput!) {
cart {
createCartRedirectUrls(input: $input) {
redirectUrls {
redirectedCheckoutUrl
}
}
}
}
```

The other option is when a customer signs in to your headless storefront and your application redirects them to a BigCommerce-hosted page, such as checkout, you can use the [Customer Login API](/docs/start/authentication/customer-login) to sign them in to the hosted session.

You can sign a customer in to an embedded checkout by using the session-sync URL from the [createCartRedirectUrls mutation](https://developer.bigcommerce.com/graphql-storefront/reference#definition-CartMutations).

```js filename="Example JWT payload" showLineNumbers copy
{
Expand All @@ -51,7 +65,7 @@ To address this need, BigCommerce provides the [Current Customer API](/docs/star

## Surfacing customer group pricing

When querying the GraphQL Storefront API, customer-specific pricing will be reflected in query results if the request is made using a [customer impersonation token](/docs/start/authentication/graphql-storefront#customer-impersonation-tokens).
When querying the GraphQL Storefront API, customer-specific pricing will be reflected in query results if the request is made using a [customer access token](/docs/start/authentication/graphql-storefront#customer-access-tokens).

For server-side REST implementations, you can use the [REST Management API pricing feature](/docs/rest-management/pricing) to [Get prices](/docs/rest-management/pricing/products#get-prices-batch) for a particular customer group.

Expand Down
136 changes: 87 additions & 49 deletions docs/storefront/headless/end-to-end-guides/graphql-storefront.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,52 +16,72 @@ To complete the guide, you need the following:

## Accessing the GraphQL Storefront API

You can make requests to the GraphQL Storefront API in a server-to-server context. Typically, server-side API clients should use [customer impersonation tokens](/docs/rest-authentication/tokens/customer-impersonation-token) to authenticate requests to the GraphQL Storefront API. Frontend API clients that primarily serve static sites with no sign-in capabilities can use [storefront tokens](/docs/rest-authentication/tokens). However, server-to-server requests can use storefront tokens as well. This guide describes creating and using customer impersonation tokens to authenticate and use the GraphQL Storefront API.
You can make requests to the GraphQL Storefront API in a server-to-server context. This guide describes creating and using a customer access token to authenticate and use the GraphQL Storefront API.

### Create a customer impersonation token
### Create a customer access token

A customer impersonation token lets you conduct actions for any shopper, in the context of their account. Customer impersonation tokens are sensitive, and you must not expose them publicly. To generate a customer impersonation token, send a request to the [Create a customer impersonation token](/docs/rest-authentication/tokens/customer-impersonation-token#create-a-token) endpoint.

You can make customer impersonation token-authenticated requests by specifying the customer ID as the value of the `X-BC-Customer-Id` header when making requests to the GraphQL Storefront API endpoint. For an example that uses the `X-BC-Customer-Id` header, see this guide's section on [Querying product data](#querying-product-data).

<Callout type="info">
Typically, there is no need to use a customer impersonation token in the GraphQL Storefront API playground. Using the playground with a customer impersonation token is solely an example in this guide.
</Callout>
A customer access token is unique to an individual user's account. To generate a customer access token, enter your GraphQL variables (user email and password) and run the [login mutation](/docs/start/authentication/graphql-storefront#login-mutation).

<Tabs items={['Request', 'Response']}>
<Tab>
```http filename="Create a customer impersonation token" showLineNumbers copy
POST https://api.bigcommerce.com/stores/{{STORE_HASH}}/v3/storefront/api-token-customer-impersonation
X-Auth-Token: {{ACCESS_TOKEN}}
Accept: application/json
Content-Type: application/json

```json filename="GraphQL variables" showLineNumbers copy
{
"channel_id": 1,
"expires_at": 1602288000
"email": "user@email.com",
"pass": "password"
}
```

</Tab>
<Tab>
```graphql filename="Create a customer access token" showLineNumbers copy
# Create a customer access token
POST https:{{storeDomain}}/graphql
Authorization: Bearer {{STOREFRONT_TOKEN}}
Accept: application/json
Content-Type: application/json

```json filename="Create a customer impersonation token" showLineNumbers copy
{
"data":
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
mutation Login($email: String!, $pass: String!) {
login(email: $email, password: $pass) {
result
customer {
entityId
email
}
customerAccessToken {
value
expiresAt
}
}
}
```
</Tab>

<Tab>
```json filename="Example login mutation: Create a customer access token" showLineNumbers copy
{
"data": {
"login": {
"result": "success",
"customer": {
"entityId": 21,
"email": "user@email.com"
},
"customerAccessToken": {
"value": "53ea9099d5484cdedddbf8f59a73bc284afefd7943c4dbe578db52435e0c0de7_1721406629",
"expiresAt": "2024-07-19T16:30:29Z"
}
}
"meta": {}
}
```
</Tab>
}
```
</Tab>
</Tabs>

In the playground, add the customer impersonation token to the **Headers** tab as follows. Ensure that you leave a space between `Bearer` and the customer impersonation token.

```json filename="Authorization header with customer impersonation token" copy
In a tool like [Postman](https://postman.com/) or [Altair](https://altairgraphql.dev/), add the customer access token to the **Headers** tab as follows. For more information, see Using a [Customer Access Token](/docs/start/authentication/graphql-storefront#using-a-customer-access-token).

```json filename="Authorization header with customer access token" copy
{
"Authorization": "Bearer {customer impersonation token}"
"X-Bc-Customer-Access-Token": "{{customer access token}}",
"Authorization": "Bearer {{STOREFRONT_TOKEN}}"
}
```

Expand All @@ -82,9 +102,9 @@ When possible, use the following best practices for queries to the GraphQL Store
<Tab>
```http filename="Get a product" showLineNumbers copy
GET https://store.example.com/graphql
Authorization: Bearer {{CUSTOMER_IMPERSONATION_TOKEN}}
Authorization: Bearer {{STOREFRONT_TOKEN}}
Accept: application/json
X-Bc-Customer-Id: 123
X-Bc-Customer-Access-Token: {{CUSTOMER_ACCESS_TOKEN}}

# This query retrieves one product.
query SingleProduct ($entityId: Int) {
Expand All @@ -110,9 +130,26 @@ When possible, use the following best practices for queries to the GraphQL Store
<Tab>

```http filename="Get a product" showLineNumbers copy

```

{
"data": {
"site": {
"product": {
"id": "UHJvZHVjdDoxNTk=",
"entityId": 159,
"name": "computer",
"sku": "CMP-1-223",
"description": "<p><span>High speed Computer</span></p>",
"prices": {
"price": {
"value": 739.03,
"currencyCode": "USD"
}
}
}
}
}
}
```
</Tab>
</Tabs>

Expand All @@ -129,8 +166,8 @@ Make note of one of the cart's `entityId` displayed in the response. You will ne
```graphql filename="Create a cart (simple)" showLineNumbers copy
POST https://store.example.com/graphql
Accept: application/json
Authorization: Bearer {{CUSTOMER_IMPERSONATION_TOKEN}}
X-Bc-Customer-Id: 123
Authorization: Bearer {{STOREFRONT_TOKEN}}
X-Bc-Customer-Access-Token: {{CUSTOMER_ACCESS_TOKEN}}

# Creates a new cart, adding a simple product.

Expand Down Expand Up @@ -213,8 +250,8 @@ To add a new line item to the existing cart, add the cart ID to the input variab
<Tab>
```http filename="Add cart line items" showLineNumbers copy
POST https://store.example.com/graphql
Authorization: Bearer {{CUSTOMER_IMPERSONATION_TOKEN}}
X-Bc-Customer-Id: 123
Authorization: Bearer {{STOREFRONT_TOKEN}}
X-Bc-Customer-Access-Token: {{CUSTOMER_ACCESS_TOKEN}}

mutation addCartLineItems($addCartLineItemsInput: AddCartLineItemsInput!) {
cart {
Expand Down Expand Up @@ -276,8 +313,8 @@ You can access cart and checkout details in the same request as other informatio

```graphql filename="Get checkout" showLineNumbers copy
GET https://store.example.com/graphql
Authorization: Bearer {{CUSTOMER_IMPERSONATION_TOKEN}}
X-Bc-Customer-Id: 123
Authorization: Bearer {{STOREFRONT_TOKEN}}
X-Bc-Customer-Access-Token: {{CUSTOMER_ACCESS_TOKEN}}

query getCheckout($entityId: Int) {
site {
Expand Down Expand Up @@ -530,8 +567,8 @@ This mutation adds a billing address to an existing checkout.
<Tab>
```graphql filename="Add a checkout billing address" showLineNumbers copy
POST https://store.example.com/graphql
Authorization: Bearer {{CUSTOMER_IMPERSONATION_TOKEN}}
X-Bc-Customer-Id: 123
Authorization: Bearer {{STOREFRONT_TOKEN}}
X-Bc-Customer-Access-Token: {{CUSTOMER_ACCESS_TOKEN}}

mutation addCheckoutBillingAddress($addCheckoutBillingAddressInput: AddCheckoutBillingAddressInput!) {
checkout {
Expand Down Expand Up @@ -601,8 +638,8 @@ Make a note of a shipping consignment's `entityId` and the desired shipping opti
<Tab>
```graphql filename="Add a checkout shipping consignment" showLineNumbers copy
POST https://store.example.com/graphql
Authorization: Bearer {{CUSTOMER_IMPERSONATION_TOKEN}}
X-Bc-Customer-Id: 123
Authorization: Bearer {{STOREFRONT_TOKEN}}
X-Bc-Customer-Access-Token: {{CUSTOMER_ACCESS_TOKEN}}

mutation addCheckoutShippingConsignments($addCheckoutShippingConsignmentsInput: AddCheckoutShippingConsignmentsInput!) {
checkout {
Expand Down Expand Up @@ -700,8 +737,8 @@ This mutation adds a selected shipping to an existing checkout.
<Tab>
```graphql filename="Select checkout shipping option" showLineNumbers copy
POST https://store.example.com/graphql
Authorization: Bearer {{CUSTOMER_IMPERSONATION_TOKEN}}
X-Bc-Customer-Id: 123
Authorization: Bearer {{STOREFRONT_TOKEN}}
X-Bc-Customer-Access-Token: {{CUSTOMER_ACCESS_TOKEN}}

mutation selectCheckoutShippingOption($selectCheckoutShippingOptionInput: SelectCheckoutShippingOptionInput!) {
checkout {
Expand Down Expand Up @@ -757,8 +794,8 @@ Completing a checkout creates an incomplete order until you process the payment.
<Tab>
```graphql filename="Complete checkout" showLineNumbers copy
POST https://store.example.com/graphql
Authorization: Bearer {{CUSTOMER_IMPERSONATION_TOKEN}}
X-Bc-Customer-Id: 123
Authorization: Bearer {{STOREFRONT_TOKEN}}
X-Bc-Customer-Access-Token: {{CUSTOMER_ACCESS_TOKEN}}

mutation completeCheckout($completeCheckoutInput: CompleteCheckoutInput!) {
checkout {
Expand Down Expand Up @@ -803,6 +840,7 @@ Completing a checkout creates an incomplete order until you process the payment.
### GraphQL Storefront API

* [Storefront tokens](/docs/storefront/cart-checkout/guide/graphql-storefront#tokens)
* [Customer access tokens](/docs/start/authentication/graphql-storefront#customer-access-tokens)
* [Querying within a BigCommerce storefront](/docs/storefront/graphql#querying-within-a-bigcommerce-storefront)
* [Create a cart using a simple product](/docs/storefront/cart-checkout/guide/graphql-storefront#create-a-cart-using-a-simple-product)
* [Add cart line items](/docs/storefront/cart-checkout/guide/graphql-storefront#add-cart-line-items)
Expand Down
72 changes: 72 additions & 0 deletions docs/storefront/headless/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,78 @@ The following example uses the GraphQL Storefront API to complete a checkout and
</Tab>
</Tabs>

## Headless/GraphQL account login

BigCommerce has introduced a feature that leverages the [Customer Access Token](/docs/start/authentication/graphql-storefront#customer-access-tokens) for seamless redirection, logging in customers automatically when they reach checkout from the storefront. Built with JWT-based "Session Sync," this enhancement enables transferring session details, such as customer and cart data, across various contexts. Developers can use GraphQL API for advanced session syncing, ensuring a smoother, cohesive experience for customers across platforms.

The following examples demonstrate how to sync and validate session details for headless storefronts and hosted checkouts.

<Tabs items={['Format', 'Mutation', 'Validate']}>
<Tab>

```json # Low-level session sync JWT generation
# Returns JUST a JWT, *not* a URL
# Can "sync" any arbitrary combination of parameters
# e.g., just sync analytics ID, just sync customer ID, just sync analytics + customer, etc
# Designed to be used omni-directionally
# e.g., headless -> stencil, stencil-> headless, headless->headless, stencil->stencil

mutation GenerateSessionSyncJwt ($redirectTo: String!,$cartId: String,$visitorId: String,$ipAddress: IpAddress){
generateSessionSyncJwt(redirectTo:$redirectTo,cartId: $cartId, visitorId: $visitorId, ipAddress: $ipAddress){
result {
token
url
}
}
}
```
</Tab>
<Tab>

```json # Low-level session sync JWT generation
# Returns JUST a JWT, *not* a URL
# Can "sync" any arbitrary combination of parameters
# e.g., just sync analytics ID, just sync customer ID, just sync analytics + customer, etc
# Designed to be used omni-directionally
# e.g., headless -> stencil, stencil-> headless, headless->headless, stencil->stencil

mutation GenerateSessionSyncJwt {
generateSessionSyncJwt(redirectTo:"/foo",visitorId:"abc123"){
result {
token
url
}
}
}
```
</Tab>

<Tab>
```json #
mutation ValidateJwt{
validateSessionSyncJwt(jwt: "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJCQyIsImlhdCI6MTczMDQwMTc3MiwiZWF0IjoxNzMwNDAxODAyLCJqdGkiOiI2NTI5NDAyMi02MDA4LTRmY2YtOTUxOC03MWE5NjA0MjEyMjgiLCJvcGVyYXRpb24iOiJzZXNzaW9uX3N5bmMiLCJzaWQiOjczMzE5Nywic2Vzc2lvbl9wYXlsb2FkIjp7ImNhcnRfaWQiOm51bGwsImN1c3RvbWVyX2lkIjpudWxsLCJ2aXNpdG9yX2lkIjoiYWJjMTIzIn0sInJlcXVlc3RfaXAiOm51bGwsInJlZGlyZWN0X3RvIjoiaHR0cHM6Ly9iaWdjb21tZXJjZS5zdXBwb3J0L2ZvbyJ9.TAueByfTCdpw3UwwuKElUoeEjYcqCT13H3pt7UzIh6t6682_w4aaz1akS-FOyyOstOUYcjyUPLN5qi04eXXKKA"){
content{
analytics{
visitorId
}
cart{
entityId
}
customer {
firstName
}
customerAccessToken{
expiresAt
value
}
redirectTo
}
}
}
```
</Tab>
</Tabs>

## Next step

- [Learn how to create a channel and a channel site](/docs/storefront/headless/channels)
Loading