@commercelayer/js-auth

中文文档
8.1.0发布于 2026/11/02MITTypeScriptESM + CJS零依赖构建来源已验证近 30 天下载 3.1万

A JavaScript library designed to simplify authentication when interacting with the Commerce Layer API.

原始使用文档
文档目录21 节
  1. What is Commerce Layer?
  2. Table of contents
  3. Getting started
  4. Installation
  5. API credentials
  6. Storage strategy
  7. Sales channel
  8. Integration
  9. Other flows
  10. Webapp application with authorization code flow
  11. Provisioning application
  12. Client credentials flow
  13. Utilities
  14. Revoking a token
  15. Decoding an access token
  16. Verifying an access token
  17. Getting the Core API base endpoint
  18. Getting the Provisioning API base endpoint
  19. Contributors guide
  20. Need help?
  21. License

Commerce Layer JS Auth

A lightweight JavaScript library designed to simplify authentication when interacting with the Commerce Layer API. It provides a robust token caching system out-of-the-box, with support for configurable persistent storage, composite storage strategies, and dedicated customer token management.

It works everywhere — on your browser, server, or at the edge.

What is Commerce Layer?

Commerce Layer is a multi-market commerce API and order management system that lets you add global shopping capabilities to any website, mobile app, chatbot, wearable, voice, or IoT device, with ease. Compose your stack with the best-of-breed tools you already mastered and love. Make any experience shoppable, anywhere, through a blazing-fast, enterprise-grade, and secure API.

Table of contents


Getting started

To get started with Commerce Layer JS Auth, you need to install it and add it to your project.

NPM JSR Lightway     Discord

Installation

Commerce Layer JS Auth is available as an npm package.

# npm
npm install @commercelayer/js-auth

# yarn
yarn add @commercelayer/js-auth

# pnpm
pnpm add @commercelayer/js-auth

# deno
deno add jsr:@commercelayer/js-auth

# bun
bun add @commercelayer/js-auth

API credentials

Commerce Layer implements the industry-standard OAuth 2.0 protocol to manage clients' authorization. It defines different types of API credentials. Which one to use depends on your specific needs.

[!NOTE] Check our API credentials documentation for further information for each credential type and learn how to create them through the Commerce Layer dashboard.

To use Commerce Layer API you need to be authorized in the first place. This means you need to get a valid access token.

flowchart TB
  %% Default style for nodes
  classDef node stroke-width:2px;

  %% Main nodes
  auth("<b>Auth API</b><br/><br/>https://<b>auth</b>.commercelayer.io")

  dashboard("JWT Dashboard")
  user("JWT User")
  sales_channel("JWT Sales Channel")
  integration("JWT Integration")
  webapp("JWT WebApp")

  provisioningAPI("<b>Provisioning API</b><br/><br/>https://<b>provisioning</b>.commercelayer.io")
  coreAPI("<b>Core API</b><br/><br/>https://#lt;<b>slug</b>#gt;.commercelayer.io")
  metricsAPI("<b>Metrics API</b><br/><br/>https://#lt;<b>slug</b>#gt;.commercelayer.io/metrics")
  comingSoon("<b>Metrics API</b> (coming soon)<br/><br/>https://<b>metrics</b>.commercelayer.io")

  %% Node styles
  style dashboard fill:#FFE6CC,stroke:#D79B00,color:#000
  style user fill:#F8CECC,stroke:#B85450,color:#000
  style sales_channel fill:#D5E8D4,stroke:#82B366,color:#000
  style integration fill:#DAE8FC,stroke:#6C8EBF,color:#000
  style webapp fill:#E1D5E7,stroke:#9673A6,color:#000
  style comingSoon opacity:.3

  %% Connections
  auth --> dashboard
  auth --> user
  auth --> sales_channel
  auth --> integration
  auth --> webapp

  dashboard --> provisioningAPI
  user --> provisioningAPI
  user --> comingSoon
  sales_channel --> coreAPI
  integration --> coreAPI
  integration --> metricsAPI
  webapp --> coreAPI
  webapp --> metricsAPI

  %% Arrow Styles
  linkStyle default stroke-width:2px;
  linkStyle 5 stroke:#D79B00
  linkStyle 6 stroke:#B85450
  linkStyle 7 stroke:#B85450,stroke-dasharray: 5 5,opacity:.3
  linkStyle 8 stroke:#82B366
  linkStyle 9 stroke:#6C8EBF
  linkStyle 10 stroke:#6C8EBF
  linkStyle 11 stroke:#9673A6
  linkStyle 12 stroke:#9673A6

Authentication calls are subject to rate limiting. To avoid hitting these limits, you should cache the authentication token in a storage (e.g., cookies, Redis, KV). This prevents requesting a new token for each API call.

This library provides a robust token caching system out-of-the-box, with support for any storage solution you choose. It includes tools for composing multiple storage mechanisms (e.g., memory + Redis) to reduce load on the underlying configured storage, and provides dedicated customer token management.

[!NOTE] For advanced use cases where you need direct control over token management, you can bypass the built-in caching helpers (makeSalesChannel and makeIntegration) and use the authenticate method directly.

Storage strategy

  • Single storage — Provides temporary or persistent storage that can survive page reloads. You can implement any storage solution.
  • Composite storage — Using the createCompositeStorage helper, you can combine multiple storage mechanisms (e.g., memory + Redis) to optimize performance and reduce load on the underlying configured storage.
  • Customer storage (sales channel only) — Optional dedicated storage for customer authentication tokens, separate from guest tokens.

Here below an example showing a basic setup with an in-memory storage:

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

/**
 * A valid storage must implement the `Storage` interface
 */
function memoryStorage(): Storage {
  const store = new Map<string, StorageValue>()
  return {
    name: "in-memory",
    async getItem(key) {
      return store.get(key) ?? null
    },
    async setItem(key: string, value: StorageValue) {
      store.set(key, value)
    },
    async removeItem(key: string) {
      store.delete(key)
    },
  }
}

const salesChannel = makeSalesChannel(
  {
    clientId: "<your_client_id>",
    scope: "market:code:europe",
    debug: {
      logLevel: "info"
    },
  },
  {
    storage: memoryStorage(),
  },
)

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

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

/**
 * For consecutive calls, you'll get the previous guest token
 * from the storage or a new one if expired
 */
const authorization2 = await salesChannel.getAuthorization()

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

Authorizations are stored using a key derived from the client ID and scope, where type is the authorization type (guest or customer):

cl_${type}-${clientId}-${scope}

If you need different keys (e.g. a custom prefix, or extra identifiers), you can provide your own getKey function:

const salesChannel = makeSalesChannel(
  {
    clientId: "<your_client_id>",
    scope: "market:code:europe"
  },
  {
    storage: memoryStorage(),
    getKey: async ({ clientId, scope }, type) =>
      `myprefix_${type}-${clientId}-${scope}`,
  },
)

The following flowchart illustrates how the library manages token caching, validation, and refresh flow:

flowchart TB
  %% Default style for nodes
  classDef node stroke-width:2px,color:#000;

  GetAuthorization(".getAuthorization()") --> CheckCustomerStorage{"Check <b>Customer</b><br/>in storage"}

  CheckCustomerStorage -->|Found| ValidateCustomerToken{"Is token<br/>still valid?"}
  CheckCustomerStorage -->|Not Found| CheckGuestStorage{"Check <b>Guest</b><br/>in storage"}

  ValidateCustomerToken -->|Yes| ReturnCustomerToken(["Return <b>Customer</b> Token"])
  ValidateCustomerToken -->|No| HasRefreshToken{"<b>Customer</b> has<br/>refresh token?"}

  HasRefreshToken -->|Yes| RefreshToken["Refresh <b>Customer</b> token"]
  HasRefreshToken -->|No| CheckGuestStorage

  RefreshToken --> StoreNewToken["Store new <b>Customer</b> token"]
  StoreNewToken --> ReturnCustomerToken

  %% CheckGuestMemory -->|Found| ValidateGuestToken{"Is token<br/>still valid?"}
  %% CheckGuestMemory -->|Not Found| CheckGuestStorage{"Check <b>Guest</b><br/>in storage"}

  ValidateGuestToken -->|Yes| ReturnGuestToken(["Return <b>Guest</b> token"])
  ValidateGuestToken -->|No| CreateNewGuest["Create new <b>Guest</b> token"]

  CheckGuestStorage -->|Found| ValidateGuestToken{"Is token<br/>still valid?"}
  CheckGuestStorage -->|Not Found| CreateNewGuest

  CreateNewGuest --> StoreNewGuestToken["Store new <b>Guest</b> token"]
  StoreNewGuestToken --> ReturnGuestToken

  classDef process fill:#DAE8FC,stroke:#6C8EBF
  classDef condition fill:#FFE6CC,stroke:#D79B00
  classDef code fill:#FFF,stroke:#000,font-family:monospace
  classDef startState fill:#FFF,stroke:#000
  classDef endState fill:#FFF,stroke:#000

  class GetAuthorization code;
  class Start startState;
  class CheckCustomerMemory,CheckCustomerStorage,CheckGuestMemory,CheckGuestStorage condition;
  class ValidateCustomerToken,ValidateGuestToken,HasRefreshToken condition;
  class RefreshToken,StoreCustomerTokenInMemory,CreateNewGuest,StoreNewToken,StoreNewGuestToken process;
  class ReturnCustomerToken,ReturnGuestToken endState;

Customer storage

Sales channels deal with two kinds of tokens: the guest token, which is not tied to any identity and can be safely shared, and the customer token, which is personal. The optional customerStorage option lets you store customer tokens separately from guest tokens. Whether customer tokens stay isolated depends on two things: the storage they are written to, and the key they are stored under.

In the browser, everything works out-of-the-box: the storage (e.g. localStorage) is already scoped to a single visitor.

[!WARNING] The default key does not include any customer identity. If customer tokens are cached in a storage shared by all visitors (e.g. Redis or an in-memory map on the server) under the default key, one customer's token would be served to every visitor.

There are two ways to store customer tokens safely on the server:

  1. Use a per-visitor customerStorage. When the customer storage is scoped to the visitor (e.g. cookies), the default key is perfectly fine, because the storage itself belongs to a single visitor. This pairs naturally with server-side rendered storefronts, where a single guest token can be cached server-side and shared by all visitors:

    const salesChannel = makeSalesChannel(
      {
        clientId: "<your_client_id>",
        scope: "market:code:europe"
      },
      {
        // The guest token carries no identity:
        // one token, cached server-side, serves all visitors...
        storage: memoryStorage(),
        // ...while the customer token is personal
        // and lives in a per-visitor storage (e.g. cookies).
        customerStorage: cookieStorageAdapter,
      },
    )
    
  2. Use a shared storage with a custom key. If you'd rather cache customer tokens in a shared server-side storage too (e.g. Redis), use the getKey function to include a customer or session identifier in the key. Since getKey receives only the client ID, scope, and authorization type, the request context must be captured via closure. Create the helper per request, while keeping the storage backend shared:

    import {
      createCompositeStorage,
      makeSalesChannel,
    } from "@commercelayer/js-auth"
    
    // Shared, process-level storage (this is where tokens are actually cached).
    const sharedStorage = createCompositeStorage({
      name: "bff",
      storages: [memoryStorage(), redisStorage],
    })
    
    // Per-request helper: creating it is cheap (no network calls, no timers).
    function salesChannelFor(customerId: string) {
      return makeSalesChannel(
        {
          clientId: "<your_client_id>",
          scope: "market:code:europe"
        },
        {
          storage: sharedStorage,
          getKey: async ({ clientId, scope }, type) =>
            type === "customer"
              ? `cl_customer-${clientId}-${scope}-${customerId}`
              : `cl_guest-${clientId}-${scope}`,
        },
      )
    }
    

[!NOTE] The helpers returned by makeSalesChannel and makeIntegration are lightweight factories: they hold no token state themselves (tokens live in the configured storage) and perform no I/O at construction time. It's safe to create one per request, as long as the storage backend is shared. The only thing a long-lived instance adds is in-flight deduplication of concurrent getAuthorization() calls.

Debugging and storage names

You can enable debugging and assign custom names to your storage instances for better visibility into token operations:

  • debug — Accepts a DebugConfig object with two options:
    • logLevel: "info" — logs meaningful events only: cache misses, token refreshes, authorizations stored/removed, and errors. Routine cache hits are silent, so setups that call getAuthorization() on every API request don't flood the logs.
    • logLevel: "verbose" — also logs steady-state operations (every storage read and cache hit).
    • maskToken — whether to mask access tokens and refresh tokens in the log output (e.g. eyJh...A9Xz). Defaults to true. Set to false to see full tokens, e.g. for inspecting them at jwt.io.
    • logger — how each entry is written. Either "console" (the default) or a function such as jsonLogger. See Forwarding logs to a log collector.
  • name — A custom identifier for your storage instance, useful when using multiple storages or composite storage configurations. The name is an attribute of the storage itself (as shown in the memoryStorage example above).

The createCompositeStorage helper accepts its own debug option, following the same rule: a hit on the first (fastest) storage is steady state, while falling back to a slower storage is a meaningful event worth logging.

[!WARNING] When maskToken is set to false, full access tokens and refresh tokens are logged to the console. Intended for local development only — in serverless/edge environments, logs may be forwarded to external log aggregation services.

 const salesChannel = makeSalesChannel(
   {
     clientId: "<your_client_id>",
     scope: "market:code:europe",
+    debug: {
+      logLevel: "info"
+    },
   },
   {
     storage: {
+      name: "storage-name",
+      debug: {
+        logLevel: "info"
+      },
       async getItem(key) {
         // implementation
       },
       // ...
     }
   },
 )

Using unstorage

The Storage interface is fully compatible with the unstorage library. However, unstorage doesn't include a name property by default. To enable debugging and identify which storage is being used in your logs, wrap createStorage with this helper:

import type { Storage } from "@commercelayer/js-auth"
import type { CreateStorageOptions } from "unstorage"
import { createStorage as unstorageCreateStorage } from "unstorage"

// Helper to create storage with explicit naming support
function createStorage(
  options: CreateStorageOptions & { name?: string },
): Storage {
  return {
    name: options.name ?? options.driver?.name,
    ...unstorageCreateStorage(options),
  }
}
// Usage example
import redisDriver from "unstorage/drivers/redis"

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

When debugging is enabled, logs will include the storage name, making it easier to trace which storage layer is handling each operation.

Forwarding logs to a log collector

By default (logger: "console") each entry is written with console.log(message, ...data), so objects stay expandable in browser devtools and readable in a terminal. In Node.js, though, console.log prints objects across multiple lines, and log collectors that read stdout (Datadog, CloudWatch, Google Cloud Logging, Loki, …) ingest each of those lines as a separate log entry.

logger also accepts a function that receives each entry. The jsonLogger helper is one such function: it writes one single-line JSON object per entry. You can pick it per environment:

import { jsonLogger, makeSalesChannel } from "@commercelayer/js-auth"

const salesChannel = makeSalesChannel({
  clientId: "<your_client_id>",
  scope: "<your_scope>",
  debug: {
    logLevel: "info",
    logger: process.env.NODE_ENV === "production" ? jsonLogger : "console",
  },
})
{"name":"@commercelayer/js-auth","level":"info","source":"sales_channel","storageName":"memory","message":"Stored \"customer\" authorization to storage","data":[{"accessToken":"eyJh...A9Xz","scope":"market:all"}]}

Error values are expanded to { name, message, stack, cause } plus any custom properties, and an object that contains itself is replaced with "[Circular]", so logging never throws.

To route entries into your own logger (pino, winston, …), pass your own function:

import pino from "pino"

const log = pino()

const salesChannel = makeSalesChannel({
  clientId: "<your_client_id>",
  scope: "<your_scope>",
  debug: {
    logLevel: "info",
    logger: ({ level, message, ...entry }) => {
      log[level === "verbose" ? "debug" : "info"](entry, message)
    },
  },
})

Token masking is applied before the entry reaches the logger, so maskToken works with every logger. Errors thrown by a logger function are ignored, so a failing logger never breaks authentication.

Sales channel

Sales channels are used to build any customer touchpoint (e.g. your storefront with a fully-functional shopping cart and checkout flow).

Below is a complete example showing how to use a sales channel for both guest and customer authentication:

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

// The `Storage` interface is fully-compatible with the `unstorage` library.
import { createStorage } from "unstorage"
import localStorageDriver from "unstorage/drivers/localstorage"

const salesChannel = makeSalesChannel(
  {
    clientId: "<your_client_id>",
    scope: "market:code:europe",
  },
  {
    /**
     * You can use any storage implementation you prefer or implement your own.
     * In this example we use `unstorage` with the `localStorage` driver
     */
    storage: createStorage({
      driver: localStorageDriver({}),
    }),
  },
)

/**
 * Get the current authorization state which includes the access token.
 * This method handles caching and token refresh automatically.
 */
const guestAuthorization = await salesChannel.getAuthorization()

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

/**
 * Authenticate a customer using their email and password, or
 * though the JWT bearer flow.
 */
const customerCredentials = await authenticate("password", {
  clientId: "<your_client_id>",
  scope: "market:code:europe",
  username: "[email protected]",
  password: "secret",
})

/**
 * Set the customer credentials in the storage.
 */
await salesChannel.setCustomer({
  accessToken: customerCredentials.accessToken,
  scope: customerCredentials.scope,

  /**
   * When `refreshToken` is provided, it'll be used to automatically
   * refresh the customer access token when it expires.
   */
  refreshToken: customerCredentials.refreshToken,
})

/**
 * Get the current customer authorization.
 */
const customerAuthorization = await salesChannel.getAuthorization()

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

/**
 * Logout the current customer.
 * This will remove the customer authorization from the storage, and revoke the access token.
 */
await salesChannel.logoutCustomer()

/**
 * You can also clear stored authorizations without revoking the tokens,
 * e.g. to force a fresh authorization on the next `getAuthorization()` call.
 */
await salesChannel.removeAuthorization("all") // or "customer" / "guest"

Customer authentication is supported through two OAuth 2.0 grant types: password and JWT bearer. Both flows return an accessToken, scope, and refreshToken that can be stored using the setCustomer method.

Password-based customer authentication

Sales channels can use the password grant type to exchange customer credentials for an access token (i.e., to get a "logged" access token).

import { authenticate } from "@commercelayer/js-auth"

const auth = await authenticate("password", {
  clientId: "<your_client_id>",
  scope: "market:code:europe",
  username: "[email protected]",
  password: "secret"
})

console.log("My access token:", auth.accessToken)
console.log("Expiration date:", auth.expires)
console.log("My refresh token:", auth.refreshToken)

Sales channels can use the refresh token grant type to refresh a customer access token with a "remember me" option:

import { authenticate } from "@commercelayer/js-auth"

const newToken = await authenticate("refresh_token", {
  clientId: "<your_client_id>",
  scope: "market:code:europe",
  refreshToken: "<your_refresh_token>"
})

[!NOTE] When using the makeSalesChannel helper, token refresh is handled automatically. The helper monitors token expiration and seamlessly refreshes customer tokens in the background, eliminating the need for manual refresh calls.

JWT bearer authentication

Commerce Layer supports OAuth 2.0 JWT Bearer token exchange, enabling applications to obtain access tokens by exchanging JWT assertions. This is particularly useful for implementing delegated authentication flows, where an application needs to make API calls on behalf of a customer without requiring their direct interaction. The flow consists of two steps:

  1. Creating a signed JWT assertion containing the customer's claims

    import { createAssertion } from "@commercelayer/js-auth"
    
    const assertion = await createAssertion({
      payload: {
        "https://commercelayer.io/claims": {
          owner: {
            type: "Customer",
            id: "4tepftJsT2"
          },
          custom_claim: {
            customer: {
              first_name: "John",
              last_name: "Doe"
            }
          }
        }
      }
    })
    
  2. Exchanging this assertion for an access token

    import { authenticate } from "@commercelayer/js-auth"
    
    const auth = await authenticate("urn:ietf:params:oauth:grant-type:jwt-bearer", {
      clientId: "<your_client_id>",
      clientSecret: "<your_client_secret>",
      scope: "market:code:europe",
      assertion
    })
    
    console.log("My access token:", auth.accessToken)
    console.log("Expiration date:", auth.expires)
    console.log("My refresh token:", auth.refreshToken)
    

Both sales channels and webapps can use this JWT bearer flow to implement secure delegated authentication.

Delegated login from an external identity provider

When customers authenticate through an external identity provider (e.g. Okta, Auth0), your backend can exchange their identity for a Commerce Layer customer token using the JWT bearer flow, and hand it over to the salesChannel helper (since this flow runs server-side, see the Customer storage section for a suitable setup):

import {
  authenticate,
  createAssertion,
} from "@commercelayer/js-auth"

// 1. Validate the external identity (e.g. verify the IdP-issued JWT)
//    and resolve it to a Commerce Layer customer ID.
const customerId = await resolveCustomerId(idpToken)

// 2. Create the signed assertion and exchange it for a customer token.
//    This step requires the client secret, so it must run server-side.
const assertion = await createAssertion({
  payload: {
    "https://commercelayer.io/claims": {
      owner: {
        type: "Customer",
        id: customerId
      },
    },
  },
})

const customerCredentials = await authenticate("urn:ietf:params:oauth:grant-type:jwt-bearer", {
  clientId: "<your_client_id>",
  clientSecret: "<your_client_secret>",
  scope: "market:code:europe",
  assertion,
})

// 3. Hand the customer token over to the `salesChannel` helper
//    created with `makeSalesChannel`.
//    From now on, `getAuthorization()` returns the customer authorization
//    and refreshes it automatically when it expires.
await salesChannel.setCustomer({
  accessToken: customerCredentials.accessToken,
  scope: customerCredentials.scope,
  refreshToken: customerCredentials.refreshToken,
})

Integration

Integrations are used to develop backend integrations with any 3rd-party system.

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({
  name: "composite-storage",
  storages: [
    memoryStorage,
    redisStorage,
  ]
})

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

/**
 * If you already requested an access token before, now you'll probably get it from Redis if not expired.
 * Otherwise, a new one will be requested.
 * 
 * This method handles caching and token refresh automatically.
 */
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()

/**
 * Disposes all mounted storages to ensure there are no open-handles left.
 * Call it before exiting process.
 */
await compositeStorage.dispose?.()

Other flows

Webapp application with authorization code flow

Available only for browser applications

Webapps use the authorization code grant type to exchange an authorization code for an access token.

Steps

In this case, first, you need to get an authorization code, then you can exchange it with an access token:

  1. Create a webapp on Commerce Layer and take note of the API credentials (client ID, client secret, callback URL, base endpoint, and the ID of the market you want to put in scope)

  2. Use this URL to authorize your webapp on Commerce Layer:

https://dashboard.commercelayer.io/oauth/authorize?client_id={{your_client_id}}&redirect_uri={{your_redirect_uri}}&scope=market:id:xYZkjABcde&response_type=code&state=1a2b3c
  1. Once you've authorized the webapp, you will be redirected to the callback URL:

    Callback URL with Authorization Code

    Use this code to get the access token:

import { authenticate } from "@commercelayer/js-auth"

const auth = await authenticate("authorization_code", {
  clientId: "<your_client_id>",
  clientSecret: "<your_client_secret>",
  callbackUrl: "<https://yourdomain.com/callback>",
  code: "<your_auth_code>"
})

console.log("My access token: ", auth.accessToken)
console.log("Expiration date: ", auth.expires)

Provisioning application

Provisioning applications use the client credentials grant type to get an access token.

Steps

  1. Access your personal provisioning application on Commerce Layer dashboard and take note of your Provisioning API credentials (client ID, client secret)

  2. Use this code to get the access token:

import { authenticate } from "@commercelayer/js-auth"

const auth = await authenticate("client_credentials", {
  clientId: "<your_client_id>",
  clientSecret: "<your_client_secret>"
})

console.log("My access token: ", auth.accessToken)
console.log("Expiration date: ", auth.expires)

Client credentials flow

For advanced use cases where you need direct control over token management, you can bypass the built-in caching helpers (makeSalesChannel and makeIntegration) and use the authenticate method directly.

[!WARNING] Note that, in this case, you'll be responsible for implementing your own token caching and refresh logic.

Sales channel applications use the client credentials grant type to get a "guest" access token.

import { authenticate } from "@commercelayer/js-auth"

const auth = await authenticate("client_credentials", {
  clientId: "<your_client_id>",
  scope: "market:code:europe"
})

console.log("My access token: ", auth.accessToken)
console.log("Expiration date: ", auth.expires)

Integration applications use the client credentials grant type to get an access token for themselves.

import { authenticate } from "@commercelayer/js-auth"

const auth = await authenticate("client_credentials", {
  clientId: "<your_client_id>",
  clientSecret: "<your_client_secret>",
})

console.log("My access token: ", auth.accessToken)
console.log("Expiration date: ", auth.expires)

Utilities

Revoking a token

Any previously generated access tokens (refresh tokens included) can be revoked before their natural expiration date:

import { revoke } from "@commercelayer/js-auth"

await revoke({
  clientId: "<your_client_id>",
  clientSecret: "<your_client_secret>",
  token: "<a_generated_access_token>"
})

Decoding an access token

We offer a helper method to decode an access token. The return is fully typed.

[!IMPORTANT] You should not use this for untrusted messages, since this helper method does not verify whether the signature is valid. If you need to verify the access token before decoding, you can use jwtVerify instead.

import { authenticate, jwtDecode, jwtIsSalesChannel } from "@commercelayer/js-auth"

const auth = await authenticate("client_credentials", {
  clientId: "<your_client_id>",
  scope: "market:code:europe"
})

const decodedJWT = jwtDecode(auth.accessToken)

if (jwtIsSalesChannel(decodedJWT.payload)) {
  console.log("organization slug is", decodedJWT.payload.organization.slug)
}

Verifying an access token

We offer an helper method to verify an access token.

It validates the integrity and authenticity of the JWT. It checks if the token is valid by verifying the signature against the public key used to create it.

This is useful to ensure that the token hasn't been tampered with and originates from Commerce Layer.

The return is fully typed:

import { authenticate, jwtVerify, jwtIsSalesChannel } from "@commercelayer/js-auth"

const auth = await authenticate("client_credentials", {
  clientId: "<your_client_id>",
  scope: "market:code:europe"
})

const decodedJWT = await jwtVerify(auth.accessToken, {
  ignoreExpiration: true
})

if (jwtIsSalesChannel(decodedJWT.payload)) {
  console.log("organization slug is", decodedJWT.payload.organization.slug)
}

Getting the Core API base endpoint

Derive the Core API base endpoint given a valid access token.

import { getCoreApiBaseEndpoint } from "@commercelayer/js-auth"

getCoreApiBaseEndpoint("<a_valid_access_token>") //= "https://yourdomain.commercelayer.io"

The method requires a valid access token with an organization in the payload. When the organization is not set (e.g., provisioning token), it throws an InvalidTokenError exception.

Getting the Provisioning API base endpoint

It returns the Provisioning API base endpoint given a valid access token.

import { getProvisioningApiBaseEndpoint } from "@commercelayer/js-auth"

getProvisioningApiBaseEndpoint("<a_valid_access_token>") //= "https://provisioning.commercelayer.io"

The method requires a valid access token (the token can be used with Provisioning API). When the token is not valid (e.g., core api token), it throws an InvalidTokenError exception.


Contributors guide

  1. Fork this repository (learn how to do this here).

  2. Clone the forked repository like so:

git clone https://github.com/<your username>/commercelayer-js-auth.git && cd commercelayer-js-auth
  1. Make your changes and create a pull request (learn how to do this).

  2. Someone will attend to your pull request and provide some feedback.

Need help?

License

This repository is published under the MIT license.

版本与发布记录

@commercelayer/js-auth 共发布 71 个版本,首次发布于 2019/11/29;过去 12 个月发布了 11 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
8.1.0latest2026/11/028 天前
8.0.22026/10/1328 天前
8.0.12026/08/212 个月前
8.0.02026/07/153 个月前
7.4.22026/05/205 个月前
7.4.12026/04/156 个月前

其他发布通道:next → 6.0.0-beta.2。

关于 @commercelayer/js-auth 的常见问题

@commercelayer/js-auth 是什么?

A JavaScript library designed to simplify authentication when interacting with the Commerce Layer API.

如何安装 @commercelayer/js-auth?

在项目目录运行 npm install @commercelayer/js-auth,或使用 pnpm add / yarn add / bun add。

@commercelayer/js-auth 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@commercelayer/js-auth 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@commercelayer/js-auth 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@commercelayer/js-auth 可以商用吗?

@commercelayer/js-auth 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@commercelayer/js-auth 的下载量有多少?

根据 npm 官方下载统计,@commercelayer/js-auth 在 2026/10/11 – 2026/11/09 这 30 天内被下载 31,045 次,日均约 1,035 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@commercelayer/js-auth 还在维护吗?

最新版本 8.1.0 发布于 2026/11/02(8 天前);至今共发布 71 个版本,其中过去 12 个月发布了 11 个;首次发布于 2019/11/29。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。