> ## Documentation Index
> Fetch the complete documentation index at: https://firespark.cloud/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Exchange token

> Exchange an OIDC ID token from your identity provider for a Fire spark access token for the authenticated customer.

The Storefront API supports [OAuth 2.0 Token Exchange](https://datatracker.ietf.org/doc/html/rfc8693) (RFC 8693) for **public clients**. Your app, web, or kiosk sends the customer's **OIDC ID token** and receives a short-lived Fire spark access token in return.

No `client_id` or `client_secret` is required. Fire spark reads the ID token's `iss` and `aud` claims, finds the merchant's configured identity provider, validates the token against that provider's JWKS, and issues an access token bound to the customer identified by the `sub` claim.

## When to use token exchange

Use this endpoint when:

* Customers sign in with an OIDC-compliant identity provider on the client
* Your channel needs to call the Storefront API on behalf of that customer
* You want Fire spark to validate identity and limit API access to a single customer

<Tip>
  Send the **ID token**, not opaque access tokens or API keys. ID tokens are JWTs
  that identify the authenticated user through standard claims: `sub`, `iss`, and
  `aud`. Every OIDC-compliant provider exposes them after sign-in.
</Tip>

## Prerequisites

1. Configure your identity provider in the [Fire spark dashboard](https://firespark.cloud) for your merchant.
2. Authenticate the customer on the client and obtain a fresh OIDC ID token from your provider.

## How it works

<Steps>
  <Step title="Customer signs in on the client">
    Your app completes sign-in with your OIDC provider using that provider's client
    SDK. Retrieve the ID token the provider issues for the authenticated session.
  </Step>

  <Step title="Client sends the ID token">
    Your client calls `POST /oauth/exchange` with the ID token as `subject_token`.
    No merchant ID or API secret is needed in the request.
  </Step>

  <Step title="Fire spark resolves the merchant and validates the token">
    Fire spark matches the token's `iss` and `aud` to the provider configured for
    a merchant, verifies the signature and expiration using that provider's JWKS,
    and reads `sub` as the customer's external identifier.
  </Step>

  <Step title="Client uses the Fire spark access token">
    Fire spark returns an access token for that customer. Use it in the
    `Authorization` header for Storefront API requests.
  </Step>
</Steps>

## Obtain the ID token

Each provider exposes the ID token through its client SDK. Use the method that returns the **JWT ID token** for the current session.

<Tabs>
  <Tab title="Auth0">
    ```javascript theme={null}
    import { useAuth0 } from "@auth0/auth0-react";

    const { getIdTokenClaims } = useAuth0();
    const claims = await getIdTokenClaims();
    const idToken = claims?.__raw;
    ```
  </Tab>

  <Tab title="Clerk">
    ```javascript theme={null}
    import { useAuth } from "@clerk/clerk-react";

    const { getToken } = useAuth();
    const idToken = await getToken();
    ```
  </Tab>

  <Tab title="Supabase">
    ```javascript theme={null}
    import { createClient } from "@supabase/supabase-js";

    const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
    const { data } = await supabase.auth.getSession();
    const idToken = data.session?.access_token;
    ```

    Supabase returns a signed JWT with `sub`, `iss`, and `aud` claims. Use the
    session JWT immediately after sign-in while it is still valid.
  </Tab>

  <Tab title="Firebase">
    ```javascript theme={null}
    import { getAuth } from "firebase/auth";

    const auth = getAuth();
    const idToken = await auth.currentUser?.getIdToken();
    ```
  </Tab>

  <Tab title="Amazon Cognito">
    ```javascript theme={null}
    import { fetchAuthSession } from "aws-amplify/auth";

    const session = await fetchAuthSession();
    const idToken = session.tokens?.idToken?.toString();
    ```
  </Tab>

  <Tab title="Okta">
    ```javascript theme={null}
    import OktaAuth from "@okta/okta-auth-js";

    const authClient = new OktaAuth({ issuer, clientId });
    const { idToken } = await authClient.tokenManager.get("idToken");
    const idTokenJwt = idToken.idToken;
    ```
  </Tab>
</Tabs>

## Token exchange request

Send a `POST` request to `/oauth/exchange` with `Content-Type: application/json`. The request body is the same regardless of provider.

<RequestExample>
  ```javascript Any OIDC provider theme={null}
  const response = await fetch(
    "https://firespark.cloud/api/storefront/v1/oauth/exchange",
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
        subject_token: idToken,
        subject_token_type: "urn:ietf:params:oauth:token-type:id_token"
      })
    }
  );

  const { access_token } = await response.json();
  ```

  ```bash cURL theme={null}
  curl -X POST "https://firespark.cloud/api/storefront/v1/oauth/exchange" \
    -H "Content-Type: application/json" \
    -d '{
      "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
      "subject_token": "YOUR_ID_TOKEN",
      "subject_token_type": "urn:ietf:params:oauth:token-type:id_token"
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json Success theme={null}
  {
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
    "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
    "token_type": "Bearer",
    "expires_in": 3600
  }
  ```
</ResponseExample>

### Request body

| Field                | Required | Description                                                                                 |
| -------------------- | -------- | ------------------------------------------------------------------------------------------- |
| `grant_type`         | Yes      | Must be `urn:ietf:params:oauth:grant-type:token-exchange`.                                  |
| `subject_token`      | Yes      | The OIDC ID token from your identity provider for the signed-in customer.                   |
| `subject_token_type` | Yes      | Must be `urn:ietf:params:oauth:token-type:id_token`. Opaque access tokens are not accepted. |

### How Fire spark identifies the merchant

Fire spark does not require a merchant ID in the request. It inspects the ID token claims:

| Claim | Purpose                                                                   |
| ----- | ------------------------------------------------------------------------- |
| `iss` | Identifies the identity provider issuer URL.                              |
| `aud` | Identifies the provider project, realm, or app client.                    |
| `sub` | Stable customer identifier used to resolve or create the customer record. |

Fire spark matches `iss` and `aud` against the provider configuration registered for a merchant, then validates the token signature before issuing an access token.

### Supported providers

Any OIDC-compliant provider that issues JWT ID tokens with a discoverable JWKS endpoint is supported. Register the provider in the dashboard before going to production.

| Provider       | Typical `iss` format                                      | How to get the ID token             |
| -------------- | --------------------------------------------------------- | ----------------------------------- |
| Auth0          | `https://{tenant}.auth0.com/`                             | `getIdTokenClaims().__raw`          |
| Clerk          | `https://{your-clerk-domain}`                             | `getToken()`                        |
| Supabase       | `https://{project-ref}.supabase.co/auth/v1`               | `getSession().access_token`         |
| Firebase       | `https://securetoken.google.com/{projectId}`              | `getIdToken()`                      |
| Amazon Cognito | `https://cognito-idp.{region}.amazonaws.com/{userPoolId}` | `fetchAuthSession().tokens.idToken` |
| Okta           | `https://{okta-domain}/oauth2/{authorizationServerId}`    | `tokenManager.get("idToken")`       |
| Keycloak       | `https://{host}/realms/{realm}`                           | `keycloak.idToken`                  |

<Note>
  The `iss` and `aud` values must match exactly what Fire spark has on file for
  your merchant. Copy them from a decoded ID token when configuring the provider
  in the dashboard.
</Note>

## Use the exchanged token

Include the Fire spark access token in the `Authorization` header. The token only grants access to resources for the customer linked to the original `sub` claim.

```bash theme={null}
curl "https://firespark.cloud/api/storefront/v1/customers/me" \
  -H "Authorization: Bearer FIRESPARK_ACCESS_TOKEN"
```

<Note>
  Re-exchange when your provider refreshes the ID token. Fire spark access tokens
  are short-lived and are not a replacement for keeping the IdP session current.
</Note>

## Security properties

* **Public client profile:** Designed for mobile and web clients. No confidential credentials are sent or stored in the app.
* **ID token only:** Only OIDC JWT ID tokens are accepted as `subject_token`. Opaque or provider API tokens are rejected.
* **Cryptographic validation:** Signature, `iss`, `aud`, and `exp` are verified against the merchant's registered provider before any customer data is returned.
* **Customer isolation:** The issued access token is bound to the `sub` from the ID token. Requests for other customers are rejected.
* **Short-lived tokens:** Exchanged tokens expire after `expires_in` seconds.

<Warning>
  Always call this endpoint over HTTPS. Do not log ID tokens or Fire spark access
  tokens. Refresh the exchange when your provider rotates the ID token.
</Warning>

## Error responses

Errors follow RFC 6749 and RFC 8693. The endpoint returns `application/json` with an `error` field.

| Error                    | Description                                                                                 |
| ------------------------ | ------------------------------------------------------------------------------------------- |
| `invalid_request`        | A required field is missing or malformed.                                                   |
| `invalid_grant`          | The ID token is invalid, expired, or does not match any registered provider for a merchant. |
| `unsupported_grant_type` | The `grant_type` value is not supported.                                                    |

```json Error theme={null}
{
  "error": "invalid_grant",
  "details": "No merchant is configured for this token issuer and audience."
}
```


## OpenAPI

````yaml POST /oauth/exchange
openapi: 3.0.1
info:
  title: Fire spark Storefront API
  description: Customer-facing channel endpoints for Fire spark.
  version: 1.0.0
servers:
  - url: https://firespark.cloud/api/storefront/v1
security:
  - bearerAuth: []
paths:
  /oauth/exchange:
    post:
      summary: Exchange token
      requestBody:
        required: true
        description: Token exchange request.
        content:
          application/json:
            schema:
              type: object
              required:
                - grant_type
                - subject_token
                - subject_token_type
              properties:
                grant_type:
                  type: string
                  enum:
                    - urn:ietf:params:oauth:grant-type:token-exchange
                subject_token:
                  type: string
                  description: OIDC ID token from your identity provider.
                subject_token_type:
                  type: string
                  enum:
                    - urn:ietf:params:oauth:token-type:id_token
      responses:
        '200':
          description: Ok
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/TokenResponse'
      security: []
components:
  schemas:
    TokenResponse:
      type: object
      properties:
        access_token:
          type: string
        token_type:
          type: string
          example: Bearer
        expires_in:
          type: integer
          example: 3600
        scope:
          type: string
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer

````