OAuth 鉴权
公开应用调用 Shoplazza Open API 之前必须先取得店铺授予的 Access Token。Shoplazza 采用 OAuth 2.0 的 Authorization Code 模式发放 Token。
本页定位:参考层——把 OAuth 涉及的参数、流程、Token 生命周期一次性讲清。
想跟着步骤把应用从零跑通:去 用 Node.js 与 Express 构建应用 教程。
流程总览
商家点击 Add App
│
▼
① Shoplazza 后台回调你配置的 App URL(带 hmac + shop + store_id)
│
▼
② 你的应用校验 hmac → 生成 state → 重定向到 Shoplazza 授权页
│
▼
③ 商家在 Shoplazza 授权页同意你的 scope 请求
│
▼
④ Shoplazza 回调你的 redirect_uri(带 code + state + hmac)
│
▼
⑤ 你的应用校验 state、hmac → 用 code 换 access_token
│
▼
⑥ 持久化 (shop, access_token, refresh_token, expires_at)
│
▼
调用 Open API,Header: `Access-Token: <access_token>`
下图为完整的 OAuth 授权交互流程:
关键参数
| 参数 | 来源 | 说明 |
|---|---|---|
client_id | 合作伙伴中心 | 应用唯一标识,公开值 |
client_secret | 合作伙伴中心 | 应用密钥,绝不可泄露给前端 |
redirect_uri | 你配置的回调地址 | 必须与合作伙伴中心配置完全一致 |
scope | 你声明的权限集合 | 逗号或空格分隔的 scope 列表 |
state | 你生成的随机串 | 防 CSRF,回调时原样返回,必须校验 |
hmac | Shoplazza 在 query 中带回 | 防请求伪造,必须校验,算法见 HMAC 签名校验 |
code | Shoplazza 回调返回 | Authorization Code,只能用一次 |
access_token | Token 接口返回 | 调 API 用的凭证,放在 Access-Token 请求头 |
refresh_token | Token 接口返回 | 用于刷新 access_token |
expires_at | Token 接口返回 | access_token 过期时间戳(秒) |
关键步骤
② 校验入参并跳转授权
商家从 Shoplazza 应用市场点击 Add App 后,Shoplazza 会请求你在合作伙伴中心配置的 App URL,URL 形如:
https://your-app.example.com/auth/install?hmac=<hmac>&install_from=app_store&shop=<store_name>.myshoplaza.com&store_id=<store_id>
你的应用必须:
- 校验
hmac(见 HMAC 签名校验) - 校验
shop是否以.myshoplaza.com结尾 - 生成随机
state并持久化(用于回调时比对) - 重定向商家到 Shoplazza 授权页:
https://<shop>/admin/oauth/authorize
?client_id=<client_id>
&scope=<scopes>
&redirect_uri=<redirect_uri>
&response_type=code
&state=<state>
商家在 Shoplazza 授权页会看到应用申请的权限(scope)确认界面:
④ ⑤ 回调与换 Token
商家点击安装、在授权页确认后:
商家同意授权后,Shoplazza 回调你的 redirect_uri:
https://your-app.example.com/auth/callback?code=<code>&shop=<shop>&state=<state>&hmac=<hmac>
校验顺序:
- 校验
hmac - 校验
state与 ② 生成的一致(防 CSRF) - 校验
shop合法
通过后,向店铺 token 接口换 access_token:
POST https://<shop>/admin/oauth/token
Content-Type: application/json
{
"client_id": "<client_id>",
"client_secret": "<client_secret>",
"code": "<code>",
"grant_type": "authorization_code",
"redirect_uri": "<redirect_uri>"
}
成功响应:
{
"token_type": "Bearer",
"expires_at": 1550546245,
"access_token": "<access_token>",
"refresh_token": "<refresh_token>",
"store_id": "2",
"store_name": "xiong1889"
}
字段说明:
expires_at:access_token 过期时间戳(秒)refresh_token:用于刷新 access_tokenstore_id/store_name:店铺标识
也可使用 Shoplazza OAuth SDK(Go)换取 token:
import (
co "github.com/shoplazza-os/oauth-sdk-go"
"github.com/shoplazza-os/oauth-sdk-go/shoplazza"
)
oauth := &co.Config{
ClientID: "s1Ip1WxpoEAHtPPzGiP2rK2Az-P07Nie7V97hRKigl4",
ClientSecret: "{YOUR_CLIENT_SECRET}",
Endpoint: shoplazza.Endpoint,
RedirectURI: "https://3830-43-230-206-233.ngrok.io/oauth_sdk/redirect_uri/",
Scopes: []string{"read_shop"},
}
token, err := oauth.Exchange(context.Background(),"xxx.myshoplaza.com", "code"))
access_token 与 refresh_token 的有效期均为 1 年。请在 access_token 接近 expires_at 前用 refresh_token 换取新 token(见下方「刷新 Access Token」)。
⑥ 持久化与调用
把 (shop, access_token, refresh_token, expires_at) 写入存储(数据库、Redis 等)。调用 Open API 时在 Header 携带:
GET https://<shop>/openapi/2022-01/customers HTTP/1.1
Access-Token: <access_token>
刷新 Access Token
当 access_token 接近 expires_at 时,用 refresh_token 换新的:
POST https://<shop>/admin/oauth/token
Content-Type: application/json
{
"client_id": "<client_id>",
"client_secret": "<client_secret>",
"refresh_token": "<refresh_token>",
"grant_type": "refresh_token",
"redirect_uri": "<redirect_uri>"
}
响应字段与初次换 token 相同。
也可使用 Shoplazza OAuth SDK(Go)刷新 token:
import (
co "github.com/shoplazza-os/oauth-sdk-go"
"github.com/shoplazza-os/oauth-sdk-go/shoplazza"
)
oauth := &co.Config{
ClientID: "s1Ip1WxpoEAHtPPzGiP2rK2Az-P07Nie7V97hRKigl4",
ClientSecret: "{YOUR_CLIENT_SECRET}",
Endpoint: shoplazza.Endpoint,
RedirectURI: "https://3830-43-230-206-233.ngrok.io/oauth_sdk/redirect_uri/",
Scopes: []string{"read_shop"},
}
token, err := oauth.RefreshToken(context.Background(), "xxx.myshoplaza.com", "refresh token")
安全要求
client_secret与access_token一律保存在服务端,绝不下发到浏览器- 每次回调必须校验
hmac和state code一次性,重复使用会失败- 必须使用 HTTPS
- SSRF 防护:校验
shop参数是合法的*.myshoplaza.com域名 - CSRF 防护:发起授权时生成随机
state,回调时校验并消费 - 时序攻击防护:比对 HMAC 使用
crypto.timingSafeEqual
Token 存储约定
示例用 SQLite 以店铺域名为主键存储 access_token(表 shop_tokens:shop_domain 主键 / access_token / updated_time),店铺再次访问时直接读库、无需重新授权。生产环境建议:
- 改用 PostgreSQL / MySQL 等独立数据库
- 数据库路径通过环境变量(如
DB_PATH)配置,避免相对路径在不同部署环境出问题 - 用
require("sqlite3")而非.verbose(),避免额外调试日志影响性能
最佳实践
环境变量管理
- 开发环境:使用
.env文件 - 生产环境:使用云平台的环境变量功能或密钥管理服务(如 AWS Secrets Manager、Azure Key Vault、阿里云 KMS 等)
- 永远不要将敏感信息硬编码到项目中
- 将
.env添加到.gitignore
const CLIENT_SECRET = process.env.CLIENT_SECRET;
HMAC 验证
所有来自 Shoplazza 的请求都应该验证 HMAC 签名:
app.get("/auth", hmacValidator, (req, res) => {
// 只有验证通过的请求才会执行这里的代码
});
项目工程化
生产项目建议采用前后端分离的工程化结构:
shoplazza-app-demo/
├── server/
│ ├── routes/
│ ├── utils/
│ ├── database/
│ └── index.js
├── src/
│ ├── components/
│ ├── pages/
│ └── main.js
├── dist/
├── package.json
└── .env
参考实现
- 完整 Node.js + Express 实战代码:用 Node.js 与 Express 构建应用
- 各语言 SDK:API 客户端库
- 申请的 scope 列表:访问权限(Scopes)