The future is agentic.

Core concepts

Commerce Layer authentication: what you must know.

Understand how Commerce Layer authentication works and how to manage guest, customer, and integration tokens with proper caching, security, and real-world development patterns.

MM
Marco
· November 19, 2025

When someone new joins Commerce Layer, our very first chat is almost always about getting to know each other, in real life and via API. So, whether you're just getting started or need a refresher, here's everything you need to know about authentication, tokens, and how to manage them effectively.

Authentication is how applications prove their identity (and who they're acting on behalf of) when calling Commerce Layer APIs. Whether you're building a storefront, an integration, or an operational tool, the flow is the same: request an access token, attach it to your API calls, and refresh or revoke it as needed.

Commerce Layer provides APIs (Core, Provisioning, Metrics) with different access patterns. Tokens make that access explicit and auditable. The token you obtain encodes scope and ownership (guest, customer, or app), which determines exactly what the caller can do.

If you're familiar with OAuth 2.0, the concepts will feel natural — clients, secrets, grant types, access tokens, and sometimes refresh tokens:

  • API credentials define the actor
    Sales channel (for customer‑facing apps) or integration (for backend services).
  • Grant types define how you obtain the token
    Client credentials, password, refresh token, JWT bearer, or authorization code.

In practice, you pair the right credentials with the right grant:

  • Sales channel + client credentials
    Get a guest token for browsing a storefront.
  • Sales channel + password / JWT bearer
    Authenticate a known customer; optionally receive a refresh token for "remember me" functionality.
  • Integration + client credentials
    Obtain a server-to-server token for background jobs, webhooks, or back-office operations.

We won’t go deeper here, our documentation already covers the details about API credentials and grant types.

Tokens don’t last forever

By default, tokens expire after 2 hours for integrations or 4 hours for sales channels. Once expired, you need to request a new one.

However, requesting a fresh token on every API call isn't feasible. Authentication endpoint requests are not cached by Commerce Layer and are subject to rate limits. Without proper token caching in your application, you'll quickly hit 429 Too Many Requests errors.

Depending on where your application runs, choose a storage strategy like:

  • Browsers
    Prefer cookies or local storage. Consider that a cookie can also be shared with your backend so both layers use the same token.
  • Edge / server
    Use a fast store (e.g., Redis, KV). Keep separate storage for guest and customer tokens to avoid leaking a customer token to all users.

Auth library to the rescue

Commerce Layer JS Auth is a lightweight JavaScript library that simplifies authentication with Commerce Layer APIs. It works in the browser, on servers, and at the edge, so you can use it everywhere.

Historically, you’d call the authenticate method with a grant type and a few other options to obtain an access token, then you had to manage the token cache on your own. With v7, we introduced a new, higher-level approach based on API credentials.

It also includes a storage abstraction and helpers so you can cache and retrieve tokens without writing plumbing code.

This new approach maps directly to how you build your app:

  • For storefronts, use the sales channel with makeSalesChannel().
  • For servers, jobs, and webhooks, use the integration with makeIntegration().

Sales channel with guest and customer flows

Storefronts usually start with a guest token and later switch to a customer token when someone signs in. With the sales channel helper, this transition — and the underlying token storage — is handled for you.

import {
  authenticate,
  makeSalesChannel,
  type Storage,
  type StorageValue,
} from "@commercelayer/js-auth"

/**
 * A valid storage must implement the `Storage` interface
 */
function memoryStorage(): Storage {
  const store: Record<string, StorageValue> = {}
  return {
    async getItem(key) {
      return store[key] ?? null
    },
    async setItem(key: string, value: StorageValue) {
      store[key] = value
    },
    async removeItem(key: string) {
      delete store[key]
    },
  }
}

const salesChannel = makeSalesChannel(
  {
    clientId: "<your_client_id>",
    scope: "<your_scope>",
    debug: true, // useful to understand what happens behind the scene
  },
  {
    storage: memoryStorage(),
  },
)

/**
 * At the beginning, you'll get a guest token
 */
const authorization1 = await salesChannel.getAuthorization()

console.log("Guest access token:", authorization1.accessToken)

/**
 * At some point, a customer logs in
 */
const customerAuth = await authenticate("password", {
  clientId: "<your_client_id>",
  scope: "<your_scope>",
  username: "<customer_email>",
  password: "<customer_password>",
})

/**
 * After customer login, set the customer token (+ optional refreshToken)
 */
await salesChannel.setCustomer({
  accessToken: customerAuth.accessToken,
  refreshToken: customerAuth.refreshToken, // optional for "remember me"
  scope: customerAuth.scope,
})

/**
 * After setting the customer, you'll get a customer token
 */
const authorization2 = await salesChannel.getAuthorization()

console.log("Customer access token:", authorization2.accessToken)

/**
 * Logout (revokes and clears storage for customer)
 */
await salesChannel.logoutCustomer()

/**
 * After logout, you'll get the previous guest token or a new one if expired
 */
const authorization3 = await salesChannel.getAuthorization()

console.log("Guest access token after logout:", authorization3.accessToken)

As you can see, whenever you'll need an access token, you'll simply call getAuthorization().

Behind the scenes, this method checks for a valid customer token in storage first, then falls back to a guest token. If neither exists or both are expired, it automatically requests a new guest token.

Integration token with composite storage

Backend jobs, webhooks, and admin tasks rely on integration tokens, so caching matters. A composite storage setup lets you fall back gracefully between fast memory and persistent Redis:

import {
  createCompositeStorage,
  makeIntegration,
} from "@commercelayer/js-auth"

/**
 * The `Storage` interface is fully-compatible with the `unstorage` library.
 */

import { createStorage } from "unstorage"
import memoryDriver from "unstorage/drivers/memory"
import redisDriver from "unstorage/drivers/redis"

const memoryStorage = createStorage({
  driver: memoryDriver(),
})

const redisStorage = createStorage({
  driver: redisDriver({
    url: "<your_redis_connection_string>",
  }),
})

const compositeStorage = createCompositeStorage([
  memoryStorage,
  redisStorage,
])

const integration = makeIntegration(
  {
    clientId: "<your_client_id>",
    clientSecret: "<your_client_secret>",
    debug: true,
  },
  {
    storage: compositeStorage,
  },
)

/**
 * If you already requested an access token before,
 * now you'll probably get it from memory or Redis if not expired.
 * Otherwise, a new one will be requested.
 */
const authorization1 = await integration.getAuthorization()

console.log("Integration access token #1:", authorization1.accessToken)

/**
 * Subsequent calls will return the cached token from memory storage.
 */
const authorization2 = await integration.getAuthorization()

console.log("Integration access token #2:", authorization2.accessToken)

/**
 * Revoke the current integration authorization.
 * This will remove the authorization from memory and storage, and revoke the access token.
 */
await integration.revokeAuthorization()

Conclusion

Understanding authentication in Commerce Layer starts with recognizing how each credential defines access, who the request represents, and what actions it can perform. Whether it is a guest browsing a storefront, a logged-in customer, or a backend integration managing data, every token carries a specific purpose and scope.

By managing token lifespans carefully and caching tokens efficiently, you can keep your applications both secure and performant. The new API‑credentials-based model in Commerce Layer JS Auth v7 abstracts much of this complexity, giving you a robust, production‑ready authentication workflow out of the box.

If your project still uses v6, plan to upgrade to v7 to benefit from these improvements. The latest version is designed to reflect how teams actually cache tokens in production environments. With this new update, you can focus less on token handling and more on building seamless, secure experiences.

Key takeaways

  • Integration tokens give you full access to everything, while sales channel tokens are scoped to a specific market. Choose wisely: if you're building a storefront, use a sales channel token so customers only see what's relevant to them (prices in their currency, available SKUs in their region).

    When a customer logs in, authenticate with their username and password to get a customer token. This unlocks personalized data like order history, saved addresses, and payment methods.

  • Authentication requests are rate-limited, making caching essential. The examples above demonstrate how to implement caching with the new API credentials approach. Use cookies for browsers, Redis or KV stores for servers, and the composite storage pattern for multi-layer caching.

    Our JS Auth library handles the complexity: getAuthorization() checks the cache first and only requests a new token when necessary.

  • Tokens can be revoked before expiration (for example, to roll back a configuration mistake).

    We don't have an endpoint to check whether the token has been revoked or not. When you consume the token, you'll know if the token is valid and if it can be used against that endpoint; otherwise, you'll get an INVALID_TOKEN error.

    Handle this gracefully by catching the exception, removing the invalid token from storage, and requesting a fresh one. This ensures your application recovers automatically from revoked tokens.

  • Setting integration tokens to last months or years might seem convenient, but it creates significant security risks. Stick with short lifespans (2-4 hours) and automatic refresh mechanisms. With v7's storage helpers, token refresh is transparent. You never have to think about it.

  • If you haven't implemented Single Sign-On (SSO) yet, it's worth exploring. SSO simplifies customer authentication, improves security, and provides a better user experience. About that, check out our guide on how to implement SSO using Next.js and Auth0 to get started.