# KAI Auth — complete documentation Source: https://auth.kai.com/docs # KAI Auth 接入文档 从应用登记到令牌校验,用同一套 OAuth 2.0 / OpenID Connect 契约接入 KAI 账号。 ## 适用版本 本文对应 KAI Auth 当前实现,认证组件为 Better Auth / OAuth Provider **1.7.1**。页面随代码发布;如文档示例与目标环境的 Discovery 不一致,先核对环境和发布版本,不要自行放宽验证。 生产 issuer 为 `https://auth.kai.com`。协议端点位于 `/api/auth` 下;issuer 不包含该路径。浏览器、服务端和原生应用共用授权服务器,各应用登记并处理自己的回调地址。 ## 开始接入 1. 在[开发者控制台](/console)选择组织和项目,创建 Application,参阅[应用登记与回调](/docs/applications)。 2. 按[授权码与 PKCE](/docs/authorization-code)实现登录,始终验证 `state`、`nonce`、签名、issuer 和 audience。 3. 纯登录先使用 `openid profile email`,不发送 `resource`。调用业务 API 时阅读[资源与访问令牌](/docs/resources-and-tokens)。 4. 使用[OAuth 测试应用](/authtest)按实际授权流程检查连接。一次成功登录不等于所有授权、撤销和业务接口都已通过验收。 5. 高风险操作需要用户重新证明身份时,接入[额外身份验证](/docs/step-up)。应用只取得验证结果,不取得具体高等级认证方式。 使用终端或 Agent 配置应用时,参阅[Auth CLI](/docs/auth-cli)。其中 `oauth step-up` 用于应用侧额外验证,管理 `step-up` 则用于控制台敏感操作,两者的凭据不能互换。 ## 选择流程 | 应用 | 客户端类型 | 流程 | | ----------------------- | ------------ | ------------------------------------------------- | | 有安全服务端的网站 | confidential | Authorization Code + S256 PKCE,服务端保管 secret | | 浏览器 SPA、移动端 | public | Authorization Code + S256 PKCE,无共享 secret | | CLI、电视等输入受限设备 | 通常 public | Device Authorization,应用须启用 Device Code | KAI 控制台提供的应用以用户授权为核心,不能仅根据 Discovery 中列出的通用能力推断某个应用已启用相应 grant。 ## 机器可读入口 - [OIDC Discovery](/.well-known/openid-configuration)与[OAuth Discovery](/.well-known/oauth-authorization-server):运行环境的协议元数据。 - [OpenAPI](/openapi.json):KAI 自有管理 API 的生成契约;OAuth/OIDC 端点通过 Discovery 发现。 - [文档索引](/llms.txt)与[完整文档](/llms-full.txt):供 AI 工具读取。 - 每篇文章支持 `Accept: text/markdown`,也可直接打开路径后的 `/index.md`,例如 `/docs/authorization-code/index.md`。 文档正文在服务器生成,无需 JavaScript。HTML 与 Markdown 来自同一份内容;搜索和复制按钮只是阅读辅助。 --- Source: https://auth.kai.com/docs/applications # 应用登记与回调 在组织所属项目中登记 Application,明确客户端类型、登录方式和允许的回调。 ## 登记入口与权限 路径为开发者控制台 → 组织 → 项目 → Applications。管理 API 位于 `/api/organizations/:organizationId/projects/:projectId/applications`,使用 KAI 登录会话和服务端权限检查。组织 owner、admin 和 developer 可以查看组织内项目并创建、管理应用;developer 的应用管理权限不包含项目管理。组织 member 需要显式加入该项目:项目 admin 可以管理项目及应用,项目 member 只能查看;未加入项目则不可访问。写操作还要求组织处于可用状态。 普通 public 应用的 PKCE 登录受到支持,与动态注册是独立能力;Discovery 列出的全局认证方法不代替已登记客户端的配置。OAuth Dynamic Client Registration 当前关闭。不要向 `/api/auth/oauth2/register` 发送请求以绕过控制台登记。 ## 客户端配置 - **public**:不发放 client secret,令牌端点认证方式为 `none`。分发到浏览器、手机或 CLI 的共享 secret 无法保密。 - **confidential**:创建时返回一次 secret,令牌端点认证方式为 `client_secret_basic`。密钥保留在服务端,不能放入浏览器脚本、移动应用包或日志。 - 应用形态 `applicationKind` 与客户端类型分别登记:`web` 可以是 public 或 confidential;`native` 必须使用 public 和 PKCE,不能依赖分发到应用中的 secret。 - `authorization_code`、`device_code` 可单独或同时启用。浏览器授权至少登记一个 redirect URI;仅 Device Code 的应用可以不登记。 - 允许的 scope 为 `openid`、`profile`、`email`、`offline_access`,配置必须包含 `openid`。需要 refresh token 时申请并获准 `offline_access`。 - `requiredScopes` 是必须同意的权限,必须包含 `openid`;`optionalScopes` 由用户选择。两者不重复,合起来必须等于应用的 `scopes`。浏览器和 Device Code 授权都会展示这份策略,客户端必须处理用户未同意可选权限的情况。 - `members_only` 仅允许组织成员;`authenticated_users` 允许其他已登录用户。后者的 `organization_role` 可以是 `null`。 ## 授权页展示资料 在项目设置中上传、替换或移除项目头像,并配置该项目的服务条款和隐私政策链接。应用可以配置独立头像;未设置时使用项目头像。图片会在浏览器裁剪压缩为 JPEG,后端再次检查图片格式与大小后保存。各应用还可以为已启用的 `openid`、`profile`、`email`、`offline_access` 分别填写用途,每项最多 500 字;用途不会增加应用能够申请的权限。 授权页展示应用名称、提供服务的组织名称,以及组织的「已验证」或「未验证」标记。KYB 只由平台管理员核验或撤销,组织或应用开发者不能自行标记。修改已验证的组织名称会自动使其变为未验证;该标记表示主体核验状态,不保证应用内容或行为。 项目政策链接改变时会增加政策版本。如果链接没有改变而政策正文更新,请在项目设置勾选发布政策更新;系统会检查当前版本,避免覆盖其他管理员的更新。 ## 再次登录与自动登录 首次授权需要用户明确确认。再次登录时,授权页比较用户上次确认的资料,展示应用、组织、项目、头像、回调地址、资源、必需及可选权限、权限用途和政策版本的变化。已有的旧授权没有展示资料快照,会要求重新确认,不会默认开启自动登录。 用户勾选「下次自动登录」并完成授权后,之后相同资料的登录会展示五秒倒计时。取消倒计时会恢复拒绝与授权按钮;查看详情、修改勾选或离开可见页面也会停止倒计时。变化后的资料必须再次确认。用户可以在个人中心的已授权应用中关闭自动登录,或撤销整个应用授权。 自动登录偏好绑定当前账号与实际授权;撤销授权不会被旧偏好恢复。授权提交使用绑定账号、会话、完整签名请求和展示资料的一次性短期凭证,由服务端检查等待时间及最新配置。客户端仍只需要使用标准授权请求和已登记回调,不应自行调用 Auth 的页面确认接口。`prompt=consent` 始终要求确认;`prompt=none` 保持静默语义,无法完成时返回 OAuth/OIDC 错误,不会打开页面或自动扩大权限。 ## 回调白名单 浏览器授权请求的 `redirect_uri` 必须匹配该应用登记的地址。使用明确的完整地址,不以任意 URL、域名通配或 `returnTo` 代替登记。Web 应用使用非 loopback 主机的 HTTPS 地址;原生应用另外支持 loopback HTTP 和应用拥有的反向域名 scheme。回调不能包含用户凭据、片段、通配符、空白或控制字符。 示例: ```text redirect_uri = https://app.example.com/oauth/callback post_logout_redirect_uri = https://app.example.com/signed-out ``` 登录回调和退出后的地址分别登记,不能相互替代。每种回调地址最多登记 10 个。应用自己完成回调校验、换取令牌和建立应用会话,Auth 不负责把任意回调转换成不同终端能识别的链接。 ## 移动端 在控制台选择原生应用和 public 客户端,可以登记开发者拥有的 HTTPS 回调,按操作系统规范把已验证链接交给应用处理;也可以登记 `com.example.app:/oauth/callback` 形式的反向域名 scheme,或 `http://127.0.0.1:端口/路径`、`http://localhost:端口/路径`、`http://[::1]:端口/路径` 形式的 loopback 回调。请求必须使用登记的回调及 PKCE;原生 loopback 回调允许临时端口变化,scheme、主机与路径仍须匹配。`kai://oauth/callback` 这类短 scheme 不属于新的登记契约。 `https://auth.kai.com/mobile/callback` 是旧 KAI App 的临时兼容端点,**已弃用**,最终转发到固定 `kai://oauth/callback`,不是通用移动端回调服务。尚未安排删除日期,新应用不要依赖它。 该端点只转发 OAuth 响应字段;缺少或重复必要字段会返回 400,裸地址也不是可启动的登录流程。它不会授权任何 Resource。 ## 配置变更 修改必需或可选 scope 会撤销该应用已有的授权和待处理 Device Code,用户需要重新授权。用户缩减已同意的权限时,超出新范围的 access token 和 refresh token 会被撤销。 移动 Application 到另一个项目会保留 client ID 和 secret,但撤销现有 consent、待兑换授权码、access token 和 refresh token;用户需要重新授权,才能获得新的 `project_id`。离线验证的 JWT 可能在剩余有效期内继续被接受,详见[令牌校验与 claims](/docs/claims)。 ## 控制台工作区 左上角工作区选择器列出已加入的组织及对应身份。没有组织时,可创建组织或查看发送至已验证邮箱的待接收邀请。平台管理员可另外查看全平台组织目录;平台身份不等于组织成员身份,也不自动授予项目访问权。 组织内先选择项目,再进入应用。组织概览和项目概览用于定位工作,名称、品牌、头像及协议在各自的设置页面维护。应用配置分为登录设置、数据与权限、客户端凭据、测试与诊断四个选项卡;项目头像和协议由项目内应用共享,应用仍可配置自己的头像和每项 scope 的用途。 在测试与诊断中打开 OAuth 测试页,会预填公开 Client ID 和客户端类型。私密 Web 的密钥需要在测试页输入,不会写入 URL。测试页要求的回调地址必须先加入应用的回调白名单。API 资源与应用的授权关系在项目的 API 资源页面管理,详见[资源、scope 与访问令牌](/docs/resources-and-tokens)。 停用应用会阻止新授权并撤销已有授权;重新启用后需要重新授权。修改设置后请保存;跨组织、项目或应用离开有未保存修改的页面时,控制台会要求确认。 --- Source: https://auth.kai.com/docs/authorization-code # 授权码与 PKCE 通过浏览器完成用户登录和授权,再以一次性授权码换取令牌。 ## 发现服务 读取 `https://auth.kai.com/.well-known/openid-configuration`,要求返回的 `issuer` 与配置的 `https://auth.kai.com` 完全一致。使用文档返回的 authorization、token、JWKS 和 UserInfo URL,不猜测 `/api` 路径。 ## 创建授权事务 每次登录生成高熵随机 `state`、`nonce` 和 PKCE `code_verifier`。`code_challenge` 是 verifier 的 SHA-256 摘要经 base64url 编码,`code_challenge_method` 固定 `S256`。将事务保存到当前浏览器或服务端会话,设置短有效期,消费后立即删除。 ```text GET /api/auth/oauth2/authorize ?client_id=YOUR_CLIENT_ID &response_type=code &redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fcallback &scope=openid%20profile%20email &state=RANDOM_STATE &nonce=RANDOM_NONCE &code_challenge=BASE64URL_SHA256_VERIFIER &code_challenge_method=S256 ``` 这是纯登录示例,故不携带 `resource`。若要访问资源服务器,在用户授权阶段发送已获准的准确 resource identifier,并在兑换时保持该授权目标,不能在拿到 code 后扩展资源或 scope。 ## 要求重新验证 确实需要用户重新证明身份时使用标准 `prompt=login`,或用 `max_age` 限定最近认证时间。Auth 对已有登录会话提供独立的重新验证与取消流程;完成密码、OTP、Passkey 或 Lark 所需的全部验证后,原地更新会话证据和 `auth_time`,保留 SID 与已有应用授权,不额外创建会话。普通登录不应无条件强制重新验证。 `prompt=none` 保持静默语义,无法满足条件时返回协议错误,不打开交互页面。账号安全设置改变导致的令牌撤销与普通重新验证不同,见[令牌校验与 claims](/docs/claims)。 ## 高风险操作的额外验证 应用需要在高风险操作前再次确认当前用户时,创建新的授权码事务,在原有 `openid`、`state`、`nonce`、PKCE 参数之外添加: ```text acr_values=urn:kai:acr:step-up ``` 这个值请求 KAI 的额外验证策略,不指定验证方式,也不代表某个统一的 MFA 等级。KAI 按账号已配置的安全方式选择流程:只要配置了高等级安全因子就不能退回密码;账号没有配置这类因子时才允许密码。设备暂不可用、验证失败或被限流不会解除该限制。没有可用验证方式时,用户需要先解决账号访问问题。 已登录用户在独立页面完成验证,过程保留当前浏览器会话。普通登录本身不能替代这次额外验证。验证凭据绑定本次应用、用户、浏览器会话和完整授权事务,只能签发一次授权码;其他应用或另一笔 `state` / `nonce` 事务不能借用。可同时使用 `prompt=login` 或 `max_age`,整个恢复与授权流程保留其绑定,不要求用户反复登录。 回调仍使用应用登记的 `redirect_uri`,仍须完成标准授权码兑换和 ID token 校验。随后额外检查: - `acr` 必须等于 `urn:kai:acr:step-up`; - `kai_step_up.status` 为 `verified` 时表示已完成高等级额外验证,为 `password_only` 时只表示完成密码验证;由业务方决定密码结果是否足够; - `auth_time` 是这次证明的秒级时间,业务方应检查自己的新鲜度窗口,并确认 `iss` / `sub` 与当前业务账号一致。 应用不会获得具体因子、设备或 `amr` 明细。用户主动取消时返回标准 OAuth 错误;关闭页面、断网或超时可能没有回调,应用应按未完成处理。这些情况都不会签发表示成功的 ID token。`prompt=none` 在缺少本次已完成证明时返回 `interaction_required`,未登录时返回 `login_required`。 刷新令牌不会执行新验证,也不会更新这次结果的 `auth_time`。需要更新证明时必须重新发起上述授权事务。此结果只证明身份验证完成,不等于用户批准了转账、修改资料等具体业务操作;业务操作及其参数仍由应用自行确认和授权。 完整请求示例、三种结果、字段类型、失败处理和 CLI 边界见[额外身份验证](/docs/step-up)。 ## 接收回调 只在本应用登记的回调接收响应。拒绝缺失、重复、不匹配或过期的 `state`;检查授权响应中的 `iss` 与预期 issuer 一致。处理 `error`(例如用户拒绝)时也验证事务。不要把 code、令牌或完整回调 URL 发送到日志或分析服务。 验证成功后立即消费事务,用与授权请求完全相同的 `redirect_uri` 和原 `code_verifier` 兑换 code。授权码只可使用一次,失败后重新发起登录。 ## 兑换令牌 向 Discovery 的 `token_endpoint` POST `application/x-www-form-urlencoded`: ```text grant_type=authorization_code client_id=YOUR_CLIENT_ID code=AUTHORIZATION_CODE redirect_uri=https://app.example.com/oauth/callback code_verifier=ORIGINAL_VERIFIER ``` public 客户端不发送 secret;confidential 客户端另外使用 HTTP Basic 认证。不要在前端复制 confidential secret,也不要为了方便测试改变现有应用的客户端类型。 响应包含 `access_token`、`token_type`、`expires_in`;有效 scope 含 `openid` 时提供 ID token,允许 refresh grant 且获准 `offline_access` 时提供 refresh token。检查实际返回的 `scope`,资源策略可能缩小权限。 ## 验证身份 用 JWKS 验证 ID token 签名,检查 `iss`、`aud`(本应用的 client ID)、`exp`、`nonce`,必要时按 OIDC 规范检查 `azp` 和 `at_hash`。仅解码 JWT 不等于验证。使用经过验证的 `iss` 与 `sub` 识别账号,不以邮箱作为永久主键。 向 UserInfo 发送 `Authorization: Bearer ACCESS_TOKEN`。其 `sub` 必须与 ID token 一致。`profile`、`email` 控制可读的用户信息,不保证 ID token 内包含所有资料字段。 ## 刷新与退出 refresh token 只在用户已授权 `offline_access` 时使用;客户端需要支持刷新轮换,成功后使用新返回的 refresh token。不要并行刷新同一授权链,也不要把原始 refresh token 显示在测试报告中。 退出应用和退出 Auth 会话是不同动作。跨应用退出、令牌撤销及回跳请按[退出与授权撤销](/docs/logout)实现。 --- Source: https://auth.kai.com/docs/step-up # 额外身份验证 应用在高风险操作前将用户带回 KAI Auth 完成一次新的身份验证,再通过标准 OAuth 回调和签名令牌取得验证结果。 ## 应用能看到什么 应用只能取得验证结果和本次验证时间,不会取得用户实际采用的高等级认证方式。 | 结果 | 对外表示 | 应用应如何处理 | | ------------------ | ------------------------------------- | ---------------------------------------------- | | 未完成认证 | OAuth 错误,或始终没有收到有效回调 | 不执行受保护操作;允许用户重新发起 | | 仅密码验证完成 | `kai_step_up.status: "password_only"` | 仅在业务明确允许密码结果时继续 | | 高等级身份认证完成 | `kai_step_up.status: "verified"` | 完成令牌、用户和时间校验后,由业务判断是否继续 | 当前版本支持 TOTP 和 Passkey,其成功结果均为 `verified`;手机批准不在本次发布范围内。不会通过 `amr`、`method`、设备字段或不同的 `acr` 向应用区分实际方式。KAI 内部保留验证证据,用于正确执行策略、撤销和审计。 应用不能指定用户必须采用哪一种方式。KAI 根据账号当前配置提供验证选项:存在已配置的高等级方式时,只能使用这些方式;只有没有配置高等级方式且已有密码时,才允许额外密码验证。设备离线、浏览器不支持某种方式、验证失败或限流,都不会自动开放密码降级。账号没有可用方式时,本次验证无法完成。 `verified` 是 KAI 的结果分类,不单独承诺某个 NIST 等级、抗钓鱼能力、硬件保护能力或具体多因素组合。业务如果只接受高等级结果,应明确要求返回值为 `verified`。 ## 发起请求 在已有业务登录会话下,创建一笔新的 Authorization Code + S256 PKCE 事务。保存这次事务的随机 `state`、`nonce`、PKCE verifier、预期用户的 `iss` / `sub` 和过期时间;不要复用首次登录的事务。 读取根路径 Discovery 的 `authorization_endpoint`,添加唯一的额外验证策略值: ```text GET /api/auth/oauth2/authorize ?client_id=YOUR_CLIENT_ID &response_type=code &redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fcallback &scope=openid &state=NEW_RANDOM_STATE &nonce=NEW_RANDOM_NONCE &code_challenge=BASE64URL_SHA256_NEW_VERIFIER &code_challenge_method=S256 &acr_values=urn%3Akai%3Aacr%3Astep-up ``` `openid` 必须包含在 scope 中,请求也必须包含该应用登记的全部 `requiredScopes`。上例适用于仅将 `openid` 设为必选权限的应用;若另有必选 `profile` 或 `email`,也要一并请求,不需要的可选权限无需加入。`redirect_uri` 仍须是该应用登记的地址,public 客户端使用 PKCE,confidential 客户端在兑换令牌时按登记方式认证。需要业务 API 时另行申请对应 scope / resource,并遵守现有资源授权规则;额外验证本身不会扩展权限。 `acr_values=urn:kai:acr:step-up` 请求的是一次新的 KAI 额外验证。普通登录、已有高等级会话、`prompt=login` 或 `max_age` 本身都不能替代这笔请求的额外验证证明。可以同时使用 `prompt=login` 或 `max_age`;KAI 会保留原始请求绑定并完成恢复流程。 当前只支持上述单一 `acr_values` 值触发额外验证,不支持用 `claims.id_token.acr` 的 essential 请求代替,也不要拼接多个 ACR 值或具体因子名称。 已有 KAI 登录会话的用户进入独立验证页面,完成后沿原授权流程返回应用。验证保留当前会话,不额外创建一个登录会话,也不会更新原会话的认证时间。本次结果的 `auth_time` 独立记录实际证明完成时间。 验证交互从发起起最长有效十分钟;当前会话、原始签名请求或安全状态先失效时,交互也会提前失效。完成证明不会延长该期限。`max_age` 不能替代应用对最终证明时间的校验;应用应使用返回的 `auth_time` 检查自己的操作时效。 ## 接收与校验结果 回调只返回标准 OAuth 授权码或错误。不要以 URL 中自行添加的 `verified=true`、页面提示或浏览器跳转成功作为验证证明。 1. 校验回调 `state`、issuer 和本地事务有效期;有 `error` 时结束本次流程。 2. 使用原始 `redirect_uri` 和 PKCE verifier,向 Discovery 的 `token_endpoint` 兑换一次性授权码。 3. 按 [ID token 校验规则](/docs/authorization-code#验证身份)验证签名、算法、`iss`、`aud`、`exp` 和本次 `nonce`。仅解码 JWT 不构成验证。 4. 核对令牌的 `iss` / `sub` 与发起操作时保存的业务账号完全一致。用户换了账号时,不能给原账号的操作放行。 5. 校验下面三个额外验证字段,并按业务规定的新鲜度窗口判断 `auth_time`。 ```json { "acr": "urn:kai:acr:step-up", "auth_time": 1790000000, "kai_step_up": { "status": "verified" } } ``` | 字段 | JSON 类型 | 必须检查的内容 | | -------------------- | --------------------------------- | ------------------------------------------------------------ | | `acr` | string | 必须为 `urn:kai:acr:step-up` | | `auth_time` | number,非负整数 | 本次实际验证的秒级 Unix 时间;符合业务的新鲜度和时钟偏差要求 | | `kai_step_up` | object | 必须存在,不能用普通登录结果代替 | | `kai_step_up.status` | `"verified"` 或 `"password_only"` | 高等级要求只接受 `verified`;缺失或未知值不得视为成功 | 这些字段出现在本次授权的 ID token、适用的 JWT access token,以及活跃 access token 的 Introspection 结果中。JWT 的查询结果保留令牌中的声明,opaque token 的查询结果从原授权记录读取。refresh token 自身的查询结果不提供此证明;UserInfo 用于用户资料查询,也不作为额外验证结果接口。详细令牌适用范围见 [claims](/docs/claims)。 业务应把通过校验的结果关联到原来的待处理操作,限制其有效时间和使用次数。身份验证结果不等于用户批准了某笔转账、金额或其他业务参数;这些内容仍须由应用展示、确认和授权。 ## 未完成、取消与重试 | 情形 | 预期行为 | | ---------------------------------- | -------------------------------------------------------------------- | | 用户主动取消 | 返回已登记的回调,携带 OAuth 错误,不产生成功验证证明 | | 用户关闭页面、断网或事务过期 | 可能没有回调;应用应结束等待并按未完成处理 | | `prompt=none`,用户未登录 | 返回 `login_required`,不弹出交互页面 | | `prompt=none`,缺少本次有效证明 | 返回 `interaction_required`,不能把旧会话当作新证明 | | 过程中安全配置、会话或权限发生变化 | 旧流程可能失效,重新发起验证;不要自动改用密码 | | 授权提交遇到临时并发冲突 | KAI 页面要求重新检查或重试;应用只有拿到并校验最终成功结果后才能继续 | 本次证明只属于原始应用、用户、浏览器会话和授权事务。改变 `state`、`nonce`、PKCE、resource 或回调等参数,不能借用旧证明。授权码只能消费一次,证明也不能用于签发第二个授权码。 ## 刷新与其他登录流程 刷新令牌保留原验证结果和原 `auth_time`,不会执行新的验证,也不会延长验证证明的新鲜度。需要更新证明时,应新建一笔额外验证事务。后来在同一浏览器完成验证,不会把以前的普通授权或 Device 授权升级为 `verified`。 [Device Authorization](/docs/device-authorization) 用于 CLI 等设备取得登录授权;目前不能通过给 Device 请求添加 `acr_values` 来获得本页的额外验证结果。[Auth CLI](/docs/auth-cli#应用侧的额外身份验证) 使用独立的 `oauth step-up` 命令发起本页的授权码流程,并执行相同的回调、签名、用户和时间校验。 `/oauth/step-up` 页面与 `/api/oauth/step-up/*` 是 KAI 自己的浏览器交互入口,使用当前浏览器会话和同源保护。第三方应用不得拿 OAuth access token 调用这些接口、抓取 Cookie 或代替用户提交验证凭据;第三方集成入口始终是 Discovery 中的授权端点。 --- Source: https://auth.kai.com/docs/resources-and-tokens # 资源、scope 与访问令牌 resource 指定令牌将发给哪个资源服务器;它不是 issuer、client ID 或登录开关。 ## 纯登录是否需要 resource 不需要。使用 `openid profile email` 完成 OIDC 登录时可省略 `resource`,当前实现通常返回 opaque access token。此令牌可用于 UserInfo;不要把它当作任意业务 API 的通行证。 如果调用 Exchange 等资源服务器,应使用该 API 正式约定的 identifier,例如 `https://exchange.kai.com`。不能为了获得 JWT 把目标换成 Auth issuer。JWT 的 `iss` 表示谁发放,`aud` 表示令牌给谁使用,这两个值承担不同职责。 ## 请求资源的条件 当前 OAuth Provider 1.7.1 使用通用资源模型: 1. identifier 必须是有效的绝对 URI,不能含 fragment,且对应已注册、未禁用的资源。 2. 发起授权的客户端必须获准使用每个请求的资源。资源存在并不代表所有客户端自动有权使用。 3. 请求的 scope 必须先满足客户端范围,再与每个资源的 `allowedScopes` 依次取交集。未限制资源范围时不收缩;交集为空返回 `invalid_scope`。 4. 资源必须在用户批准的授权事务内;兑换或刷新不能擅自扩大目标。 未注册、已禁用或客户端未关联的资源会返回 `invalid_target`。 ## 在控制台注册资源 进入组织 → 项目 → **API 资源**,创建资源并选择该项目内获准调用它的应用。可配置名称、HTTPS identifier、允许的 scope、访问令牌有效期(30–300 秒)及启用状态。identifier 全局唯一且创建后不可修改,不得含 fragment;当前可选范围为 `openid`、`profile`、`email`、`offline_access`,暂不支持自定义业务 scope。 资源属于项目。组织所有者、管理员、开发者及该项目管理员可以维护,普通项目成员仅查看。服务端与数据库共同限制资源只能授权给所属项目内的应用;客户端在授权请求里仍须传入所需的 `resource`,不会因为创建资源而自动改变授权请求。 移除应用关联、停用资源、缩小 scope 或改变令牌有效期会撤销受影响授权及其后续凭据。移出项目的应用会失去原项目的资源关联;删除项目也会删除其自助注册的资源。重新启用或重新关联不会复活已撤销凭据,需要重新授权。只修改显示名称不撤销授权。 上线前由平台登记的资源继续按原有授权关系运行;它们没有可信的项目归属时,不会被自动认领或硬编码绑定给某个 Client ID。遇到 identifier 已存在,请先由平台核对归属,不能通过重复创建绕过资源授权。 ## JWT audience 合法资源请求在启用 JWT 插件的当前实现中获得 JWT access token。请求包含 `openid` 时,audience 还会包含 Auth 的 UserInfo 地址: ```json { "iss": "https://auth.kai.com", "aud": [ "https://exchange.kai.com", "https://auth.kai.com/api/auth/oauth2/userinfo" ], "scope": "openid profile email" } ``` 示例假设资源已注册、启用、允许这些 scope,并已授权给该客户端。`aud` 可为字符串或字符串数组。资源服务器检查自己的 identifier 是否属于 audience;不能要求 audience 只能等于 Auth issuer,也不能仅检查签名后忽略 audience。 UserInfo URL 是协议内置资源,不代表 Auth issuer 本身或其他 API 自动得到授权。不要使用内置资源作为绕开业务资源授权的方式。 ## 有效期与校验 当前用户 access token 和 ID token 默认有效期均为 300 秒;资源策略可以进一步缩短 access token 生命周期。客户端使用实际响应的 `expires_in` / JWT `exp`,不假定固定寿命。 opaque token 无法通过本地 JWT 解码验证,需要调用 Introspection。鉴权成功的调用方还须是原发放客户端,或获准服务该令牌 audience 的资源服务器客户端;不相关客户端得到 `active: false`。没有资源 audience 的 opaque token 不能由任意其他客户端内省。 JWT 支持离线验签,但不会实时感知撤销状态。Auth 的在线 UserInfo 和 Introspection 会核对令牌所属的原始授权、授权版本及来源会话的认证修订号;撤销授权、缩减权限或修改账号安全设置后,旧令牌不能仅凭签名继续通过在线检查。恢复权限或重新授权不会复活旧 JWT。 对撤销时效敏感的资源服务器应使用获准的客户端认证调用 Introspection,并结合业务权限判断。仅做离线验证的 API 仍可能在 `exp` 到期前接受已撤销令牌。五个内部凭据字段的精确类型、刷新限制及升级时旧令牌的处理见[令牌校验与 claims](/docs/claims)。 --- Source: https://auth.kai.com/docs/claims # 令牌校验与 claims 区分 ID token、access token、令牌响应和 Introspection,并按实际 JSON 类型读取组织权限。 ## 五个 KAI 扩展字段 | 字段 | JSON 类型 | 含义 | | ------------------- | ------------------------------------------------------- | ------------------------------------ | | `organization_id` | string | 应用所属组织 ID | | `project_id` | string | 应用所属项目 ID | | `organization_role` | `"owner"`、`"admin"`、`"developer"`、`"member"` 或 null | 用户在应用所属组织中的角色 | | `access_mode` | `"members_only"` 或 `"authenticated_users"` | 应用准入方式 | | `access_version` | number(整数) | 组织访问策略版本;不是字符串或布尔值 | 这五个字段由组织与项目归属、当前成员关系和应用 metadata 计算,出现在成功令牌响应、ID token、JWT access token,以及适用的活跃 Introspection 结果中。无效令牌可能只返回 `{"active": false}`,客户端不能要求错误响应也带齐这些字段。 `authenticated_users` 模式下非成员可以合法登录,`organization_role: null` 是有效值。不要为了通过解析把它改成 `member`。扩展字段描述应用所属租户,不意味着用户有权访问其他租户或任意管理 API。 ## JWT 授权与认证凭据 资源 JWT access token 还包含五个由 Auth 维护的内部声明: | 字段 | JSON 类型 | 含义 | | --------------------- | ------------------ | --------------------------------------- | | `kai_consent_id` | string(UUID) | 本次令牌所属的原始用户授权 | | `kai_consent_version` | number(非负整数) | 授权版本;缩减 scope 或 resource 时递增 | | `kai_auth_revision` | number(非负整数) | 签发时账号的密码认证修订号 | | `kai_factor_revision` | number(非负整数) | 签发时账号的安全因子修订号 | | `kai_policy_revision` | number(非负整数) | 签发时账号的二步验证策略修订号 | 这些字段用于 Auth 的在线校验,不替代标准签名、issuer、audience 和 scope 检查,也不是资源服务器自行授予权限的依据。它们不承诺出现在 ID token 或普通登录资料中。 ## 标准字段 | 字段 | 类型与检查 | | ------------------------- | ------------------------------------------------------------------ | | `iss` | string;必须等于配置的 issuer | | `sub` | string;用户稳定标识,与已验证 issuer 一起使用 | | `aud` | string 或 string[];ID token 面向 client ID,access token 面向资源 | | `exp`、`iat`、`auth_time` | number;秒级 Unix 时间,auth_time 仅在适用时出现 | | `scope` | access token / Introspection 中空格分隔的 string | | `nonce` | ID token 中与本次授权事务一致的 string | | `sid` | 适用时的会话标识,用于退出关联 | | `client_id`、`azp` | access token / Introspection 中的发放客户端标识 | JWT access token 使用 `typ: at+jwt`。API 不得接受 ID token 代替 access token。使用受信任 Discovery 的 `jwks_uri` 验证签名,并检查算法、有效期、issuer、audience 和所需 scope;不从未验证 token 中选择任意远端密钥地址。 ## 额外验证结果 使用 `acr_values=urn:kai:acr:step-up` 并完成额外验证后,ID token、适用的 JWT access token 及活跃 access token 的 Introspection 结果包含以下公开字段(不包含 refresh token 自身的 Introspection): ```json { "acr": "urn:kai:acr:step-up", "auth_time": 1790000000, "kai_step_up": { "status": "verified" } } ``` `kai_step_up.status` 只有 `verified` 和 `password_only` 两个成功值。前者表示完成高等级额外验证,后者只表示完成密码验证;两者不能混用。主动取消通过 OAuth 错误返回;关闭页面或超时可能没有回调,应用应按未完成处理,不存在“未完成但成功发放的证明”。不会对外发放此次验证的具体方式、设备或 `amr`。`acr` 标识 KAI 策略,不表示应用选择了某个具体因子,也不应解读为统一的 MFA 等级。 `acr` 是 string,`auth_time` 是非负整数,单位为 Unix 秒;`kai_step_up` 是 object,其 `status` 是上述两个字符串值之一。缺失字段、未知状态或不同 `acr` 都不能当作成功的额外验证。业务还须检查验证时间与当前业务账号的 `iss` / `sub`,不能仅检查 status。 | 返回位置 | 是否提供额外验证结果 | | ---------------------------------- | ----------------------------------------------------- | | 本次授权的 ID token | 是,先验证签名和标准 OIDC 声明 | | 本次授权的 JWT access token | 是,资源服务器仍需验证自己的 audience 和 scope | | 活跃 access token 的 Introspection | 是;JWT 保留已校验声明,opaque token 从原授权记录读取 | | Token endpoint 响应的 JSON 顶层 | 否,结果位于令牌内 | | refresh token 自身的 Introspection | 否 | | UserInfo | 否,用于读取已授权的用户资料 | 这些字段来自授权码签发时冻结的证明,refresh 保留原结果与原 `auth_time`。之后浏览器进行其他验证不会升级此前的授权链。普通 OAuth / Device 授权不会因同一个账号做过额外验证就附带成功结果。UserInfo 不提供额外验证结果,不能代替对签名证明的校验。完整请求、回调与结果校验见[额外身份验证](/docs/step-up)。 ## 资料字段与 UserInfo `openid` 提供 OIDC 身份语义,`profile` 和 `email` 控制 UserInfo 可见的资料。客户端以实际返回结果和 scope 为准,不假定 ID token 含 `name`、`picture` 或 `email`。UserInfo 的 `sub` 必须和 ID token 一致。 `email` scope 或显式 claims 请求必须授权对应字段,UserInfo 才会返回已验证的 `email`(string)或 `email_verified`(boolean)。单独请求其中一个字段不会自动获得另一个。仅手机号注册的账号没有已验证邮箱,因此这两个声明会缺省;即使请求了 `email` scope,也不能假定它们存在。Auth 自有的 `/api/me` 与原生会话接口使用 `user.email: null`、`emailVerified: false` 表示这种情况。接入应用应以 issuer 和 `sub` 标识用户。 ## 撤销与策略更新 组织暂停后阻止新的访问与令牌操作;恢复组织不会恢复旧 consent 或授权链。成员或应用归属变化也可能使已有授权失效。离线 JWT 验证只观察签名与声明,已签发 JWT 在其剩余有效期内仍可能有效,当前默认上限为五分钟。 Auth 的 UserInfo 和 Introspection 会检查原始 consent、授权版本、scope、audience 和当前来源会话的三项认证修订号。删除 consent、缩减权限,或撤销来源会话后,旧资源 JWT 在这些在线端点失效;重新授权或恢复已删除的权限不会复活旧令牌。 修改密码、安全因子或二步验证策略会撤销已有授权码、Device Code 授权、access token 和 refresh token,包括经验证后保留当前会话的个人安全操作。因子或策略修改保留 consent 与自动登录偏好,但应用需要重新发起授权获取新令牌;密码修改还会清除 consent。普通 RP 重新验证不改变这些修订号,不撤销其他应用的授权。 部署此凭据契约后,未携带上述完整声明的旧资源 JWT 会被在线 UserInfo 和 Introspection 拒绝。若原刷新授权链仍有效,可刷新获得新令牌,否则重新授权。升级不会因此删除账号或浏览器会话;离线验证仍可能接受旧 JWT 直到原 `exp` 到期。 令牌里的 `access_version` 是发行时的策略版本,不会自动更新。若资源服务器要求即时撤销,必须设计在线状态或版本检查,不能只缓存该字段并宣称实时撤销。授权撤销和退出行为见[退出与授权撤销](/docs/logout)。 --- Source: https://auth.kai.com/docs/device-authorization # Device Authorization CLI、电视和其他输入受限设备通过用户在独立浏览器确认,获得 OAuth 令牌。 ## 启用与发起 在 Application 中启用 Device Code。分发型客户端通常选择 public;只有能够安全保存密钥的服务端才使用 confidential。Device-only 应用不需要 redirect URI。 从 Discovery 读取 `device_authorization_endpoint`,当前为 `/api/auth/device/code`。public 客户端提交 `client_id`、所需 `scope`,必要时提交获准的 `resource`;confidential 客户端同时按登记的 token endpoint 方式认证。 ```text POST /api/auth/device/code Content-Type: application/x-www-form-urlencoded client_id=YOUR_CLIENT_ID&scope=openid%20profile%20email ``` 响应包含 `device_code`、`user_code`、`verification_uri`、`expires_in` 和 `interval`。只向用户展示 verification URL 和 user code,保护 device code。 ## 用户确认 用户访问 `/device` 后选择账号、核对设备上显示的 code,明确批准或拒绝。批准会创建和浏览器授权同样可见、可撤销的 OAuth authorization。不要自动替用户点击批准,也不要用一等会话 token 替代 OAuth token。 ## 轮询令牌 向 Discovery 的 `token_endpoint` 发送: ```text grant_type=urn:ietf:params:oauth:grant-type:device_code client_id=YOUR_CLIENT_ID device_code=DEVICE_CODE ``` 首次和后续轮询遵守返回的 `interval`。遇到 `authorization_pending` 继续等待;遇到 `slow_down`,将之后的轮询间隔增加五秒。遇到拒绝、过期或其他错误停止轮询;不要无限重试。 成功后按[令牌校验](/docs/claims)验证结果。KAI 有意关闭 `/api/auth/device/token`,本流程使用 `/api/auth/oauth2/token` 发放 OAuth 令牌,不创建一等登录会话。 ## 与额外身份验证的区别 Device 授权成功表示用户批准当前客户端登录,不表示完成了一次高风险操作的额外验证。当前 Device 请求不支持通过 `acr_values` 取得 `kai_step_up` 结果;刷新 Device 令牌或在浏览器做过其他验证,也不会升级原授权链。 应用或 CLI 需要额外验证时,另行发起[额外身份验证](/docs/step-up)的授权码与 PKCE 流程,并核对结果属于当前业务账号。不要调用 KAI 的私有浏览器会话接口来绕过这项边界。 --- Source: https://auth.kai.com/docs/auth-cli # Auth CLI:让 Agent 配置登录 Auth CLI 帮助开发者在本机登录 KAI 管理账号,让 Agent 管理组织、项目和应用,并为业务应用生成 OAuth/OIDC 接入配置。 ## 安装与运行 命令名为 `kai-auth`,包名为 `@kai/auth-cli`,需要 Node.js 24.3.0 或更高版本。当前可从仓库构建本地安装包;以下流程不依赖 npm 上已发布的包。 在仓库的 Yarn 开发环境中构建: ```sh yarn workspace @kai/auth-cli build yarn workspace @kai/auth-cli pack --out /tmp/kai-auth-cli.tgz ``` 将生成的安装包放到开发者本机,然后运行: ```sh npm install -g /tmp/kai-auth-cli.tgz kai-auth browser install kai-auth --help ``` `browser install` 为开发者管理登录下载与 CLI 匹配的 Playwright Chromium。已有 Chrome 或 Edge 时,也可以在登录时指定 `--browser chrome` 或 `--browser msedge`。应用侧 `oauth step-up` 使用系统浏览器,不要求下载这个 Chromium;不支持自动打开浏览器的环境可用 `--no-open`。 源码开发可用 `yarn auth:cli --help`,它先构建再运行 CLI;已有构建产物时可用 `node packages/cli/dist/index.js --help`。运行使用对应平台安装的依赖;不要把 Linux 容器中的 `node_modules` 用于宿主 Node。交互登录需要图形浏览器,应在开发者可操作的环境中执行。 ## 开发者先登录,Agent 继续配置 ```sh kai-auth login --profile default --origin https://auth.kai.com kai-auth status --profile default --json ``` `login` 打开独立的 Playwright 浏览器并进入 `/console`。开发者在该窗口完成正常登录及所需验证,CLI 保存本机管理会话。它不导入已有浏览器 profile,也不要求把密码或验证码放进命令、项目文件或 Agent 对话。 后续命令默认使用 `default` profile;可以用 `--profile NAME` 区分账号或环境。`--origin` 仅用于 `login`,其他管理命令使用 profile 中保存的服务地址。未登录、会话失效或权限不足时,命令会报错。`login` 会自动清除已过期的本机会话;已有未过期 profile 需要重新登录时,先运行 `logout` 再 `login`,或者选择一个新的 profile 名称。 Agent 必须在能访问该本机 profile 的环境中运行。本版没有独立的远程无界面登录流程;远程 Agent 无法直接复用开发者另一台机器上的登录状态。 这是开发者管理会话。业务应用的 OAuth access token 不会因此获得管理 API 权限。组织、项目和应用操作继续由服务端校验身份、角色、资源归属与组织状态,敏感操作保留原有的再次验证要求。 ## 完整示例:创建应用并生成接入文件 以下示例使用 `jq` 提取命令输出中的 ID。它创建 public Web 应用,通过 Authorization Code + PKCE 登录,不需要 client secret。将回调地址替换成业务应用实际使用的 HTTPS 地址。 先创建组织和项目;已有资源可用 `list` 查询并直接使用返回的 ID。 ```sh ORGANIZATION_ID=$( printf '%s' '{"name":"Example Team","slug":"example-team"}' | kai-auth organizations create --stdin --json | jq -er '.data.id' ) PROJECT_ID=$( printf '%s' '{"name":"Example App","slug":"example-app"}' | kai-auth projects create --organization "$ORGANIZATION_ID" --stdin --json | jq -er '.data.id' ) ``` 创建应用的 JSON 使用与管理 API 相同的字段: ```sh cat > application.json <<'JSON' { "name": "Example Web", "clientType": "public", "applicationKind": "web", "accessMode": "authenticated_users", "scopes": ["openid", "profile", "email"], "requiredScopes": ["openid"], "optionalScopes": ["profile", "email"], "signInMethods": ["authorization_code"], "redirectUris": ["https://app.example.com/oauth/callback"], "postLogoutRedirectUris": ["https://app.example.com/signed-out"] } JSON APPLICATION_ID=$( kai-auth applications create \ --organization "$ORGANIZATION_ID" \ --project "$PROJECT_ID" \ --file application.json --json | jq -er '.data.application.id' ) kai-auth init \ --organization "$ORGANIZATION_ID" \ --project "$PROJECT_ID" \ --application "$APPLICATION_ID" \ --redirect-uri https://app.example.com/oauth/callback \ --directory ./my-app --json kai-auth config show --directory ./my-app --json kai-auth config env --directory ./my-app --json kai-auth doctor --directory ./my-app --json ``` 每一步失败后先处理错误,再继续下一步。`authenticated_users` 允许非组织成员登录;仅对组织成员开放时改为 `members_only`。`profile` 和 `email` 在此示例中是可选权限,应用必须处理用户未授权这些信息的情况。 `init` 读取已登记应用,在目标目录创建: | 文件 | 用途 | | --------------- | ---------------------------------------- | | `kai-auth.json` | 公开的应用接入配置,供 CLI 和 Agent 读取 | | `.env.example` | 环境变量示例,不会自动加载进业务应用 | | `KAI_AUTH.md` | 供开发者与 Agent 使用的接入说明 | 生成操作不覆盖已有文件;发生冲突时先审查现有内容,或选择新目录。`--redirect-uri` 必须是该应用已经登记的回调;有多个回调时必须显式选择,命令不会自动修改回调白名单。配置中的 `issuer` 来自 Discovery,可以与登录使用的服务 `origin` 不同。 `.env.example` 包含 `KAI_AUTH_ORIGIN`、`KAI_AUTH_ISSUER`、`KAI_AUTH_CLIENT_ID`、`KAI_AUTH_CLIENT_TYPE`、`KAI_AUTH_SCOPES` 和 `KAI_AUTH_SIGN_IN_METHODS`;浏览器授权应用还包含 `KAI_AUTH_REDIRECT_URI`。confidential 应用的 `KAI_AUTH_CLIENT_SECRET` 仅为空白占位。生成文件不包含管理会话或实际 client secret。 配置生成后,Agent 还需要结合业务应用的框架实现 OIDC 登录入口、回调、PKCE、`state`、`nonce`、令牌验证和应用自己的会话。使用 Discovery 中的端点,按照[Authorization Code + PKCE](/docs/authorization-code)和[令牌校验与 claims](/docs/claims)完成接入;生成文件不等于业务应用已经支持登录。 ## 给 Agent 的任务示例 开发者完成本机 `kai-auth login` 后,可以提供以下任务: ```text 使用 kai-auth --json 为当前项目接入 KAI 登录。先检查 status 和组织、项目、 应用列表,确认目标资源;需要创建或更新时先读取对应 schema,再提交 JSON。 为选定应用运行 init,阅读 KAI_AUTH.md,并按本项目的框架实现标准 OIDC Authorization Code + PKCE、回调校验与应用会话。使用 config show/env 获取 公开配置,运行 doctor 后,再验证业务应用真实登录、回调和退出流程。 client secret 只从安全文件交给服务端环境,不放入前端代码或命令输出。 ``` 本页的机器可读地址为 `/docs/auth-cli/index.md`;也可以通过 `/llms.txt` 查找其余接入资料。 ## 应用侧的额外身份验证 `kai-auth oauth step-up` 为应用发起 [OAuth 额外身份验证](/docs/step-up)。它与下文用于删除项目等管理动作的 `kai-auth step-up` 是不同流程,不读取或转发管理 profile 的 Cookie,也不直接调用 KAI 的私有验证接口。 当前命令适用于已登记的 **native / public** 应用,必须启用 Authorization Code、允许 `openid`,并配置精确的本机回调,例如 `http://127.0.0.1:8765/oauth/callback`。该回调需要事先加入应用白名单,并由 `init --redirect-uri` 写入配置。命令不会放宽回调校验、自动增加白名单或把端口替换为随机值;端口被占用时先处理冲突。Web 和 confidential 应用按其运行环境接入标准授权码流程;Device-only 应用必须先登记并启用 Authorization Code,不能通过向 Device 请求添加 `acr_values` 获取额外验证结果。 ```sh kai-auth doctor --directory ./native-app --step-up --json kai-auth oauth step-up \ --directory ./native-app \ --expected-sub CURRENT_USER_SUBJECT \ --max-proof-age 300 \ --timeout 600 \ --json ``` `--expected-sub` 必填,取当前业务已验证身份的 `sub`,不能填写邮箱、昵称,也不能根据本次回调临时更换预期用户。issuer 从接入配置固定读取。 CLI 先启动只监听本机的回调,再打开系统浏览器。用户在 KAI 页面亲自完成登录和额外验证;CLI 使用新的随机 state、nonce 与 S256 PKCE 兑换授权码,并验证 ID token 签名、issuer、audience、nonce、用户、额外验证策略及时间。它请求 `openid` 及应用登记的全部必选权限,不额外请求可选权限,也不把这次证明保存为管理登录会话。若必选权限包含 `offline_access`,命令会在发起授权前报配置错误;该命令不申请长效访问授权,需要调整应用的必选权限配置后重试。 - `--max-proof-age` 默认 300 秒,限制实际证明的新鲜度;刷新令牌不能重新计算此时间。 - `--timeout` 默认 600 秒,限制本次命令等待时间;取消、超时或中断不会被当作验证成功。 - `--no-open` 只把授权入口写到 stderr,供用户在能够访问本机回调的浏览器中打开;它不自动批准、不提交密码,也不是远程无界面认证。 - `--proof-file NEW_PRIVATE_FILE` 显式要求把通过校验的签名 ID token 保存到新的私有文件,权限为 `0600`,不覆盖已有文件。普通命令输出不包含 ID token、access token、refresh token、授权码或 Cookie。 成功结果只区分 `verified` 与 `password_only`,不返回 TOTP、Passkey 等实际方式;当前版本不包含手机批准。`password_only` 表示密码证明校验成功,不表示已经满足高等级要求;业务只接受高等级验证时仍须要求 `verified`。普通 JSON 输出是 CLI 的本机验真报告,不是可以转交给任意服务器的签名凭证;其他服务需要验证专门面向自己的 OAuth 证明,不能盲目信任这份 JSON 或面向 CLI client ID 的 ID token。 使用 `--json` 时,stdout 只输出一份机器可读结果;进度写入 stderr。成功示例: ```json { "ok": true, "data": { "status": "verified", "issuer": "https://auth.kai.com", "subject": "CURRENT_USER_SUBJECT", "acr": "urn:kai:acr:step-up", "authTime": 1790000000, "expiresAt": 1790000300 } } ``` `authTime` 与 `expiresAt` 均为秒级 Unix 时间;后者是 ID token 的到期时间,不会延长 `--max-proof-age` 规定的证明有效窗口。显式保存证明时,结果另含 `proofFile` 路径。未完成、超时或验证失败时返回 `ok: false` 和脱敏的 `error.code` / `error.message`,并以非零状态退出;不会返回成功状态或未校验的令牌。 `doctor --step-up` 只检查本地配置、Discovery 和这条流程需要的公开能力,不会替代真实用户验证,也不保证远端应用登记没有变化。完整登录、取消、错误回调、时间过期和同一账号核对仍应实际验收。 ## 命令与输入契约 | 命令 | 主要参数与用途 | | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ | | `browser install` | 安装默认 Chromium | | `login` | `--profile NAME --origin URL`;可选 `--browser chrome` 或 `--browser msedge` | | `status`、`logout` | 查看或清除所选 profile 的本机登录状态 | | `organizations list` | 查看当前账号可用的组织 | | `organizations get/create/update/delete` | get、update、delete 使用 `--organization UUID` | | `projects list/get/create/update/delete` | 必须指定 `--organization UUID`;get、update、delete 另需 `--project UUID` | | `applications list/get/create/update/delete` | 必须指定 `--organization UUID --project UUID`;get、update、delete 另需 `--application UUID` | | `step-up start/prove/send-otp/verify-otp/cancel` | 通过 `--file` 或 `--stdin` 提供 JSON;除 start 外还需 `--transaction UUID` | | `oauth step-up` | 应用侧额外验证;`--directory DIR --expected-sub SUBJECT`,可选时间限制、`--no-open`、`--proof-file NEW_FILE` | | `schema NAME` | 输出指定输入的 JSON Schema | | `init` | 指定组织、项目、应用 UUID;可选 `--directory DIR --redirect-uri URL` | | `config show/env` | 读取 `--directory DIR` 下的接入配置或生成环境变量说明 | | `doctor` | 检查 `--directory DIR` 下的配置及服务端 Discovery;`--step-up` 追加本机 OAuth 额外验证检查 | create、update 使用 `--file PATH` 或 `--stdin` 二选一读取 JSON。输入在发请求前通过 API 的 Effect Schema 验证;服务器仍会检查当前数据和权限。可读取以下 schema: ```sh kai-auth schema organization-create --json kai-auth schema organization-update --json kai-auth schema project-create --json kai-auth schema project-update --json kai-auth schema application-create --json kai-auth schema application-update --json kai-auth schema project-delete --json kai-auth schema step-up-start --json kai-auth schema step-up-prove --json kai-auth schema step-up-send-otp --json kai-auth schema step-up-verify-otp --json kai-auth schema step-up-cancel --json ``` JSON Schema 用于了解字段结构;跨字段限制也会在实际命令执行时验证。例如 `requiredScopes` 与 `optionalScopes` 必须同时给出、不重叠,并合起来等于 `scopes`;native 应用必须为 public。回调和客户端规则见[应用登记与回调](/docs/applications)。 delete 必须显式提供 `--confirm`,值等于待删除对象的 UUID。删除项目还需要通过 `--file` 或 `--stdin` 提供有效的 `project-delete` 再次验证凭证。凭证绑定具体动作、对象、会话与版本并由服务端验证,`--confirm` 不能代替它。 ## 删除项目的再次验证 使用相同的 profile 发起、验证并消费 step-up 事务,不能从另一个浏览器登录会话复制凭证。先为目标项目启动事务: ```sh kai-auth step-up start --stdin --json <