© 2026 XiaoXian
文章
知识库
知识OAuth2OIDCArcTower

ArcTower-OAuth2接入文档

OAuth 2.0 / OpenID Connect 认证流程文档展路径

DarfDarfAI Level 3
11 分钟阅读·1,954 字写于 19 天前

OAuth 2.0 / OpenID Connect 认证流程文档#

1. 概述#

AxT 的 SSO 基于 OAuth 2.0 授权码模式(Authorization Code Flow) + OpenID Connect (OIDC) 实现。第三方客户端通过此流程获取 accessToken、refreshToken 以及(可选)idToken,用于访问受保护的 API 资源和获取用户身份信息。

角色说明#

  • 客户端(Client): 在 ArcTower 创建 OAuth App 的开发者/应用
  • 用户(Resource Owner): 登录并授权的最终用户
  • 授权服务器(Authorization Server): ArcTower SSO Provider,部署于 auth.axtrk.com
  • 资源服务器(Resource Server): ArcTower 用户 API,部署于 api.axtrk.com

部署域划分#

域名职责
auth.axtrk.comSSO Issuer —— OIDC Discovery / JWKS / 授权 / Token / UserInfo / 撤销 / 内省
api.axtrk.com业务 API(含 OAuth App CRUD、第一方用户 API)
account.axtrk.com前端授权页面(承载登录 UI)

Token 有效期与签名#

Token有效期签名算法说明
code5 分钟——临时授权码,一次性使用
accessToken10 分钟RS256访问令牌,用于请求 API 资源
refreshToken14 天RS256刷新令牌,原子轮换 + 重放检测
idToken10 分钟RS256OIDC 身份令牌,请求 scope 含 openid 时签发

所有 Token 均使用 RS256 非对称签名,按 token 类型隔离密钥。资源服务器可通过 JWKS 端点获取公钥独立验证,无需联系授权服务器。

支持的安全特性#

  • ✅ PKCE (RFC 7636) —— S256 / plain 两种方法
  • ✅ state 参数 —— 服务端存储并强制校验(防 CSRF)
  • ✅ Nonce(OIDC) —— 绑定到 idToken,防重放
  • ✅ Refresh Token Rotation —— 每次刷新签发新 RT,旧 RT 失效,检测到重放自动撤销整条链
  • ✅ 单点退出(SLO) —— 第一方账户登出/改密时级联撤销所有 OAuth 会话
  • ✅ Token 内省(RFC 7662) / Token 撤销(RFC 7009)

2. OIDC Discovery#

所有协议端点和公钥信息可通过标准 Discovery 端点动态发现,客户端应首选使用 Discovery 而非硬编码 URL。

2.1 获取 OIDC 配置#

请求:

GET https://auth.axtrk.com/.well-known/openid-configuration

返回示例:

{
  "issuer": "https://auth.axtrk.com",
  "authorization_endpoint": "https://auth.axtrk.com/api/v1/oauth/authorize/{clientId}",
  "token_endpoint": "https://auth.axtrk.com/api/v1/oauth/access",
  "userinfo_endpoint": "https://auth.axtrk.com/api/v1/o/me",
  "jwks_uri": "https://auth.axtrk.com/.well-known/jwks.json",
  "revocation_endpoint": "https://auth.axtrk.com/api/v1/oauth/revoke-token",
  "introspection_endpoint": "https://auth.axtrk.com/api/v1/oauth/introspect",
  "end_session_endpoint": "https://auth.axtrk.com/api/v1/oauth/logout",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "subject_types_supported": ["pairwise"],
  "id_token_signing_alg_values_supported": ["RS256"],
  "scopes_supported": ["openid", "profile", "email", "phone"],
  "token_endpoint_auth_methods_supported": ["client_secret_post"],
  "code_challenge_methods_supported": ["S256", "plain"],
  "claims_supported": ["sub", "iss", "aud", "exp", "iat", "nonce", "auth_time",
                       "preferred_username", "picture", "email", "email_verified",
                       "phone_number", "phone_number_verified"]
}

2.2 获取公钥集 (JWKS)#

资源服务器用公钥离线验证 Token 签名。

请求:

GET https://auth.axtrk.com/.well-known/jwks.json

返回示例:

{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "alg": "RS256",
      "kid": "oauth-v1",
      "n": "...(modulus)...",
      "e": "AQAB"
    },
    {
      "kty": "RSA",
      "use": "sig",
      "alg": "RS256",
      "kid": "id-v1",
      "n": "...(modulus)...",
      "e": "AQAB"
    }
  ]
}

JWT Header 中的 kid 字段决定使用哪把公钥验证;oauth-v1 用于 accessToken,id-v1 用于 idToken。


3. 标准授权码流程(含 PKCE)#

+-------------------+
|       客户端       |
+-------------------+
        |
        | 1. 生成 code_verifier + code_challenge = BASE64URL(SHA256(verifier))
        |    将用户跳转至 AxT 授权页
        |    GET https://account.axtrk.com/oauth/authorize
        |      ?clientId={clientId}
        |      &redirect_uri={uri}
        |      &state={state}
        |      &nonce={nonce}            (OIDC 可选)
        |      &scope=openid+profile+email   (含 openid 时触发 OIDC)
        |      &code_challenge={challenge}
        |      &code_challenge_method=S256
        v
+----------------------+
|    用户登录 + 授权     |
+----------------------+
        |
        | 2. 授权通过后重定向
        |    {redirect_uri}?code={code}&state={state}
        v
+-------------------+
|       客户端       |
+-------------------+
        |
        | 3. 校验 state == 原值 (否则终止)
        |
        | 4. 使用 code + code_verifier 换取 Token
        |    POST https://auth.axtrk.com/api/v1/oauth/access
        |    Body: {clientId, clientSecret, code, redirectUri, state, codeVerifier}
        v
+-------------------+
|   授权服务器       |
| (auth.axtrk.com)    |
+-------------------+
        |
        | 5. 校验 code / state / PKCE / clientSecret
        |    颁发 accessToken + refreshToken (+ idToken 若 scope 含 openid)
        v
+-------------------+
|       客户端       |
+-------------------+
        |
        | 6. 访问受保护 API
        |    GET https://auth.axtrk.com/api/v1/o/me
        |    Header: Authorization: Bearer {accessToken}
        v
+-------------------+
|   授权服务器       |
+-------------------+
        |
        | 7. 返回 UserInfo
        v
        ...
        |
        | 8. accessToken 过期后使用 refreshToken 轮换
        |    POST https://auth.axtrk.com/api/v1/oauth/refresh
        |    Body: {clientId, clientSecret, refreshToken}
        |    → 返回全新的 accessToken + refreshToken(旧 RT 立即失效)
        v
        ...

4. 第三方快捷登录(tpo_provider)#

未登录用户可附加 tpo_provider 参数,让用户通过 Google/GitHub/QQ 一键登录或自动注册为 FastUser,无需手动跳转登录页。

客户端
  │ 1. 跳转授权页(附 tpo_provider=github)
  │    GET https://account.axtrk.com/oauth/authorize?...&tpo_provider=github
  ▼
前端检测未登录 + tpo_provider
  │ 2. 自动跳转至 GitHub OAuth
  ▼
GitHub 登录授权
  │ 3. 回调 /auth/tpo/callback/{platform}?code=...
  ▼
AxT 回调处理
  │ 4. POST /api/v1/tpo-auth/login
  │    查找已有用户 or 创建 FastUser
  │    返回第一方 accessToken
  ▼
前端恢复 OAuth 上下文
  │ 5. 自动跳回 /oauth/authorize 完成授权
  ▼
后续与标准流程一致

5. 详细接口#

5.1 授权端点#

请求: GET https://account.axtrk.com/oauth/authorize

前端授权页本身不是后端接口,它会调用后端 GET https://auth.axtrk.com/api/v1/oauth/authorize/{clientId} 生成授权码并重定向。

Query 参数:

参数类型必填说明
clientIdstring是客户端 ID(路径参数)
redirect_uristring否回调 URL,未传则使用配置中第一个
statestring强烈建议防 CSRF 的随机串,服务端会存储并在换 token 时校验
noncestringOIDC 建议防 idToken 重放,会写入 idToken 的 nonce claim
scopestringOIDC 必填空格分隔,含 openid 时触发 OIDC 并签发 idToken。可选:profile / email / phone
code_challengestring公共客户端必填PKCE 挑战值 = BASE64URL(SHA256(code_verifier))
code_challenge_methodstring配合 challengeS256(推荐)或 plain
tpo_providerstring否google / github / qq —— 未登录时自动走第三方登录

PKCE 生成示例(JavaScript):

// 1. 生成 code_verifier: 43-128 字符的随机串
const verifier = base64url(crypto.getRandomValues(new Uint8Array(32)));
 
// 2. 生成 code_challenge
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
const challenge = base64url(new Uint8Array(digest));
// 存储 verifier,跳转授权页并传 challenge

跳转回调: 授权成功后浏览器被重定向至:

{redirect_uri}?code={code}&state={state}
参数说明
code临时授权码,5 分钟有效,一次性
state原始传入值,客户端必须校验

5.2 Token 端点#

用 code 换 accessToken(+ idToken)+ refreshToken。

请求:

POST https://auth.axtrk.com/api/v1/oauth/access
Content-Type: application/json

Body:

{
  "clientId": "your_client_id",
  "clientSecret": "your_client_secret",
  "code": "abc123",
  "redirectUri": "https://your-app.com/callback",
  "state": "random123",
  "codeVerifier": "your_code_verifier"
}
字段必填说明
clientId是——
clientSecret是——
code是步骤 2 获得的授权码
redirectUri条件若授权时传了必须一致
state条件若授权时传了必须一致(服务端校验)
codeVerifier条件若授权时传了 code_challenge 必须提供

返回示例(含 OIDC):

{
  "code": 200,
  "data": {
    "accessToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6Im9hdXRoLXYxIn0...",
    "refreshToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6Im9hdXRoX3Jmcy12MSJ9...",
    "idToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImlkLXYxIn0...",
    "tokenType": "Bearer",
    "expiresIn": 600,
    "refreshExpiresIn": 1209600,
    "scope": "profile"
  },
  "message": "Success"
}
字段说明
accessTokenRS256 JWT,用于访问 API
refreshTokenRS256 JWT,用于刷新(原子轮换)
idToken仅当请求 scope 含 openid 时返回
tokenType固定 Bearer
expiresInaccessToken 秒数(10m)
refreshExpiresInrefreshToken 秒数(14d)
scope实际授权的权限列表(空格分隔)

5.3 刷新 Token#

请求:

POST https://auth.axtrk.com/api/v1/oauth/refresh
Content-Type: application/json

Body:

{
  "clientId": "your_client_id",
  "clientSecret": "your_client_secret",
  "refreshToken": "old_refresh_token"
}

返回: 结构同 5.2,但不含 idToken。

重要机制:

  • 每次刷新都会签发全新的 accessToken + refreshToken,旧 RT 立即失效
  • 若检测到同一个 RT 被重复使用(重放攻击),服务器会撤销当前整条令牌链,要求用户重新登录
  • 客户端必须在本地立即用新 RT 替换旧 RT

5.4 UserInfo 端点#

请求:

GET https://auth.axtrk.com/api/v1/o/me
Authorization: Bearer {accessToken}

返回示例:

{
  "code": 200,
  "data": {
    "user": {
      "openId": "axt_15fd483903e9198109206c92866c9f33",
      "username": "Darf",
      "country": "CN",
      "active": true,
      "avatarUrl": "https://q2.qlogo.cn/headimg_dl?dst_uin=1680839&spec=640",
      "registrationTime": "2025-07-17T16:18:19.179092Z",
      "twoFactorAuth": false
    },
    "role": {
      "prefix": "AT",
      "role": "USER",
      "expireTime": null
    }
  },
  "message": "Success"
}

openId 是用户 UUID + 应用 oidHashKey 的 SHA256 哈希,同一用户对不同应用的 openId 不同(pairwise)。


5.5 Token 撤销(RFC 7009)#

用于客户端主动注销单个 accessToken 或 refreshToken。

请求:

POST https://auth.axtrk.com/api/v1/oauth/revoke-token
Content-Type: application/json

Body:

{
  "clientId": "your_client_id",
  "clientSecret": "your_client_secret",
  "token": "token_to_revoke"
}

传入 accessToken 或 refreshToken 均可。按 RFC 7009,即使 token 已失效也会返回 200。


5.6 Token 内省(RFC 7662)#

资源服务器/客户端可查询 Token 是否仍然有效及其 claims。

请求:

POST https://auth.axtrk.com/api/v1/oauth/introspect
Content-Type: application/json

Body:

{
  "clientId": "your_client_id",
  "clientSecret": "your_client_secret",
  "token": "token_to_inspect"
}

返回(活跃):

{
  "code": 200,
  "data": {
    "active": true,
    "sub": "axt_15fd483903e9198109206c92866c9f33",
    "client_id": "your_client_id",
    "token_type": "Bearer",
    "exp": 1745678901,
    "iat": 1745635701,
    "scope": ["profile"]
  }
}

返回(失效):

{
  "code": 200,
  "data": { "active": false }
}

5.7 结束会话(OIDC End Session)#

通知授权服务器用户已从客户端登出,清除对应 Token。

请求:

POST https://auth.axtrk.com/api/v1/oauth/logout
Content-Type: application/json

Body:

{
  "clientId": "your_client_id",
  "clientSecret": "your_client_secret",
  "token": "access_or_refresh_token"
}

按 clientTier 分流:

Tier行为
STANDARD仅撤销本次传入的 token(RFC 7009 / OIDC 标准行为)
ADVANCED / INTERNAL撤销本次 token + 反向单点退出:清空该用户在 SSO 端的所有第一方 access token,并级联撤销该用户在所有 OAuth 客户端的会话(等同于用户主动 POST /api/v1/auth/logout)

接口签名对调用方完全透明,是否级联取决于该 OAuth App 注册时的 tier。普通客户端无法通过此端点触发反向单点退出。


6. idToken 结构(OIDC)#

请求 scope 含 openid 时 /access 返回的 idToken 为标准 JWT,包含:

Claim说明
isshttps://auth.axtrk.com
sub用户 openId(pairwise,按 client 隔离)
audclientId
exp / iat有效期 / 签发时间(epoch 秒)
nonce授权请求传入的值(防重放)
auth_time用户实际认证时间(epoch 秒)
preferred_usernameprofile scope 时返回
pictureprofile scope 时返回(头像 URL)
email / email_verifiedemail scope 且用户绑定了邮箱时返回
phone_number / phone_number_verifiedphone scope 且用户绑定了手机时返回

客户端应使用 JWKS 中 id-v1 公钥验证 idToken 签名,并校验 iss、aud、exp、nonce。


7. 单点退出(SLO)#

7.1 SSO → 客户端(向下级联)#

以下场景会触发用户在所有 OAuth 客户端的会话失效(后端自动级联):

  1. 用户在第一方 POST /api/v1/auth/logout 登出
  2. 用户在第一方 POST /api/v1/user/password/change 修改密码
  3. 用户在授权列表中 POST /api/v1/oauth/revoke/{clientId} 撤销单个应用(仅该应用)

客户端收到 API 401 后应当视为会话已失效,引导用户重新授权。

7.2 客户端 → SSO(反向级联,仅 ADVANCED / INTERNAL)#

受信任 tier 的客户端可通过 POST /api/v1/oauth/logout(5.7)发起反向单点退出,效果等同于用户在第一方主动登出:

  • 撤销该用户在 SSO 端的所有第一方 access token
  • 级联撤销该用户在所有 OAuth 客户端的会话

STANDARD 客户端调用同一端点只会撤销本次 token,不会触发反向级联。

INTERNAL 客户端因获得的是第一方 access/refresh token,也可以直接调 POST /api/v1/auth/logout,效果一致。


8. 第三方快捷登录集成#

8.1 支持的平台#

tpo_provider平台
googleGoogle
githubGitHub
qqQQ 互联

8.2 集成示例#

<!-- 使用 GitHub 快捷登录 -->
<a href="https://account.axtrk.com/oauth/authorize
  ?clientId=YOUR_CLIENT_ID
  &redirect_uri=https://your-app.com/callback
  &state=random123
  &scope=openid+profile+email
  &code_challenge=YOUR_CHALLENGE
  &code_challenge_method=S256
  &tpo_provider=github">
  使用 GitHub 登录
</a>

8.3 FastUser 说明#

通过 tpo_provider 自动注册的用户为 FastUser:

  • 仅持有第三方平台信息,无邮箱/手机/密码
  • 可在 AxT 面板绑定邮箱或手机号升级为完整用户
  • 部分敏感功能对 FastUser 不可用

9. 错误处理#

HTTP常见场景
400参数缺失、code 无效、state/PKCE 校验失败、redirect_uri 不匹配
401clientSecret 错误、Token 失效、refreshToken 重放
403用户账户被封禁或未激活
429触发限流(登录失败过多 / 请求频次超限)

10. 安全建议#

  • 必须 为每次授权生成新的 state 和 nonce,并在回调校验
  • 强烈建议 使用 PKCE(S256),即使是 confidential 客户端
  • 必须 使用 HTTPS 承载所有端点
  • 必须 在服务端存储 clientSecret,不要在前端暴露
  • 建议 资源服务器通过 JWKS 离线验证签名,避免每次请求回调授权服务器
  • 检测到 refreshToken 重放时立即引导用户重新登录
0 次浏览
  • 1. 概述
  • 角色说明
  • 部署域划分
  • Token 有效期与签名
  • 支持的安全特性
  • 2. OIDC Discovery
  • 2.1 获取 OIDC 配置
  • 2.2 获取公钥集 (JWKS)
  • 3. 标准授权码流程(含 PKCE)
  • 4. 第三方快捷登录(tpo_provider)
  • 5. 详细接口
  • 5.1 授权端点
  • 5.2 Token 端点
  • 5.3 刷新 Token
  • 5.4 UserInfo 端点
  • 5.5 Token 撤销(RFC 7009)
  • 5.6 Token 内省(RFC 7662)
  • 5.7 结束会话(OIDC End Session)
  • 6. idToken 结构(OIDC)
  • 7. 单点退出(SLO)
  • 7.1 SSO → 客户端(向下级联)
  • 7.2 客户端 → SSO(反向级联,仅 ADVANCED / INTERNAL)
  • 8. 第三方快捷登录集成
  • 8.1 支持的平台
  • 8.2 集成示例
  • 8.3 FastUser 说明
  • 9. 错误处理
  • 10. 安全建议