跳到主要内容

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,回调时原样返回,必须校验
hmacShoplazza 在 query 中带回防请求伪造,必须校验,算法见 HMAC 签名校验
codeShoplazza 回调返回Authorization Code,只能用一次
access_tokenToken 接口返回调 API 用的凭证,放在 Access-Token 请求头
refresh_tokenToken 接口返回用于刷新 access_token
expires_atToken 接口返回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>

你的应用必须:

  1. 校验 hmac(见 HMAC 签名校验
  2. 校验 shop 是否以 .myshoplaza.com 结尾
  3. 生成随机 state 并持久化(用于回调时比对)
  4. 重定向商家到 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>

校验顺序:

  1. 校验 hmac
  2. 校验 state 与 ② 生成的一致(防 CSRF)
  3. 校验 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_token
  • store_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"))
Token 有效期

access_tokenrefresh_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_secretaccess_token 一律保存在服务端,绝不下发到浏览器
  • 每次回调必须校验 hmacstate
  • code 一次性,重复使用会失败
  • 必须使用 HTTPS
  • SSRF 防护:校验 shop 参数是合法的 *.myshoplaza.com 域名
  • CSRF 防护:发起授权时生成随机 state,回调时校验并消费
  • 时序攻击防护:比对 HMAC 使用 crypto.timingSafeEqual

Token 存储约定

示例用 SQLite 以店铺域名为主键存储 access_token(表 shop_tokensshop_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

参考实现