b2c-slas-auth-patterns by salesforcecommercecloud/b2c-developer-tooling
npx skills add https://github.com/salesforcecommercecloud/b2c-developer-tooling --skill b2c-slas-auth-patterns超越基础登录的 SLAS(购物者登录与 API 访问服务)高级认证模式。这些模式支持无密码认证、混合店面支持以及系统到系统的集成。
| 方法 | 使用场景 | 用户体验 |
|---|---|---|
| 密码 | 传统登录 | 用户名 + 密码表单 |
| 邮箱一次性密码 | 无密码邮箱登录 | 代码发送至邮箱 |
| 短信一次性密码 | 无密码短信登录 | 代码发送至手机 |
| 通行密钥 | FIDO2/WebAuthn | 生物识别或设备 PIN 码 |
| 会话桥接 | 混合店面 | 无缝 PWA ↔ SFRA |
| 混合认证 | B2C 25.3+ | 内置平台认证同步 |
| TSOB | 系统集成 | 后端服务调用 |
通过电子邮件发送一次性密码,实现无密码登录。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
/oauth2/passwordless/login 并附带回调 URIpwdless_login_token POST 到您的回调地址// POST /shopper/auth/v1/organizations/{org}/oauth2/passwordless/login
async function initiatePasswordlessLogin(email, siteId) {
const response = await fetch(
`https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}/oauth2/passwordless/login`,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
user_id: email,
mode: 'callback',
channel_id: siteId,
callback_uri: 'https://yoursite.com/api/passwordless/callback'
})
}
);
// SLAS 将向您的 callback_uri 发送包含 pwdless_login_token 的 POST 请求
return response.json();
}
您的回调端点接收 pwdless_login_token。生成一次性密码并发送给用户:
// 您的回调端点(接收来自 SLAS 的 POST 请求)
app.post('/api/passwordless/callback', async (req, res) => {
const { pwdless_login_token, user_id } = req.body;
// 生成 6 位数字一次性密码
const otp = Math.floor(100000 + Math.random() * 900000).toString();
// 存储令牌与一次性密码的映射关系(例如,使用 Redis,设置 10 分钟 TTL)
await redis.setex(`pwdless:${otp}`, 600, JSON.stringify({
token: pwdless_login_token,
email: user_id
}));
// 通过电子邮件发送一次性密码(在 SLAS 管理界面中配置)
await sendOTPEmail(user_id, otp);
res.status(200).send('OK');
});
// POST /shopper/auth/v1/organizations/{org}/oauth2/passwordless/token
async function exchangeOTPForToken(otp, clientId, clientSecret, siteId) {
// 检索存储的令牌
const stored = JSON.parse(await redis.get(`pwdless:${otp}`));
if (!stored) throw new Error('无效或已过期的一次性密码');
const response = await fetch(
`https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}/oauth2/passwordless/token`,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Basic ${btoa(clientId + ':' + clientSecret)}`
},
body: new URLSearchParams({
grant_type: 'client_credentials',
hint: 'pwdless_login',
pwdless_login_token: stored.token,
channel_id: siteId
})
}
);
// 返回:{ access_token, refresh_token, ... }
return response.json();
}
使用 Marketing Cloud 或自定义集成通过短信发送一次性密码。
通过 Salesforce Marketing Cloud 配置短信:
使用与邮箱相同的回调流程,但通过短信提供商发送:
// 在您的回调处理程序中
const twilio = require('twilio')(accountSid, authToken);
async function sendOTPSMS(phoneNumber, otp) {
await twilio.messages.create({
body: `您的登录验证码是:${otp}`,
from: '+1234567890',
to: phoneNumber
});
}
使用 FIDO2/WebAuthn 通行密钥启用生物识别认证。注册需要通过一次性密码进行预先身份验证。流程包括:使用 SLAS 开始注册,通过浏览器 WebAuthn API 创建凭证,然后完成注册。认证遵循类似的开始/认证/完成模式。
完整注册和认证代码示例请参见 references/PASSKEYS.md。
使用签名的桥接令牌(访客使用 dwsgst,注册用户使用 dwsrst)在 PWA Kit 和 SFRA 店面之间保持会话连续性。支持 PWA 到 SFRA 和 SFRA 到 PWA 两个方向。请注意,对于注册购物者,DWSID 已弃用。
完整实现细节,包括令牌生成、重定向模式、回调处理程序和错误处理,请参见 references/SESSION-BRIDGE.md。
混合认证取代了插件 SLAS,适用于混合 PWA/SFRA 店面。它直接内置于 B2C 平台中,并提供自动会话同步。
如果正在使用插件 SLAS,请迁移到混合认证:
重要: 访客令牌刷新必须包含 channel_id 参数。
公共客户端(无密钥)接收一次性使用的刷新令牌:
async function refreshTokenPublic(refreshToken, clientId, siteId) {
const response = await fetch(
`https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}/oauth2/token`,
{
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: clientId,
channel_id: siteId // 必需
})
}
);
// 返回新的 refresh_token(旧的将失效)
return response.json();
}
私有客户端可以重复使用刷新令牌:
async function refreshTokenPrivate(refreshToken, clientId, clientSecret, siteId) {
const response = await fetch(
`https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}/oauth2/token`,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Basic ${btoa(clientId + ':' + clientSecret)}`
},
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
channel_id: siteId // 必需
})
}
);
// 相同的 refresh_token 可以再次使用
return response.json();
}
服务器到服务器的认证,代表购物者进行操作。
async function getTSOBToken(shopperLoginId) {
const response = await fetch(
`https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}/oauth2/trusted-system/token`,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Basic ${btoa(clientId + ':' + clientSecret)}`
},
body: new URLSearchParams({
grant_type: 'client_credentials',
login_id: shopperLoginId,
channel_id: siteId,
usid: shopperUsid // 可选:重用现有会话
})
}
);
// 返回代表指定购物者操作的令牌
return response.json();
}
3 秒保护窗口: 3 秒内对同一购物者的多次 TSOB 调用将返回 HTTP 409:
"Tenant id <id> has already performed a login operation for user id <user_id> in the last 3 seconds."
在您的代码中处理此情况:
async function getTSOBTokenWithRetry(shopperLoginId, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await getTSOBToken(shopperLoginId);
} catch (error) {
if (error.status === 409 && i < maxRetries - 1) {
await new Promise(r => setTimeout(r, 3000));
continue;
}
throw error;
}
}
}
sfcc.ts_ext_on_behalf_of 范围)login_id 长度在 60 个字符以内使用 JWKS 验证 SLAS 令牌。
async function getJWKS() {
const response = await fetch(
`https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}/oauth2/jwks`
);
return response.json();
}
const jose = require('jose');
async function validateToken(accessToken) {
// 获取 JWKS
const jwksUrl = `https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}/oauth2/jwks`;
const JWKS = jose.createRemoteJWKSet(new URL(jwksUrl));
// 验证令牌
const { payload } = await jose.jwtVerify(accessToken, JWKS, {
issuer: `https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}`,
audience: clientId
});
return payload;
}
| 声明 | 描述 |
|---|---|
sub | 主体(客户 ID 或访客 ID) |
isb | 身份主体绑定 |
iss | 签发者 |
aud | 受众(客户端 ID) |
exp | 过期时间 |
iat | 签发时间 |
scope | 授予的范围 |
tsob | TSOB 令牌类型(用于受信系统令牌) |
channel_id每周安装数
79
代码仓库
GitHub 星标数
34
首次出现
2026 年 2 月 11 日
安全审计
安装于
github-copilot74
codex71
opencode70
cursor70
gemini-cli69
amp69
Advanced authentication patterns for SLAS (Shopper Login and API Access Service) beyond basic login. These patterns enable passwordless authentication, hybrid storefront support, and system-to-system integration.
| Method | Use Case | User Experience |
|---|---|---|
| Password | Traditional login | Username + password form |
| Email OTP | Passwordless email | Code sent to email |
| SMS OTP | Passwordless SMS | Code sent to phone |
| Passkeys | FIDO2/WebAuthn | Biometric or device PIN |
| Session Bridge | Hybrid storefronts | Seamless PWA ↔ SFRA |
| Hybrid Auth | B2C 25.3+ | Built-in platform auth sync |
| TSOB | System integration | Backend service calls |
Send one-time passwords via email for passwordless login.
/oauth2/passwordless/login with callback URIpwdless_login_token to your callback// POST /shopper/auth/v1/organizations/{org}/oauth2/passwordless/login
async function initiatePasswordlessLogin(email, siteId) {
const response = await fetch(
`https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}/oauth2/passwordless/login`,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
user_id: email,
mode: 'callback',
channel_id: siteId,
callback_uri: 'https://yoursite.com/api/passwordless/callback'
})
}
);
// SLAS will POST to your callback_uri with pwdless_login_token
return response.json();
}
Your callback endpoint receives pwdless_login_token. Generate an OTP and send it to the user:
// Your callback endpoint (receives POST from SLAS)
app.post('/api/passwordless/callback', async (req, res) => {
const { pwdless_login_token, user_id } = req.body;
// Generate 6-digit OTP
const otp = Math.floor(100000 + Math.random() * 900000).toString();
// Store token + OTP mapping (e.g., Redis with 10 min TTL)
await redis.setex(`pwdless:${otp}`, 600, JSON.stringify({
token: pwdless_login_token,
email: user_id
}));
// Send OTP via email (configure in SLAS Admin UI)
await sendOTPEmail(user_id, otp);
res.status(200).send('OK');
});
// POST /shopper/auth/v1/organizations/{org}/oauth2/passwordless/token
async function exchangeOTPForToken(otp, clientId, clientSecret, siteId) {
// Retrieve stored token
const stored = JSON.parse(await redis.get(`pwdless:${otp}`));
if (!stored) throw new Error('Invalid or expired OTP');
const response = await fetch(
`https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}/oauth2/passwordless/token`,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Basic ${btoa(clientId + ':' + clientSecret)}`
},
body: new URLSearchParams({
grant_type: 'client_credentials',
hint: 'pwdless_login',
pwdless_login_token: stored.token,
channel_id: siteId
})
}
);
// Returns: { access_token, refresh_token, ... }
return response.json();
}
Send OTP via SMS using Marketing Cloud or custom integration.
Configure SMS through Salesforce Marketing Cloud:
Use the same callback flow as email, but send via SMS provider:
// In your callback handler
const twilio = require('twilio')(accountSid, authToken);
async function sendOTPSMS(phoneNumber, otp) {
await twilio.messages.create({
body: `Your login code is: ${otp}`,
from: '+1234567890',
to: phoneNumber
});
}
Enable biometric authentication using FIDO2/WebAuthn passkeys. Registration requires prior identity verification via OTP. The flow involves starting registration with SLAS, creating a credential via the browser WebAuthn API, then completing registration. Authentication follows a similar start/authenticate/finish pattern.
See references/PASSKEYS.md for full registration and authentication code examples.
Maintain session continuity between PWA Kit and SFRA storefronts using signed bridge tokens (dwsgst for guest, dwsrst for registered). Supports both PWA-to-SFRA and SFRA-to-PWA directions. Note that DWSID is deprecated for registered shoppers.
See references/SESSION-BRIDGE.md for full implementation details including token generation, redirect patterns, callback handlers, and error handling.
Hybrid Auth replaces Plugin SLAS for hybrid PWA/SFRA storefronts. It's built directly into the B2C platform and provides automatic session synchronization.
If using Plugin SLAS, migrate to Hybrid Auth:
Important: The channel_id parameter is required for guest token refresh.
Public clients (no secret) receive single-use refresh tokens:
async function refreshTokenPublic(refreshToken, clientId, siteId) {
const response = await fetch(
`https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}/oauth2/token`,
{
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: clientId,
channel_id: siteId // REQUIRED
})
}
);
// Returns NEW refresh_token (old one is invalidated)
return response.json();
}
Private clients can reuse refresh tokens:
async function refreshTokenPrivate(refreshToken, clientId, clientSecret, siteId) {
const response = await fetch(
`https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}/oauth2/token`,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Basic ${btoa(clientId + ':' + clientSecret)}`
},
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
channel_id: siteId // REQUIRED
})
}
);
// Same refresh_token can be used again
return response.json();
}
Server-to-server authentication to act on behalf of a shopper.
async function getTSOBToken(shopperLoginId) {
const response = await fetch(
`https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}/oauth2/trusted-system/token`,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Basic ${btoa(clientId + ':' + clientSecret)}`
},
body: new URLSearchParams({
grant_type: 'client_credentials',
login_id: shopperLoginId,
channel_id: siteId,
usid: shopperUsid // Optional: reuse existing session
})
}
);
// Returns tokens that act as the specified shopper
return response.json();
}
3-Second Protection Window: Multiple TSOB calls for the same shopper within 3 seconds return HTTP 409:
"Tenant id <id> has already performed a login operation for user id <user_id> in the last 3 seconds."
Handle this in your code:
async function getTSOBTokenWithRetry(shopperLoginId, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await getTSOBToken(shopperLoginId);
} catch (error) {
if (error.status === 409 && i < maxRetries - 1) {
await new Promise(r => setTimeout(r, 3000));
continue;
}
throw error;
}
}
}
sfcc.ts_ext_on_behalf_of scope)login_id length under 60 charactersValidate SLAS tokens using JWKS (JSON Web Key Set).
async function getJWKS() {
const response = await fetch(
`https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}/oauth2/jwks`
);
return response.json();
}
const jose = require('jose');
async function validateToken(accessToken) {
// Get JWKS
const jwksUrl = `https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}/oauth2/jwks`;
const JWKS = jose.createRemoteJWKSet(new URL(jwksUrl));
// Verify token
const { payload } = await jose.jwtVerify(accessToken, JWKS, {
issuer: `https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}`,
audience: clientId
});
return payload;
}
| Claim | Description |
|---|---|
sub | Subject (customer ID or guest ID) |
isb | Identity subject binding |
iss | Issuer |
aud | Audience (client ID) |
exp | Expiration time |
iat | Issued at time |
scope |
channel_id in refresh requestsWeekly Installs
79
Repository
GitHub Stars
34
First Seen
Feb 11, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
github-copilot74
codex71
opencode70
cursor70
gemini-cli69
amp69
Linux云主机安全托管指南:从SSH加固到HTTPS部署
46,900 周安装
| Granted scopes |
tsob | TSOB token type (for trusted system tokens) |