学习笔记 · Obsidian
Smith API:OAuth2、OIDC、PKCE 与 Device Flow
LangSmith 同时提供 OAuth 2.0 authorization code + PKCE、RFC 8628 device flow、refresh token、RFC 7591 dynamic registration、RFC 7009 revocation 和 OIDC discovery/userinfo。Client 管理是 organization-scoped;授权 consent 还必须核对用户可访问的 organization/workspace。
Discovery
| 端点 | 公开性 | 返回 |
|---|---|---|
GET /.well-known/oauth-authorization-server | OpenAPI 标有可选认证方案 | issuer、authorize/token/device/register/revoke/JWKS、grant/response/scope、PKCE methods、resource support |
GET /.well-known/openid-configuration | public | OIDC endpoints、claims、signing algorithms、subject types |
GET /oauth/client/{clientID} | public | name/logo/homepage/terms/privacy 等非敏感 consent 展示字段 |
GET /userinfo | Bearer | sub、email/verified、name/picture、LangSmith org ID/name |
OIDC client 必须验证 issuer、audience、signature、expiry、nonce/state,不因为 discovery 来自官方就跳过 token validation。Public client metadata 不能返回 secret、内部 redirect 或组织私密配置。
Authorization Code + PKCE
sequenceDiagram
participant C as Client
participant B as Browser/User
participant L as LangSmith OAuth
C->>L: GET /oauth/authorize + client_id + redirect_uri + challenge S256 + state
L-->>B: 302 consent page
B->>L: POST /oauth/authorize/approve + org/workspace + challenge
L-->>B: redirect_uri + authorization code + state
C->>L: POST /oauth/token + code + verifier + redirect_uri
L-->>C: access + refresh + id token + workspace_id
/oauth/authorize 要求 response_type=code、registered redirect、code_challenge_method=S256。Approve 是 authenticated multipart request;organization 必须等于当前身份,workspace 可选但必须属于 organization 且用户可访问。state 防 CSRF,client 必须生成、保存并恒定时间比较。
Token endpoint 按 grant_type 分派:authorization_code 需要 code/verifier/redirect;device flow 需要 device_code;refresh 需要 refresh_token。成功响应含 access_token、expires_in、可选 ID/refresh token、token type 与 workspace ID。Authorization code、PKCE verifier、refresh token 都视为 secret,单次/短期使用。
Device Authorization Flow
- Client
POST /oauth/device/code,只传 client ID,获得 device code、user code、verification URI、expiry、poll interval。 - 用户在已登录页面输入 user code;页面
POST /oauth/device/authorize,同时指定匹配当前身份的 organization 与可选 workspace。 - Device 按服务器
interval轮询/oauth/token的 device grant,直到 authorized、expired 或 denied。
不得缩短服务器 interval 强轮询;device/user code 要足够随机、短期、一次性、限速并防撞库。CLI/无头设备应把 verification URI 和 user code 清晰展示,但不能把 device code 写到共享日志。
Client 管理
Organization owner 可在 /api/v1/platform/oauth/clients 创建、列表、读取、Patch、删除,或 /{id}/rotate-secret:
- client type 为 public/confidential;confidential create/rotate 只显示一次 secret;
- 配置 name、redirect URIs、allowed scopes、grant types、logo/homepage/policy/TOS;
- Patch 可 disable client;rotate 立即使旧 secret 失效;delete 成功 204。
生产轮换要在支持双 secret 的前提下无损切换;该接口的语义是立即 invalidating previous secret,若没有重叠窗口,必须协调停机/瞬时部署。Metadata URI 仅允许可信 HTTPS,前端展示外部 logo/link 时要防追踪与内容注入。
Dynamic Client Registration
POST /oauth/register 实现 RFC 7591,公开注册只签发 public client;redirect 仅允许受控 loopback、HTTPS 或 native URI,body 上限 8 KiB,成功 201。Request 可含 redirect URIs、name、grant/response、scope、token auth method 等。
Dynamic registration 是攻击面:限制速率/总量、严查 scheme/host/port、拒绝 credential-in-URL 与通配 redirect,不允许借 logo/policy URI SSRF。官方变更记录还说明 headless HTTP redirect 只接受 127.0.0.1 或 [::1],不接受 localhost;客户端应优先读实时 metadata/错误,而不是硬编码旧规则。
Authorized apps 与撤销
GET /api/v1/platform/oauth/authorized-apps列当前用户授权的 app、scopes 与授权时间;DELETE .../{clientID}撤销该 app 并使用户在此 app 下的 active tokens 失效;POST /oauth/revoke接受 access/refresh token,按 RFC 7009 无论 token 是否存在都返回 200,防止 token 探测。
撤销后的 resource server 仍需实时/短缓存验证,不能接受到 JWT 过期才生效的长窗口。Client 对 revoke 200 只能认定“请求已被接受为幂等处理”,不能据此证明 token 之前存在。
安全与错误处理
- Public client 永远不把 client secret 当保护手段,必须 PKCE;confidential client 把 secret 放服务器 secret store。
- Redirect URI 做 exact match;
state、nonce、PKCE、issuer/audience 全部验证。 - Scope 默认最小;组织/workspace 选择只能从 authenticated identity 的授权集合中取。
- Token 不进 URL、浏览器持久存储、trace 或 error detail;refresh token rotation/reuse detection 由产品实时能力决定,上线前验证。
- 400 是 OAuth request/token error;401 未认证;403 当前用户/组织无权;404 隐藏或不存在;500 不应自动重放 authorization code。
- Client secret create/rotate 响应丢失时不能通过 GET 取回,只能再次受控 rotate。