The CommerceEngine SDK provides two sophisticated approaches to token management, designed to handle everything from simple prototyping to production-scale applications with automatic token refresh and persistence.

Overview

Manual Token Management

Simple approach where you handle token storage and refresh logic yourself

Automatic Token Management

Production-ready approach with automatic token refresh, persistence, and cleanup

Manual Token Management

Perfect for prototyping, testing, or when you need full control over token handling.

Basic Setup

import StorefrontSDK from "@commercengine/storefront-sdk";

const sdk = new StorefrontSDK({
  storeId: "your-store-id",
  apiKey: "your-api-key",
  // No tokenStorage provided = manual mode
});

Authentication Flow

1

Get anonymous token

const { data, error } = await sdk.auth.getAnonymousToken();

if (data) {
  // Manually set tokens
  await sdk.setTokens(data.access_token, data.refresh_token);
}
2

Login with credentials

const { data: loginData } = await sdk.auth.loginWithPassword({
  email: "[email protected]",
  password: "password"
});

if (loginData) {
  // Update tokens after login
  await sdk.setTokens(loginData.access_token, loginData.refresh_token);
}
3

Handle token refresh manually

// Check if user is logged in
const isLoggedIn = await sdk.isLoggedIn();

if (!isLoggedIn) {
  // Token might be expired, try to refresh
  const refreshToken = getStoredRefreshToken(); // Your storage logic
  
  const { data: refreshData } = await sdk.auth.refreshToken({
    refresh_token: refreshToken
  });
  
  if (refreshData) {
    await sdk.setTokens(refreshData.access_token, refreshData.refresh_token);
  }
}

Manual Mode Methods

// Set tokens for all clients
await sdk.setTokens(accessToken, refreshToken);

// Clear all tokens
await sdk.clearTokens();

// Check authentication status
const isLoggedIn = await sdk.isLoggedIn();
const isAnonymous = await sdk.isAnonymous();

// Get user information
const userInfo = await sdk.getUserInfo();
const userId = await sdk.getUserId();
const customerId = await sdk.getCustomerId();

Automatic Token Management

Recommended for production applications. Handles token refresh, storage, and cleanup automatically.

Token Storage Options

Browser localStorage Storage

import StorefrontSDK, { BrowserTokenStorage } from "@commercengine/storefront-sdk";

const sdk = new StorefrontSDK({
  storeId: "your-store-id",
  apiKey: "your-api-key",
  tokenStorage: new BrowserTokenStorage("myapp_") // Optional prefix
});

Features:

  • βœ… Persists across browser sessions
  • βœ… Automatic token refresh
  • βœ… Cross-tab synchronization
  • βœ… Works with SPA frameworks (React, Vue, Angular)
import { CookieTokenStorage } from "@commercengine/storefront-sdk";

const sdk = new StorefrontSDK({
  storeId: "your-store-id",
  apiKey: "your-api-key",
  tokenStorage: new CookieTokenStorage({
    prefix: "myapp_",
    maxAge: 7 * 24 * 60 * 60, // 7 days
    secure: true,
    sameSite: "Lax"
  })
});

Features:

  • βœ… SSR/SSG compatible
  • βœ… Server and client access
  • βœ… Cross-subdomain sharing
  • βœ… Automatic expiry handling

Memory Storage (Server-Side)

import { MemoryTokenStorage } from "@commercengine/storefront-sdk";

const sdk = new StorefrontSDK({
  storeId: "your-store-id",
  apiKey: "your-api-key",
  tokenStorage: new MemoryTokenStorage()
});

// Perfect for:
// - Server-side API routes
// - Background jobs
// - Testing environments

Features:

  • βœ… Fast in-memory access
  • βœ… No persistence overhead
  • βœ… Perfect for server environments
  • βœ… Automatic cleanup on process end

Custom Token Storage

Implement the TokenStorage interface for custom storage solutions:

import { TokenStorage } from "@commercengine/storefront-sdk";

class RedisTokenStorage implements TokenStorage {
  constructor(private redisClient: RedisClient, private userId: string) {}

  async getAccessToken(): Promise<string | null> {
    return await this.redisClient.get(`user:${this.userId}:access_token`);
  }

  async setAccessToken(token: string): Promise<void> {
    await this.redisClient.setex(`user:${this.userId}:access_token`, 3600, token);
  }

  async getRefreshToken(): Promise<string | null> {
    return await this.redisClient.get(`user:${this.userId}:refresh_token`);
  }

  async setRefreshToken(token: string): Promise<void> {
    await this.redisClient.setex(`user:${this.userId}:refresh_token`, 86400 * 7, token);
  }

  async clearTokens(): Promise<void> {
    await this.redisClient.del([
      `user:${this.userId}:access_token`,
      `user:${this.userId}:refresh_token`
    ]);
  }
}

// Use custom storage
const sdk = new StorefrontSDK({
  storeId: "your-store-id",
  apiKey: "your-api-key",
  tokenStorage: new RedisTokenStorage(redisClient, userId)
});

Authentication Patterns

Anonymous to Authenticated Flow

import StorefrontSDK, { BrowserTokenStorage } from "@commercengine/storefront-sdk";

const sdk = new StorefrontSDK({
  storeId: "your-store-id",
  apiKey: "your-api-key",
  tokenStorage: new BrowserTokenStorage("myapp_")
});

async function authenticateUser() {
  // 1. Start with anonymous authentication
  const { data: anonData } = await sdk.auth.getAnonymousToken();
  
  // 2. User browses anonymously, adds items to cart
  await sdk.cart.createCart({
    items: [{ product_id: "product-123", quantity: 1 }]
  });
  
  // 3. User decides to login
  const { data: loginData } = await sdk.auth.loginWithPassword({
    email: "[email protected]", 
    password: "password"
  });
  
  // 4. SDK automatically updates tokens and maintains cart
  // No manual token management required!
}

OTP Authentication Flow

async function otpLogin(phone: string) {
  // 1. Initiate OTP login
  const { data: otpData } = await sdk.auth.loginWithPhone({
    phone: phone,
    country_code: "+1",
    register_if_not_exists: true
  });

  if (!otpData) return;

  // 2. User enters OTP (from your UI)
  const otp = await getUserOTPInput(); // Your UI logic

  // 3. Verify OTP
  const { data: authData } = await sdk.auth.verifyOtp({
    otp: otp,
    otp_token: otpData.otp_token,
    otp_action: otpData.otp_action
  });

  // 4. Tokens are automatically managed
  if (authData) {
    console.log("User authenticated successfully!");
  }
}

Multi-Environment Token Management

// Development/Staging/Production configuration
const getSDKConfig = () => {
  const isProduction = process.env.NODE_ENV === "production";
  
  return {
    storeId: process.env.NEXT_PUBLIC_STORE_ID!,
    environment: isProduction ? Environment.Production : Environment.Staging,
    apiKey: process.env.NEXT_PUBLIC_API_KEY!,
    
    tokenStorage: new CookieTokenStorage({
      prefix: isProduction ? "prod_" : "dev_",
      secure: isProduction,
      domain: isProduction ? ".mysite.com" : undefined,
      maxAge: isProduction ? 7 * 24 * 60 * 60 : 24 * 60 * 60 // 7 days prod, 1 day dev
    }),
    
    debug: !isProduction,
    
    onTokensUpdated: (accessToken, refreshToken) => {
      // Analytics or logging
      if (isProduction) {
        analytics.track("user_authenticated");
      }
    }
  };
};

const sdk = new StorefrontSDK(getSDKConfig());

Token Lifecycle Events

Handling Token Updates

const sdk = new StorefrontSDK({
  storeId: "your-store-id",
  apiKey: "your-api-key",
  tokenStorage: new BrowserTokenStorage(),
  
  onTokensUpdated: (accessToken, refreshToken) => {
    // Called when:
    // - User logs in
    // - Tokens are refreshed automatically
    // - setTokens() is called manually
    
    console.log("Tokens updated");
    
    // Update your application state
    updateUserState({ authenticated: true });
    
    // Send to analytics
    analytics.identify(getUserIdFromToken(accessToken));
  },
  
  onTokensCleared: () => {
    // Called when:
    // - User logs out
    // - Tokens are invalid and can't be refreshed
    // - clearTokens() is called manually
    
    console.log("Tokens cleared");
    
    // Update your application state
    updateUserState({ authenticated: false });
    
    // Redirect to login
    router.push("/login");
  }
});

JWT Utilities

The SDK provides utilities to extract information from tokens:

// Get user information from current token
const userInfo = await sdk.getUserInfo();
// Returns: { userId: string, email?: string, customerId?: string, ... }

// Check authentication status
const isLoggedIn = await sdk.isLoggedIn();     // true if authenticated user
const isAnonymous = await sdk.isAnonymous();   // true if anonymous token

// Get specific user identifiers
const userId = await sdk.getUserId();
const customerId = await sdk.getCustomerId();
const customerGroupId = await sdk.getCustomerGroupId();

Best Practices

βœ… Production Recommendations

Always use automatic token management in production with appropriate storage for your environment.

// βœ… GOOD: Production setup
const sdk = new StorefrontSDK({
  storeId: process.env.NEXT_PUBLIC_STORE_ID!,
  environment: Environment.Production,
  apiKey: process.env.NEXT_PUBLIC_API_KEY!,
  tokenStorage: new CookieTokenStorage({
    prefix: "myapp_",
    secure: true,
    sameSite: "Lax"
  }),
  onTokensUpdated: handleAuthSuccess,
  onTokensCleared: handleAuthFailure
});

// ❌ BAD: Manual management in production
const sdk = new StorefrontSDK({
  storeId: "store-id",
  apiKey: "api-key",
  // No tokenStorage = manual management
});

Security Best Practices

  1. Use secure cookies in production:
const tokenStorage = new CookieTokenStorage({
  secure: true,      // HTTPS only
  sameSite: "Lax",   // CSRF protection
  httpOnly: false    // Required for client access
});
  1. Environment-specific configuration:
const isProduction = process.env.NODE_ENV === "production";

const sdk = new StorefrontSDK({
  // ... other config
  tokenStorage: new CookieTokenStorage({
    secure: isProduction,
    domain: isProduction ? ".mysite.com" : undefined
  })
});
  1. Handle token events properly:
const sdk = new StorefrontSDK({
  // ... config
  onTokensCleared: () => {
    // Clear sensitive data
    clearUserCache();
    clearCartData();
    
    // Redirect to safe page
    router.push("/");
  }
});

Framework Integration

For framework-specific token management patterns and implementation details, see our dedicated integration guides:

Token Management Summary: The SDK’s automatic token management handles all the complexity of authentication, token refresh, and storage, allowing you to focus on building your application features while ensuring a seamless user experience.