-
Notifications
You must be signed in to change notification settings - Fork 57
PAPI-3173 - Introduce private token authentication and deprecate simple token for S2S requests #1245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
PAPI-3173 - Introduce private token authentication and deprecate simple token for S2S requests #1245
Changes from all commits
497040f
f279ad3
093a24a
6413e3c
fba659a
fe65784
705e385
5c6e2e3
16898d7
2a3b6f5
c8290a1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,6 +1,6 @@ | ||||||
| # Authenticating requests to the GraphQL Storefront API | ||||||
|
|
||||||
| Authenticate GraphQL Storefront API requests using bearer tokens passed with the `Authorization` header. You can authenticate using two different kinds of tokens: [storefront tokens](#storefront-tokens) or [customer impersonation tokens](#customer-impersonation-tokens). | ||||||
| Authenticate GraphQL Storefront API requests using bearer tokens passed with the `Authorization` header. You can authenticate using three different kinds of tokens: [storefront tokens](#storefront-tokens), [private tokens](#private-tokens), or [customer impersonation tokens](#customer-impersonation-tokens). | ||||||
|
|
||||||
| ```http filename="Example request configuration" showLineNumbers copy | ||||||
| POST https://your_store.example.com/graphql | ||||||
|
|
@@ -15,15 +15,19 @@ Content-Type: application/json | |||||
|
|
||||||
| ## Storefront tokens | ||||||
|
|
||||||
| Storefront tokens are most appropriate to use directly from the web browser, but you can use them in server-to-server communications. If you're creating a token for an application that will make server-to-server or proxied requests to the GraphQL Storefront API, or you work with customer data, use a [customer impersonation token](#customer-impersonation-tokens). If you only wish to query information from an anonymous shopper's perspective, use a storefront token. | ||||||
| Storefront tokens are designed for use directly from the web browser. They support CORS via `allowed_cors_origins` and are intended for browser-based applications. For server-to-server integrations, use [private tokens](#private-tokens) instead. If you need to work with customer data, use a [customer impersonation token](#customer-impersonation-tokens). If you only wish to query information from an anonymous shopper's perspective in a browser context, use a storefront token. | ||||||
|
|
||||||
| <Callout type="warning"> | ||||||
| **Deprecation notice:** A deprecation date will be introduced soon. After this date, new storefront tokens created will not be usable statelessly in server-to-server contexts. Existing storefront tokens created before the deprecation date will continue to work in server-to-server contexts. We will contact merchants about migrating to private tokens. If you're planning a server-to-server integration, you should use [private tokens](#private-tokens) now. | ||||||
| </Callout> | ||||||
|
|
||||||
| ### Storefront token security | ||||||
|
|
||||||
| Generally speaking, vanilla storefront tokens are not considered sensitive, and it is safe to expose them in web browsers. Storefront tokens can only expose information and actions that shoppers can access when they browse a storefront. | ||||||
|
|
||||||
| It is possible to create a long-lived token that does not expire. For greater security, we recommend creating shorter-lived tokens and rotating them periodically. | ||||||
|
|
||||||
| For security reasons, GraphQL Storefront API tokens are scoped to particular [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) origins, so you must supply the origin or origins on which you intend to use the token. If you have more than two origins, you will need multiple tokens. If you do not supply any CORS origins, the API will reject requests originating from web browsers, although you can still use it in other contexts. GraphQL server-to-server requests do not require `allowed_cors_origins`. | ||||||
| For security reasons, GraphQL Storefront API tokens are scoped to particular [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) origins, so you must supply the origin or origins on which you intend to use the token. If you have more than two origins, you will need multiple tokens. | ||||||
|
|
||||||
| ### Create a storefront token | ||||||
|
|
||||||
|
|
@@ -57,7 +61,9 @@ content-type: application/json | |||||
|
|
||||||
| ```json filename="Example response: Create a storefront API token" showLineNumbers copy | ||||||
| { | ||||||
| "token": "...eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9...", | ||||||
| "data": { | ||||||
| "token": "...eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9..." | ||||||
| }, | ||||||
| "meta": { | ||||||
| // ... | ||||||
| } | ||||||
|
|
@@ -68,7 +74,7 @@ content-type: application/json | |||||
|
|
||||||
| #### Customer access tokens | ||||||
|
|
||||||
| A customer access token is unique to an individual user's account because it represents an authenticated storefront session for GraphQL requests. You can obtain and use a customer access token only for server-to-server requests. Therefore, you must use the customer access token with a regular storefront token. A customer access token becomes invalid on all devices when you log out of a single device. | ||||||
| A customer access token is unique to an individual user's account because it represents an authenticated storefront session for GraphQL requests. You can obtain and use a customer access token only for server-to-server requests. Therefore, you must use the customer access token with a storefront token or private token. A customer access token becomes invalid on all devices when you log out of a single device. | ||||||
|
|
||||||
| <Callout type="warning"> | ||||||
| Do not use this token for browser-side or client-side requests. | ||||||
|
|
@@ -86,7 +92,7 @@ There are two options to obtain a customer access token. | |||||
| Enter your user email and password to use the login mutation. When using the login mutation in a server-to-server context, the mutation will return a customer access token in response to login actions as part of the GraphQL body instead of a cookie header. From there, you can store the customer access token in the presentation layer's session management system and send it with future GraphQL requests. If the login mutation request is from a browser, we will not return the customer access token in the body, and will instead set a cookie. | ||||||
|
|
||||||
| <Callout type="info"> | ||||||
| * Use the [Create a Token](/docs/rest-authentication/tokens#create-a-token) endpoint to generate the storefront bearer token needed to run the login mutation call. | ||||||
| * Use the [Create a Token](/docs/rest-authentication/tokens#create-a-token) endpoint to generate the storefront token or private token needed to run the login mutation call. | ||||||
| * If you request a customer access token in wrong communication context, you will receive the following error: | ||||||
| ***Customer access token was requested in the body, but it's only returned for server-to-server requests. For browser requests it's set as an httpOnly cookie instead.*** | ||||||
|
|
||||||
|
|
@@ -280,6 +286,59 @@ query CustomerAttributes { | |||||
|
|
||||||
| On Stencil storefronts, you can access a token at render time and pass the token to client-side code using the `{{settings.storefront_api.token}}` Handlebars property. This auto-generated token has an expiry period of 24-48 hours and will rotate before expiration. | ||||||
|
|
||||||
| ## Private tokens | ||||||
|
|
||||||
| Private tokens are designed for server-to-server integrations. They are always stateless (no session required) and provide better performance for server-to-server use cases. | ||||||
|
|
||||||
| ### Private token characteristics | ||||||
|
|
||||||
| - **Server-to-server only:** The API rejects private token-authenticated requests that originate from web browsers | ||||||
| - **Always stateless:** No session or cookie validation required | ||||||
| - **Required for server-to-server:** Private tokens are required for server-to-server integrations without customer impersonation. Storefront tokens cannot be used statelessly in server-to-server contexts. | ||||||
|
|
||||||
| <Callout type="info"> | ||||||
| Private tokens are the recommended choice for new server-to-server integrations that don't require customer impersonation. They provide better performance and are designed specifically for stateless server-to-server use cases. | ||||||
| </Callout> | ||||||
|
|
||||||
| ### Create a private token | ||||||
|
|
||||||
| Use the [Create a private token](/docs/rest-authentication/tokens#create-a-private-token) REST endpoint to create private tokens. Add the [storefront API tokens creation scope](/docs/start/authentication/api-accounts#token-creation-scopes) to the [store-level or app-level API account](/docs/start/authentication/api-accounts) you use to generate tokens. | ||||||
|
|
||||||
| <Tabs items={['Request', 'Response']}> | ||||||
| <Tab> | ||||||
|
|
||||||
| ```http filename="Example request: Create a private token" showLineNumbers copy | ||||||
| POST https://api.bigcommerce.com/stores/{{STORE_HASH}}/v3/storefront/api-token-private | ||||||
| x-auth-token: {{access_token}} | ||||||
| accept: application/json | ||||||
| content-type: application/json | ||||||
|
|
||||||
| { | ||||||
| "channel_ids": [1, 2, 3], // array of integers (must be valid channel IDs on the store) | ||||||
| "expires_at": 1602288000 // when the token will expire, as an integer unix timestamp (in seconds) | ||||||
| } | ||||||
| ``` | ||||||
| </Tab> | ||||||
| <Tab> | ||||||
|
|
||||||
| ```json filename="Example response: Create a private token" showLineNumbers copy | ||||||
| { | ||||||
| "data": { | ||||||
| "token": "...eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9..." | ||||||
| }, | ||||||
| "meta": { | ||||||
| // ... | ||||||
| } | ||||||
| } | ||||||
| ``` | ||||||
| </Tab> | ||||||
| </Tabs> | ||||||
|
|
||||||
| ### Private token security | ||||||
|
|
||||||
| Private tokens are sensitive and should **never** be exposed publicly. Treat them with the same care as other application secrets, such as API account access tokens. Store them securely on your server and never include them in client-side code or expose them in browser contexts. | ||||||
|
|
||||||
| If your token is compromised, you can use the [Revoke a token](/docs/rest-authentication/tokens#revoke-a-token) endpoint. Only use this in emergencies; do not revoke tokens unnecessarily. Instead, use a shorter expiration and allow them to expire naturally. | ||||||
krzysztof-maziarka-bc marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ## Customer impersonation tokens | ||||||
|
|
||||||
|
|
@@ -325,11 +384,12 @@ Content-Type: application/json | |||||
|
|
||||||
| ```json filename="Example response: Create a customer impersonation token" showLineNumbers copy | ||||||
| { | ||||||
| "data": | ||||||
| { | ||||||
| "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" | ||||||
| "data": { | ||||||
| "token": "...eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9..." | ||||||
| }, | ||||||
| "meta": {} | ||||||
| "meta": { | ||||||
| // ... | ||||||
| } | ||||||
| } | ||||||
| ``` | ||||||
| </Tab> | ||||||
|
|
@@ -466,6 +526,7 @@ If you're signed out, the system reverts to a "guest" version of the cart, allow | |||||
| ### Endpoints | ||||||
|
|
||||||
| * [Create a storefront token](/docs/rest-authentication/tokens#create-a-token) | ||||||
| * [Create a private token](/docs/rest-authentication/tokens#create-a-private-token) | ||||||
|
||||||
| * [Create a private token](/docs/rest-authentication/tokens#create-a-private-token) | |
| * [Create a private token](/docs/rest-authentication/tokens#create-a-private-api-token) |
krzysztof-maziarka-bc marked this conversation as resolved.
Show resolved
Hide resolved
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -41,7 +41,7 @@ Optionally, you can add a 3P SSL certificate for the checkout domain by sending | |||||
|
|
||||||
| ### Create tokens for the GraphQL Storefront API | ||||||
|
|
||||||
| After setting up the channel, you're almost ready to authenticate cross-origin requests to the GraphQL Storefront API. You can [Create customer impersonation tokens](/docs/rest-authentication/tokens/customer-impersonation-token#create-a-token) for most headless or server-to-server interactions, or [Storefront tokens](/docs/rest-authentication/tokens#create-a-token) for static frontend site interactions. Use your new channel ID and, where required, supply your channel site as an allowed_cors_origin; otherwise, your requests will be rejected. | ||||||
| After setting up the channel, you're almost ready to authenticate cross-origin requests to the GraphQL Storefront API. For server-to-server interactions, use [private tokens](/docs/rest-authentication/tokens#create-a-private-token). For customer impersonation in server-to-server contexts, use [customer impersonation tokens](/docs/rest-authentication/tokens/customer-impersonation-token#create-a-token). For browser-based applications, use [Storefront tokens](/docs/rest-authentication/tokens#create-a-token). Use your new channel ID and, where required, supply your channel site as an allowed_cors_origin for storefront tokens; otherwise, your requests will be rejected. | ||||||
|
||||||
| After setting up the channel, you're almost ready to authenticate cross-origin requests to the GraphQL Storefront API. For server-to-server interactions, use [private tokens](/docs/rest-authentication/tokens#create-a-private-token). For customer impersonation in server-to-server contexts, use [customer impersonation tokens](/docs/rest-authentication/tokens/customer-impersonation-token#create-a-token). For browser-based applications, use [Storefront tokens](/docs/rest-authentication/tokens#create-a-token). Use your new channel ID and, where required, supply your channel site as an allowed_cors_origin for storefront tokens; otherwise, your requests will be rejected. | |
| After setting up the channel, you're almost ready to authenticate cross-origin requests to the GraphQL Storefront API. For server-to-server interactions, use [private tokens](/docs/rest-authentication/tokens#create-a-private-api-token). For customer impersonation in server-to-server contexts, use [customer impersonation tokens](/docs/rest-authentication/tokens/customer-impersonation-token#create-a-token). For browser-based applications, use [Storefront tokens](/docs/rest-authentication/tokens#create-a-token). Use your new channel ID and, where required, supply your channel site as an allowed_cors_origin for storefront tokens; otherwise, your requests will be rejected. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,15 +19,15 @@ When you sign in a customer using the login mutation, subsequent queries to the | |
|
|
||
| ### Headless and server-side sign-in | ||
|
|
||
| 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. | ||
| 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) with [private tokens](/docs/start/authentication/graphql-storefront#private-tokens). Then, use the customer access token in the `X-Bc-Customer-Access-Token` header. | ||
|
|
||
| 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 | ||
|
|
||
| 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. | ||
|
|
||
| ```js "Generate redirectUrl example" showLineNumbers copy | ||
| ```graphql "Generate redirectUrl example" showLineNumbers copy | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The MDX validator flagged a syntax problem here. |
||
| mutation createRedirectUrl($input: CreateCartRedirectUrlsInput!) { | ||
| cart { | ||
| createCartRedirectUrls(input: $input) { | ||
|
|
@@ -43,13 +43,13 @@ The other option is when a customer signs in to your headless storefront and you | |
|
|
||
| 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 | ||
| ```json filename="Example JWT payload" showLineNumbers copy | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The MDX validator flagged a syntax problem here. |
||
| { | ||
| "iss": {{CLIENT_ID}}, | ||
| "iss": "{{CLIENT_ID}}", | ||
| "iat": 1535393113, | ||
| "jti": {{UUID}}, | ||
| "jti": "{{UUID}}", | ||
| "operation": "customer_login", | ||
| "store_hash": {{STORE_HASH}}, | ||
| "store_hash": "{{STORE_HASH}}", | ||
| "customer_id": {{CUSTOMER_ID}}, | ||
| "channel_id": {{CHANNEL_ID}}, | ||
| "redirect_to": "/cart.php?embedded=1&action=loadInCheckout&id=bc218c65-7a32-4ab7-8082-68730c074d02&token=aa958e2b7922035bf3339215d95d145ebd9193deb36ae847caa780aa2e003e4b", | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The anchor link
#create-a-private-tokenmay not match the generated anchor from the API specification. The summary for the private token endpoint is "Create a Private API Token" (line 144), which would typically generate an anchor like#create-a-private-api-token, not#create-a-private-token. Please verify that the anchor link correctly matches what the documentation system generates. You may need to either: 1) update the links to use#create-a-private-api-token, or 2) update the summary to "Create a Private Token" to match the anchor.