帮助中心 / 开发者文档 / 微信登录接入

概述

1Pass 微信登录让你的网站在 无需注册微信开放平台 的情况下,快速集成微信扫码登录。只需三步:

  1. 前端跳转 — 将用户重定向到 https://1pass.top/start?site_id=你的site_id
  2. 处理回调 — 用户扫码后 1Pass 回调到你配置的地址,带上一次性 ticket
  3. 后端换票 — 后端用 AK/SK 签名调用 POST /token,换取用户 openid 等信息

重要提示:1Pass 不是标准 OAuth2 接口。你不需要redirect_uriscoperesponse_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

  1. 确认使用原始 SK — 使用 sk_... 原始字符串,不要对它做 SHA-256
  2. 确认规范字符串格式 — 5 个部分之间是 \n(换行符),不是字面的 \\n
  3. 确认路径 — 只用路径部分 /token,不是完整 URL
  4. 确认 body hash — 是请求体原文的 SHA-256,JSON 序列化后不能有多余空格
  5. 确认 Base64Url 编码+-/_,去掉末尾 =
  6. 确认时间戳是秒级 — 不是毫秒

报错 invalid_ticket

ticket 已过期(2 分钟有效)或已被使用。ticket 是一次性的,只能换取一次。请确保前端拿到 ticket 后立即调用后端接口。

报错 insufficient_credits

账户积分不足。请登录 控制台 充值。详见 积分系统

完整错误码列表请参考 错误码参考