概述
1Pass 微信登录让你的网站在 无需注册微信开放平台 的情况下,快速集成微信扫码登录。只需三步:
- 前端跳转 — 将用户重定向到
https://1pass.top/start?site_id=你的site_id - 处理回调 — 用户扫码后 1Pass 回调到你配置的地址,带上一次性 ticket
- 后端换票 — 后端用 AK/SK 签名调用
POST /token,换取用户 openid 等信息
重要提示:1Pass 不是标准 OAuth2 接口。你不需要传
redirect_uri、scope、response_type
等参数 — 回调地址在控制台配置,客户端无法动态指定(安全策略)。
完整示例可参考 CodeBlog Demo。
接入流程
sequenceDiagram
participant U as 用户浏览器
participant S as 你的网站
participant P as 1Pass
participant W as 微信
U->>S: ① 点击"微信登录"
S->>P: ② 重定向 /start?site_id=xxx
P->>W: ③ 重定向到微信扫码页
U->>W: ④ 用户扫码确认
W->>P: ⑤ 微信回调 1Pass
P->>S: ⑥ 回调你的网站(带 ticket)
U->>S: ⑦ 前端将 ticket 发给后端
S->>P: ⑧ POST /token(AK/SK 签名)
P-->>S: ⑨ 返回 openid、unionid 等
S-->>U: ⑩ 登录成功
第一步:前端发起登录
当用户点击"微信登录"按钮时,将浏览器重定向到:
https://1pass.top/start?site_id=你的site_id
请求参数
| 参数 | 必填 | 说明 |
|---|---|---|
site_id |
✅ | 你的站点 ID,格式 site_xxxxxxxx |
state2 |
❌ | 透传参数,会原样返回到回调 URL(可用于 CSRF 防护或页面状态保存) |
前端代码
<button onclick="wechatLogin()">微信扫码登录</button>
<script>
const SITE_ID = 'site_7e-Vpd6QYYwRb7zk'; // 替换为你的 site_id
function wechatLogin() {
// 可选:生成 state2 用于 CSRF 防护
const state2 = Math.random().toString(36).substring(2);
sessionStorage.setItem('login_state2', state2);
// 跳转到 1Pass 登录
window.location.href =
`https://1pass.top/start?site_id=${SITE_ID}&state2=${state2}`;
}
</script>
❌ 常见错误:不要使用 /api/v1/wechat/authorize、/oauth/authorize
等路径。1Pass 的登录入口是 /start,不是标准 OAuth2 路径。
第二步:处理回调
用户扫码确认后,1Pass 会将用户重定向到你在控制台配置的回调地址:
https://your-site.com/callback?ticket=TK_xxxxx&state2=xxxxx
回调参数
| 参数 | 说明 |
|---|---|
ticket |
一次性登录凭证,有效期 2 分钟,使用一次即失效 |
state2 |
你在第一步传入的透传参数(如有) |
回调页代码
<script>
const params = new URLSearchParams(window.location.search);
const ticket = params.get('ticket');
const state2 = params.get('state2');
// 验证 state2(防止 CSRF)
const savedState2 = sessionStorage.getItem('login_state2');
if (state2 && savedState2 && state2 !== savedState2) {
alert('安全校验失败,请重新登录');
} else if (ticket) {
// 将 ticket 发给你自己的后端换取用户信息
fetch('/api/auth/wechat?ticket=' + encodeURIComponent(ticket))
.then(r => r.json())
.then(data => {
if (data.openid) {
console.log('登录成功!', data);
} else {
console.error('登录失败:', data);
}
});
}
</script>
第三步:后端换取用户信息
前端拿到 ticket 后,由后端服务调用 1Pass 的 /token 接口换取真实用户信息。
⚠️ 必须在后端调用!此接口需要 SK 签名,SK 绝不能暴露在前端代码中。
接口信息
POST https://1pass.top/token
Content-Type: application/json
请求头
| Header | 必填 | 说明 |
|---|---|---|
Content-Type |
✅ | application/json |
X-1Pass-AK |
✅ | 你的 Access Key |
X-1Pass-Ts |
✅ | 当前 Unix 时间戳(秒),允许与服务器时间相差 ±300 秒 |
X-1Pass-Nonce |
✅ | 请求唯一标识(如 UUID),5 分钟内不可重复 |
X-1Pass-Sign |
✅ | 请求签名(详见 签名算法) |
请求体
{
"ticket": "从回调 URL 获取的 ticket",
"format": "json" // 可选,默认 "json",也可选 "jwt"
}
Node.js 完整示例
const crypto = require('crypto');
const AK = 'ak_xxxxxxxx'; // 替换为你的 AK
const SK = 'sk_xxxxxxxx'; // 替换为你的 SK
async function exchangeTicket(ticket) {
const ts = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomUUID();
const body = JSON.stringify({ ticket });
const bodyHash = crypto.createHash('sha256')
.update(body, 'utf8').digest('hex');
// 构建规范字符串(5 行,\n 连接)
const canonical =
`${ts}\n${nonce}\nPOST\n/token\n${bodyHash}`;
// 用原始 SK 签名
const signature = crypto.createHmac('sha256', SK)
.update(canonical, 'utf8').digest('base64')
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
const resp = await fetch('https://1pass.top/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-1Pass-AK': AK,
'X-1Pass-Ts': ts,
'X-1Pass-Nonce': nonce,
'X-1Pass-Sign': signature,
},
body,
});
return await resp.json();
// { site_id, openid, unionid, nickname, headimgurl, issued_at }
}
更多语言示例请参考 签名算法 — 多语言示例。
返回数据结构
format=json(默认)
{
"site_id": "site_7e-Vpd6QYYwRb7zk",
"openid": "oABC1234567890abcdef",
"unionid": "oUNION1234567890abcdef",
"nickname": "张三",
"headimgurl": "https://thirdwx.qlogo.cn/mmopen/vi_32/xxx/132",
"issued_at": "2026-02-08T12:00:00.000Z"
}
字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
site_id |
string | 站点 ID |
openid |
string | 用户在当前应用下的唯一标识,始终返回 |
unionid |
string | null | 用户在微信开放平台下的统一标识(需绑定开放平台) |
nickname |
string | null | 微信昵称(受用户隐私设置影响,可能为 null) |
headimgurl |
string | null | 微信头像 URL(可能为 null) |
issued_at |
string | 签发时间(ISO 8601 格式) |
format=jwt
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzaXRlX2lkIjoi...",
"token_type": "jwt"
}
JWT payload 包含与 JSON 格式相同的字段,外加 iat(签发时间戳)。
常见错误
以下是微信登录过程中最常见的错误及排查方法:
报错 invalid_signature
- 确认使用原始 SK — 使用
sk_...原始字符串,不要对它做 SHA-256 - 确认规范字符串格式 — 5 个部分之间是
\n(换行符),不是字面的\\n - 确认路径 — 只用路径部分
/token,不是完整 URL - 确认 body hash — 是请求体原文的 SHA-256,JSON 序列化后不能有多余空格
- 确认 Base64Url 编码 —
+→-,/→_,去掉末尾= - 确认时间戳是秒级 — 不是毫秒
报错 invalid_ticket
ticket 已过期(2 分钟有效)或已被使用。ticket 是一次性的,只能换取一次。请确保前端拿到 ticket 后立即调用后端接口。
报错 insufficient_credits
完整错误码列表请参考 错误码参考。