跳转到内容

令牌与验证

查看 Markdown

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

令牌签发者签名密钥验证方式携带的内容
Access tokenConnect、Session API、Native API 或 OIDC(/redeem/refresh/direct-issue/*/token应用当前生效的 token-signing 私钥Session GET /applications/{applicationAnchor}/jwks.jsonkid 选择Payload issaudsubsidjtiiatexp
Refresh tokenConnect、Session API、Native API 或 OIDC(/redeem/refresh/direct-issue/*/token应用当前生效的 token-signing 私钥与 access token 相同Payload issaudsidjtiiatexprotationVersion
OIDC ID tokenOIDC(/token平台级 OIDC 签名密钥oidc.sudomimus.com/.well-known/jwks.jsonsubissaudexpiatat_hashnonce?auth_time?amr?acr?

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

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

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

  1. 请求 GET https://session-api.sudomimus.com/applications/{applicationAnchor}/jwks.json,并按响应的 Cache-Control 缓存。
  2. 收到 JWT 后先把内容视为不可信数据:要求 alg === "RS256"typ 符合预期、aud === applicationAnchorkid 非空且 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 明确令牌类型:

  • Access token:typ: "vnd.sudomimus.application-access+jwt"
  • Refresh token:typ: "vnd.sudomimus.application-refresh+jwt"

typ 与端点预期的凭据类型不符时,应当直接拒绝。

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

规则上和单次请求上的 TTL 覆盖都受这套区间限制。当多个 TTL 同时命中(例如一条 Layer 1 规则 + 一条 Layer 3 请求约束),Sudomimus 会取其中的最小值;如有必要,access TTL 会被缩短,确保不超过 refresh TTL。

受保护 header 只包含 algkidtyp。标准 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总是 "vnd.sudomimus.application-access+jwt"。任何不匹配的令牌都应拒绝。
sub应用可见的扇区主体(sector subject),即按(账户 × 扇区)生成的不透明标识符(例如 sub_9SQ5535CRWNDDM2T)。应用应使用该值标识用户。它在同一扇区内对同一用户保持稳定,但可以由用户轮换,并且在不同扇区之间互不相同。请将其视为不透明值,不要解析。
sid逻辑 ApplicationSession 的稳定标识。同一次登录签发的 access/refresh token 在轮换期间共享它。它不是用户 id。
jti这个 access-token 实例的唯一标识。它与 sid 不同,并在每次签发新 access token 时改变。
issSudomimus 的 HTTPS 应用令牌 issuer。
aud签发本令牌的应用的 applicationAnchor
iatexp标准 JWT 签发时间 / 过期时间,单位秒(epoch)。

Refresh token 使用同样的最小 header。它的 payload 不含 sub 或任何个人资料;sidjtirotationVersion 标识一个精确的已签名 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 作为一个完整的不透明凭据保存;不要根据 sidjti 或版本自行构造或修改令牌。 Sudomimus 会先验签,再按 sid 强一致读取 ApplicationSession,随后精确匹配 应用、jti 与版本,之后才会轮换;内部 session 会单独证明当前 subject authority。

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

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

当应用作为 OIDC 接入方 集成时,/token 端点会在 access_token 之外再返回一个 id_token。这个 ID token 由平台级 OIDC 签名密钥签发(不是应用专属密钥),通过 https://oidc.sudomimus.com/.well-known/jwks.json 上的 JWKS 验证。

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
expiat标准 JWT 时间字段,单位秒(epoch)。
nonce首次签发时回显接入方在 /authorize 时传入的值。按 OIDC core 1.0 §12.1refresh-token grant 不会再回显。
auth_time用户实际完成认证的时间(秒)。refresh-token grant 会保留原值。
at_hash把 ID token 与配对的 access token 绑定。
amracr认证方式引用与认证上下文。

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

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

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

  • Connect 协议 + Session API(access / refresh token 通过每应用 Session JWKS 验签):你的应用后端直接和 Sudomimus 对接。最低开销,无额外跳转。
  • OIDC(ID token 通过 JWKS 验签):你的应用已经接了某个标准 OIDC 库,或者你需要让第三方系统作为接入方。Sudomimus 此时扮演 IdP。

同一个应用通常只用其中一种 —— 没必要两种都接。