跳转到内容

令牌与验证

查看 Markdown

Sudomimus 签发三种令牌,由不同的密钥签名、通过不同的机制验证。本页是令牌验证与字段内容的唯一参考 —— 每个 claim 的含义,以及决定字段取值的规则。

令牌 签发者 签名密钥 验证方式 携带的内容
Access token Connect、Session API、Native API 或 OIDC(/redeem、/refresh、/direct-issue/*、/token、/authorize) 应用当前生效的 token-signing 私钥 Session GET /applications/{applicationAnchor}/jwks.json 按 kid 选择 Payload iss、aud、sub、sid、jti、iat、exp;Workload 令牌另含 act.sub
Refresh token Connect、Session API、Native API 或 OIDC(/redeem、/refresh、/direct-issue/*、/token) 应用当前生效的 token-signing 私钥 与 access token 相同 Payload iss、aud、sid、jti、iat、exp、rotationVersion
OIDC ID token OIDC(/authorize、/token) 平台级 OIDC 签名密钥 oidc.sudomimus.com/.well-known/jwks.json sub、iss、aud、exp、iat、at_hash?、c_hash?、nonce?、auth_time?、amr?、acr?

三者都是用 RS256(RSA-2048)签名的 JWT。

这是你的应用后端日常打交道的两种令牌。两者都由 Sudomimus 用与你应用绑定的密钥对签发 —— 每个应用都有自己独立的一对,轮换一个应用的密钥不会影响其他应用。

应用使用 Session API 发布的专属 token-signing JWK Set 验签。推荐流程:

flowchart TD
    Receive["收到 JWT"] --> Parse["将内容作为不可信数据解析"]
    Parse --> Header{"alg、typ、aud 是否符合预期,<br/>kid 是否非空且 exp 有效?"}
    Header -->|否| Reject["拒绝"]
    Header -->|是| Cached{"缓存的应用 JWK Set<br/>是否包含该 kid?"}
    Cached -->|是| Verify["验证签名"]
    Cached -->|否| Refresh["从已配置的端点刷新一次<br/>应用 JWK Set"]
    Refresh --> Found{"现在能找到 kid 吗?"}
    Found -->|否| Reject
    Found -->|是| Verify
    Verify --> Valid{"签名有效?"}
    Valid -->|否| Reject
    Valid -->|是| Trust["信任已验证的 claims"]
  1. 请求 GET https://session-api.sudomimus.com/applications/{applicationAnchor}/jwks.json,并按响应的 Cache-Control 缓存。
  2. 收到 JWT 后先把内容视为不可信数据:要求 alg === "RS256"、typ 符合预期、aud === applicationAnchor、kid 非空且 exp 有效。
  3. 从 JWK Set 中精确选择与 header kid 相同的 JWK 并验签;只有验签通过后才能信任任何 claim。
  4. 如果找不到 kid,立即刷新一次 JWK Set 后再拒绝。不要根据不可信的 audience 拼接请求地址;应使用配置好的应用 anchor 与 Session API origin。

JWK Set 可能同时包含预发布的下一把密钥、当前签名密钥,以及仍需验证存量令牌的退役密钥。已撤销或超过保留期的密钥不会发布,这个重叠窗口保证轮换不会让仍在有效期内的令牌失去验签能力。

紧急撤销会立即从新的 Session JWKS 响应中移除退役密钥。已缓存该公钥的验证端可能在已公布缓存有效期的剩余时间内继续接受其签名,最长持续到上次成功获取后 300 秒。这种有界收敛是离线 JWT 验证的一部分;不要将撤销理解为对所有遵循缓存规则的验证端都会瞬时生效。当敏感操作需要当前的实时会话权限时,除了验签,还应使用 Session POST /introspect。

应用令牌 JWKS 是每个应用独立的,并与平台级 OIDC JWKS 分离。验证端只会获得配置的 applicationAnchor 所属密钥;轮换某个应用不会影响其他应用。

Sudomimus 的 access token / refresh token JWT 用 typ 明确令牌类型:

  • 账户访问令牌:typ: "vnd.sudomimus.application-access+jwt"
  • Agent 和 Automation(Workload)访问令牌:typ: "vnd.sudomimus.workload-access+jwt"
  • Refresh token:typ: "vnd.sudomimus.application-refresh+jwt"

当 typ 与端点预期的凭据类型不符时,应当直接拒绝。 只接受账户身份的端点必须拒绝 Workload 令牌,即使签名有效。 应用要接受 Agent 或 Automation,既要启用对应的认证方式, 也要实现 Workload 令牌验证。仅启用认证方式,不会让只支持账户令牌的验证器自动接受这些执行主体。

默认值 最小值 最大值
Access token 3 小时 (10800s) 60 秒 7 天 (604800s)
Refresh token 30 天 (2592000s) 1 天 (86400s) 365 天 (31536000s)

规则和单次请求上的 TTL 覆盖都受这套区间限制。各层规则与请求约束之间的匹配值如何折叠,以规则模型中的 Token TTL为准。

受保护 header 只包含 alg、kid、typ。标准 JWT claim 与会话绑定放在 payload 中。sub 是配对的扇区主体,也是应用应使用的用户键;sid 是稳定的逻辑 ApplicationSession id;jti 标识某一个 bearer 实例。应用令牌不携带个人资料或原始账户 id。

// JWT header
{
"alg": "RS256",
"kid": "<signing-key identifier>",
"typ": "vnd.sudomimus.application-access+jwt"
}
// JWT payload
{
"iss": "https://sudomimus.com",
"aud": "<applicationAnchor>",
"sub": "<sector subject>",
"sid": "<application session identifier>",
"jti": "<access token identifier>",
"iat": <epoch>,
"exp": <epoch>
}
Claim 含义
typ(header) 账户访问令牌使用 "vnd.sudomimus.application-access+jwt";Agent 或 Automation 访问令牌使用 "vnd.sudomimus.workload-access+jwt"。仅接受端点明确支持的类型。
act.sub 仅存在于 Workload 访问令牌中,标识 Agent 或 Automation 的配对扇区主体。act 只能包含 sub;账户令牌不得包含 act。
sub 应用可见的扇区主体(sector subject),即按(账户 × 扇区)生成的不透明标识符(例如 sub_9SQ5535CRWNDDM2T)。应用应使用该值标识用户。它在同一扇区内对同一用户保持稳定,但可以由用户轮换,并且在不同扇区之间互不相同。请将其视为不透明值,不要解析。
sid 逻辑 ApplicationSession 的稳定标识。同一次登录签发的 access/refresh token 在轮换期间共享它。它不是用户 id。
jti 这个 access-token 实例的唯一标识。它与 sid 不同,并在每次签发新 access token 时改变。
iss Sudomimus 的 HTTPS 应用令牌 issuer。
aud 签发本令牌的应用的 applicationAnchor。
iat、exp 标准 JWT 签发时间 / 过期时间,单位秒(epoch)。

上面的示例是账户访问令牌。Workload 令牌使用专用的 typ,并在 payload 中增加 "act": { "sub": "<workload sector subject>" }。顶层 sub 仍标识所属账户, act.sub 则标识该扇区内实际执行操作的 Agent 或 Automation。验证时必须检查 act 的字段, 并在应用授权时保留这两种身份;它们都不是原始账户或 Workload UUID。 身份认证和凭证适用范围不会授予应用内的业务权限。

Workload 会话与账户会话使用同一种刷新令牌。刷新令牌既不包含 sub,也不包含 act; 刷新时,服务端根据会话的当前有效权限签发对应类型的账户或 Workload 访问令牌。

Refresh token 使用同样的最小 header。它的 payload 不含 sub 或任何个人资料;sid、jti 与 rotationVersion 标识一个精确的已签名 refresh 版本。

// JWT header
{
"alg": "RS256",
"kid": "<signing-key identifier>",
"typ": "vnd.sudomimus.application-refresh+jwt"
}
// JWT payload
{
"iss": "https://sudomimus.com",
"aud": "<applicationAnchor>",
"sid": "<application session identifier>",
"jti": "<refresh token version identifier>",
"iat": <epoch>,
"exp": <epoch>,
"rotationVersion": 1
}

rotationVersion 是正整数,每次成功轮换加一。请把新返回的 refresh JWT 作为一个完整的不透明凭据保存;不要根据 sid、jti 或版本自行构造或修改令牌。 只有令牌对该应用有效且会话仍有效时,Sudomimus 才允许刷新。 轮换、并发刷新和令牌重用的行为见会话管理。

使用 Authorization: Bearer <accessToken> 调用 GET https://session-api.sudomimus.com/userinfo,获取当前经同意门控的个人资料。响应总是包含 sub,并可能包含 email、email_verified、name、given_name、family_name、picture 与私有 claim picture_animated。这些字段是实时、可替换的个人资料;用户主键只能使用 payload sub。

如果只需要实时的策略要求与同意状态、不需要资料值,请携带同一个 Bearer token 调用 GET https://session-api.sudomimus.com/claim-state。它使用 email、 given_name、family_name、picture 与 picture_animated 作为 claim key。

OIDC 根据所选响应类型和已授权的 scope,从 /authorize 或 /token 返回 ID Token。请使用 https://oidc.sudomimus.com/.well-known/jwks.json 验证。它由平台级 OIDC 签名密钥签发。

以下示例是 token 端点返回的 ID Token。字段遵循 OpenID Connect 标准:

{
"iss": "https://oidc.sudomimus.com",
"sub": "<sector subject>",
"aud": "<client_id>",
"exp": <epoch>,
"iat": <epoch>,
"at_hash": "<access-token hash>",
"nonce": "<来自 /authorize(如有)>",
"auth_time": <epoch(如有)>,
"amr": ["<认证方式>"],
"acr": "<认证上下文>"
}
Claim 含义
iss 总是 "https://oidc.sudomimus.com"。
sub 扇区主体 —— 与配对的 access token payload 中 sub 相同的(账户 × 扇区)值。在你所属扇区内对同一用户稳定,可轮换,且不同扇区之间不同。
aud 接入方的 OIDC client_id。
exp、iat 标准 JWT 时间字段,单位秒(epoch)。
nonce 首次签发时回显接入方在 /authorize 时传入的值。按 OIDC core 1.0 §12.1,refresh-token grant 不会再回显。
auth_time 用户实际完成认证的时间(秒)。refresh-token grant 会保留原值。
at_hash ID Token 与 access token 一同返回时携带,包括 /token 响应。请对照对应的 access token 验证。
c_hash 授权响应同时返回 code 和 ID Token 时携带。请对照对应的 code 验证。/token 返回的 ID Token 不携带它。
amr、acr 认证方式引用与认证上下文。

OIDC 签名密钥会定期轮换;JWKS 在轮换期间会同时发布当前生效和最近退役的两把密钥,保证验签连续可用。

授权响应中,id_token 不携带这两个哈希;id_token token 携带 at_hash;code id_token 携带 c_hash;code id_token token 两者都携带。前通道返回 ID Token 时,请求必须提供 nonce。仅返回授权码的请求可以省略它。

只有 response_type=id_token 会包含 scope、当前策略和用户授权允许披露的个人资料声明。其他 ID Token 不携带个人资料。已签发的 JWT 是快照;撤销授权不能收回已交付的数据。

OIDC /authorize 和 /token 按所选流程返回与普通应用流程相同的最小 access_token,不携带 scope 相关个人资料。应用使用每应用 Session JWKS 验签,并从 discovery 中的 OIDC /userinfo 获取 scope 控制的资料。

完整接入方流程(含 /userinfo 和 /end-session)见 OIDC 接入方。

Connect、OIDC、设备码授权与原生 direct-issue 的当前比较,以选择接入方式为准。本页负责这些路径共享的令牌与验证契约。

客户端签名密钥和 OIDC 高级响应见RP 密钥配置与轮换与OIDC 高级接入与能力。