ce-auth

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
LLM Docs Header: All requests to
https://llm-docs.commercengine.io
must include the
Accept: text/markdown
header (or append
.md
to the URL path). Without it, responses return HTML instead of parseable markdown.
LLM Docs Header: 所有发送至
https://llm-docs.commercengine.io
的请求必须包含
Accept: text/markdown
请求头(或在URL路径后追加
.md
)。如果没有该请求头,响应将返回HTML而非可解析的markdown。

Authentication & User Management

认证与用户管理

Prerequisite: SDK must be initialized. See
setup/
if not done.
前置条件:SDK必须已初始化。如果未完成,请查看
setup/
文档。

When to Implement Auth Directly

何时需要直接实现认证

If using Hosted Checkout (recommended): Login/registration is handled inside the checkout drawer automatically. You do not need to build login UI or call the login/OTP endpoints below unless your storefront has features that require a logged-in user outside of placing orders — for example, a saved addresses page, order history, wishlists, or a loyalty dashboard.
Anonymous auth (
getAnonymousToken
) is still always required — it's handled by
StorefrontSDKInitializer
in Next.js or manually in other frameworks (see
setup/
).
When the user logs in via Hosted Checkout, tokens are synced back to the SDK automatically (via
onTokensUpdated
), so the rest of your app can use
getUserDetails()
,
listOrders()
, etc. without any additional auth work.
Build your own login UI only when:
  • Your storefront has account pages (profile, addresses, orders) accessible before checkout
  • You want a persistent login state in the header/nav (e.g., "Hi, Jane" with user menu)
  • You need auth-gated pages that don't involve the checkout flow
如果使用托管结账(推荐): 登录/注册会在结账抽屉内自动处理。除非你的店铺有在下单流程之外需要登录用户的功能(例如,已保存地址页面、订单历史、心愿单或忠诚度仪表盘),否则你无需构建登录UI或调用下方的登录/OTP接口。
匿名认证(
getAnonymousToken
)仍然是必须的——在Next.js中由
StorefrontSDKInitializer
自动处理,在其他框架中则需手动调用(详见
setup/
文档)。
当用户通过托管结账登录时,令牌会自动同步回SDK(通过
onTokensUpdated
),因此应用的其他部分无需额外的认证操作即可使用
getUserDetails()
listOrders()
等方法。
仅在以下场景下自行构建登录UI:
  • 你的店铺在结账前有可访问的账户页面(资料、地址、订单)
  • 你希望在头部/导航栏中保持持久登录状态(例如,"你好,Jane"及用户菜单)
  • 你需要不涉及结账流程的认证 gated 页面

Quick Reference

快速参考

Auth MethodEndpointUse Case
Anonymous
sdk.auth.getAnonymousToken()
Every new visitor, required first step
Email OTP
sdk.auth.loginWithEmail()
sdk.auth.verifyOtp()
Passwordless email login
Phone OTP
sdk.auth.loginWithPhone()
sdk.auth.verifyOtp()
Passwordless phone login
WhatsApp OTP
sdk.auth.loginWithWhatsApp()
sdk.auth.verifyOtp()
Passwordless WhatsApp login
Password
sdk.auth.loginWithPassword()
Traditional email/password login
Token Refresh
sdk.auth.refreshToken()
Renew expired access token
认证方式接口使用场景
匿名认证
sdk.auth.getAnonymousToken()
所有新访客,必须的第一步
邮箱OTP
sdk.auth.loginWithEmail()
sdk.auth.verifyOtp()
无密码邮箱登录
手机OTP
sdk.auth.loginWithPhone()
sdk.auth.verifyOtp()
无密码手机登录
WhatsApp OTP
sdk.auth.loginWithWhatsApp()
sdk.auth.verifyOtp()
无密码WhatsApp登录
密码认证
sdk.auth.loginWithPassword()
传统邮箱/密码登录
令牌刷新
sdk.auth.refreshToken()
续期过期的访问令牌

Decision Tree

决策树

User Request
    ├─ New visitor / first load
    │   └─ sdk.auth.getAnonymousToken()
    ├─ "Login" / "Sign in"
    │   ├─ Passwordless (recommended)
    │   │   ├─ Email → loginWithEmail() → verifyOtp()
    │   │   ├─ Phone → loginWithPhone() → verifyOtp()
    │   │   └─ WhatsApp → loginWithWhatsApp() → verifyOtp()
    │   └─ Password → loginWithPassword()
    ├─ "User profile" / "Account"
    │   └─ sdk.auth.getUserDetails() / sdk.auth.updateUserDetails()
    └─ "Token expired" / 401 error
        └─ sdk.auth.refreshToken()
用户请求
    ├─ 新访客 / 首次加载
    │   └─ sdk.auth.getAnonymousToken()
    ├─ "登录" / "Sign in"
    │   ├─ 无密码登录(推荐)
    │   │   ├─ 邮箱 → loginWithEmail() → verifyOtp()
    │   │   ├─ 手机 → loginWithPhone() → verifyOtp()
    │   │   └─ WhatsApp → loginWithWhatsApp() → verifyOtp()
    │   └─ 密码登录 → loginWithPassword()
    ├─ "用户资料" / "账户"
    │   └─ sdk.auth.getUserDetails() / sdk.auth.updateUserDetails()
    └─ "令牌过期" / 401错误
        └─ sdk.auth.refreshToken()

User States

用户状态

StateHow CreatedCapabilities
Anonymous
POST /auth/anonymous
with API key
Browse catalog, manage cart, analytics tracking
Logged-inOTP verification or password loginAll anonymous + orders, addresses, profile, loyalty
状态创建方式权限
匿名状态使用API密钥调用
POST /auth/anonymous
浏览商品目录、管理购物车、分析追踪
已登录状态OTP验证或密码登录拥有匿名状态的所有权限 + 查看订单、管理地址、编辑资料、忠诚度功能

Key Patterns

核心模式

Anonymous Auth (Required First Step)

匿名认证(必须的第一步)

typescript
const { data, error } = await sdk.auth.getAnonymousToken();
// Tokens are automatically stored and managed
// Now the user can browse products, add to cart, etc.
typescript
const { data, error } = await sdk.auth.getAnonymousToken();
// 令牌会被自动存储和管理
// 现在用户可以浏览商品、添加到购物车等

OTP Login Flow (Email)

OTP登录流程(邮箱)

typescript
// 1. Initiate login (also registers new users automatically)
const { data, error } = await sdk.auth.loginWithEmail({
  email: "user@example.com",
  register_if_not_exists: true,  // Seamless login + registration
});

if (error) return handleError(error);

const { otp_token, otp_action } = data;

// 2. User enters OTP from their email...

// 3. Verify OTP
const { data: authData, error: verifyError } = await sdk.auth.verifyOtp({
  otp: "123456",           // From user input
  otp_token: otp_token,    // From step 1
  otp_action: otp_action,  // From step 1
});

// Tokens are automatically updated — user is now logged in
typescript
// 1. 发起登录(同时自动注册新用户)
const { data, error } = await sdk.auth.loginWithEmail({
  email: "user@example.com",
  register_if_not_exists: true,  // 无缝登录 + 注册
});

if (error) return handleError(error);

const { otp_token, otp_action } = data;

// 2. 用户输入邮箱中的OTP...

// 3. 验证OTP
const { data: authData, error: verifyError } = await sdk.auth.verifyOtp({
  otp: "123456",           // 用户输入的OTP
  otp_token: otp_token,    // 来自步骤1的返回值
  otp_action: otp_action,  // 来自步骤1的返回值
});

// 令牌会自动更新 — 用户现在已登录

OTP Login Flow (Phone)

OTP登录流程(手机)

typescript
const { data, error } = await sdk.auth.loginWithPhone({
  phone: "9876543210",
  country_code: "+91",
  register_if_not_exists: true,
});

// Then verify with sdk.auth.verifyOtp() same as email flow
typescript
const { data, error } = await sdk.auth.loginWithPhone({
  phone: "9876543210",
  country_code: "+91",
  register_if_not_exists: true,
});

// 之后与邮箱流程相同,调用sdk.auth.verifyOtp()验证

Password Login

密码登录

typescript
const { data, error } = await sdk.auth.loginWithPassword({
  email: "user@example.com",
  password: "securepassword",
});

// Tokens automatically managed on success
typescript
const { data, error } = await sdk.auth.loginWithPassword({
  email: "user@example.com",
  password: "securepassword",
});

// 成功后令牌会被自动管理

Token Refresh

令牌刷新

typescript
// The SDK handles this automatically with tokenStorage
// For manual refresh:
const { data, error } = await sdk.auth.refreshToken({
  refresh_token: storedRefreshToken,
});
typescript
// SDK会通过tokenStorage自动处理令牌刷新
// 手动刷新方式:
const { data, error } = await sdk.auth.refreshToken({
  refresh_token: storedRefreshToken,
});

User Profile

用户资料

typescript
// Get user details
const { data, error } = await sdk.auth.getUserDetails({ id: userId });

// Update user details
const { data, error } = await sdk.auth.updateUserDetails({ id: userId }, {
  first_name: "Jane",
  last_name: "Doe",
});
typescript
// 获取用户详情
const { data, error } = await sdk.auth.getUserDetails({ id: userId });

// 更新用户详情
const { data, error } = await sdk.auth.updateUserDetails({ id: userId }, {
  first_name: "Jane",
  last_name: "Doe",
});

Password Management

密码管理

typescript
// Change password (logged-in user)
const { data, error } = await sdk.auth.changePassword({
  old_password: "currentPass",
  new_password: "newPass",
  confirm_password: "newPass",
});

// Forgot password flow
const { data } = await sdk.auth.forgotPassword({ email: "user@example.com" });
// Returns otp_token → user enters OTP → then:
const { data: resetData } = await sdk.auth.resetPassword({
  otp_token: data.otp_token,
  new_password: "newPass",
});
typescript
// 修改密码(已登录用户)
const { data, error } = await sdk.auth.changePassword({
  old_password: "currentPass",
  new_password: "newPass",
  confirm_password: "newPass",
});

// 忘记密码流程
const { data } = await sdk.auth.forgotPassword({ email: "user@example.com" });
// 返回otp_token → 用户输入OTP → 然后:
const { data: resetData } = await sdk.auth.resetPassword({
  otp_token: data.otp_token,
  new_password: "newPass",
});

Key Feature:
register_if_not_exists

核心功能:
register_if_not_exists

Setting
register_if_not_exists: true
on login endpoints eliminates the need for separate "Login" vs "Sign Up" flows. If the user doesn't exist, Commerce Engine automatically creates their account and sends an OTP for verification. Every user starts the same way.
在登录接口中设置
register_if_not_exists: true
可以消除单独的"登录"与"注册"流程。如果用户不存在,Commerce Engine会自动创建其账户并发送用于验证的OTP。所有用户的启动流程都是相同的。

Common Pitfalls

常见陷阱

LevelIssueSolution
CRITICALCalling API without anonymous authAlways call
sdk.auth.getAnonymousToken()
first
CRITICALStoring tokens insecurelyUse
CookieTokenStorage
(SSR) or
BrowserTokenStorage
(SPA) — never plain
localStorage
without XSS protection
HIGHSeparate login/register flowsUse
register_if_not_exists: true
for unified flow
HIGHForgetting
otp_token
from step 1
Must pass both
otp_token
and
otp_action
from login response to
verifyOtp()
HIGHNot handling 401 errorsImplement token refresh — if refresh fails, re-authenticate
MEDIUMIgnoring
{ data, error }
pattern
Always check
error
before using
data
级别问题解决方案
严重未通过匿名认证就调用API始终先调用
sdk.auth.getAnonymousToken()
严重不安全地存储令牌使用
CookieTokenStorage
(SSR)或
BrowserTokenStorage
(SPA)—— 绝不要在没有XSS保护的情况下使用纯
localStorage
分开的登录/注册流程使用
register_if_not_exists: true
实现统一流程
忘记步骤1中的
otp_token
必须将登录响应中的
otp_token
otp_action
都传递给
verifyOtp()
未处理401错误实现令牌刷新 — 如果刷新失败,重新认证
忽略
{ data, error }
模式
在使用
data
前始终检查
error

See Also

另请参阅

  • setup/
    - SDK installation & token storage
  • cart-checkout/
    - Cart requires auth token
  • orders/
    - Orders require logged-in user
  • nextjs-patterns/
    - Server Actions for auth mutations
  • setup/
    - SDK安装与令牌存储
  • cart-checkout/
    - 购物车需要认证令牌
  • orders/
    - 订单需要已登录用户
  • nextjs-patterns/
    - 用于认证变更的Server Actions

Documentation

文档