@eduwork/dsh-oidc 0.3.0-dev.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.en.md +12 -0
  2. package/CHANGELOG.md +12 -0
  3. package/README.md +9 -1
  4. package/README_EN.md +9 -1
  5. package/THIRD_PARTY_NOTICES.en.md +5 -4
  6. package/THIRD_PARTY_NOTICES.md +5 -5
  7. package/docs/account-extensions.en.md +7 -1
  8. package/docs/account-extensions.md +7 -1
  9. package/docs/compatibility.en.md +3 -1
  10. package/docs/compatibility.md +3 -1
  11. package/docs/development.en.md +1 -1
  12. package/docs/development.md +1 -1
  13. package/docs/dsh-integration.en.md +3 -3
  14. package/docs/dsh-integration.md +3 -3
  15. package/docs/enterprise-profile.en.md +49 -2
  16. package/docs/enterprise-profile.md +49 -2
  17. package/docs/gateway-auth/README.md +7 -6
  18. package/docs/gateway-auth/README_EN.md +7 -6
  19. package/docs/gateway-auth/experimental-oidc-llm.en.md +10 -8
  20. package/docs/gateway-auth/experimental-oidc-llm.md +9 -7
  21. package/docs/gateway-auth/litellm-setup.en.md +10 -4
  22. package/docs/gateway-auth/litellm-setup.md +10 -4
  23. package/docs/gateway-auth/oidc-llm-draft.en.md +89 -63
  24. package/docs/gateway-auth/oidc-llm-draft.md +86 -60
  25. package/docs/getting-started.en.md +3 -1
  26. package/docs/getting-started.md +3 -1
  27. package/docs/key-binding-protocol.en.md +1 -1
  28. package/docs/key-binding-protocol.md +1 -1
  29. package/docs/oidc-interoperability.en.md +1 -1
  30. package/docs/oidc-interoperability.md +1 -1
  31. package/docs/open-integration.en.md +3 -1
  32. package/docs/open-integration.md +3 -1
  33. package/examples/ecnu.enterprise-profile.example.json +4 -1
  34. package/examples/enterprise-profile.example.json +4 -1
  35. package/examples/identity-only.example.json +7 -2
  36. package/examples/litellm.enterprise-profile.example.json +9 -0
  37. package/examples/oidc-llm.enterprise-profile.example.json +33 -0
  38. package/examples/resources-discovery.example.json +9 -2
  39. package/lib/callback-page.js +9 -4
  40. package/lib/client.js +460 -360
  41. package/lib/desktop-oidc.js +2 -2
  42. package/lib/gateway-backend.js +20 -5
  43. package/lib/index.js +8 -6
  44. package/lib/litellm-protocol.js +4 -3
  45. package/lib/model-types.js +7 -0
  46. package/lib/oidc-llm-protocol.js +8 -5
  47. package/lib/oidc.js +22 -28
  48. package/lib/profile.js +36 -47
  49. package/lib/provider/authorization-scope.js +4 -0
  50. package/lib/provider/core.js +8 -19
  51. package/lib/provider/index.js +18 -2
  52. package/lib/provider/transform-adapter.js +4 -0
  53. package/lib/resources.js +12 -4
  54. package/lib/token-lifetime.js +15 -0
  55. package/lib/transport.js +5 -0
  56. package/lib/typert-schemas.js +14 -12
  57. package/package.json +104 -97
  58. package/protocol/resources.openapi.yaml +11 -3
  59. package/schema/enterprise-profile.v1alpha1.schema.json +40 -5
@@ -2,7 +2,7 @@
2
2
 
3
3
  [简体中文](experimental-oidc-llm.md) | **English**
4
4
 
5
- This branch provides an opt-in oidc-llm 0.1 adapter, disabled by default and not released. The protocol remains under review. This page describes implemented client behavior; it does not claim that the [complete draft](oidc-llm-draft.en.md) is finalized or that a server has passed acceptance.
5
+ The opt-in oidc-llm 0.1 adapter is included in `@eduwork/dsh-oidc@0.3.0-dev.2` and EduWork `0.3.6-dev.20260921.1`, and remains disabled by default. The protocol is still under review. This page describes implemented client behavior; it does not claim that the [complete draft](oidc-llm-draft.en.md) is finalized or that arbitrary servers have passed acceptance.
6
6
 
7
7
  ## Configuration
8
8
 
@@ -23,31 +23,33 @@ Add this object to desktop `organizations` or plugin `profiles`. The server must
23
23
  }
24
24
  ```
25
25
 
26
- Select `identityMode` explicitly. Neither mode falls back to the other:
26
+ For organizational sign-in, explicitly select `identityMode: "oidc"`, as EduWork@ECNU currently does. Neither mode falls back to the other:
27
27
 
28
28
  | Mode | Requested scopes | Identity validation |
29
29
  | --- | --- | --- |
30
30
  | `oidc` | `openid profile offline_access llm:models:read llm:invoke`, with consent | Initial RS256 ID Token required; signature, issuer, aud/azp, nonce, time, optional at_hash and UserInfo subject are verified |
31
31
  | `oauth` | `llm:profile llm:models:read llm:invoke` | Reads sub from the discovered UserInfo-shaped endpoint; does not claim OIDC authentication |
32
32
 
33
- Only static public-client registration is implemented; no client secret is accepted. Register the actual IPv4 loopback callback `http://127.0.0.1:<random-port>/oauth/callback`. Dynamic registration remains future work.
33
+ Only static public-client registration is implemented; no client secret is accepted. Registration must allow the actual port to vary in the IPv4 loopback callback `http://127.0.0.1:<random-port>/oauth/callback` while constraining its host and path. Dynamic registration is outside current integration requirements. See the [reference deployment](oidc-llm-draft.en.md#8-chatecnu-reference-deployment) for ChatECNU discovery, endpoints and capabilities. Public examples retain neutral domains; editions supply their own addresses and registered client IDs.
34
34
 
35
35
  When discovery declares `authorization_response_iss_parameter_supported: true`, both successful and error authorization responses must include `iss` exactly matching the discovered `issuer`. A missing value produces `gateway_callback_issuer_missing`; repeated, empty or mismatched values produce `gateway_callback_issuer_invalid`. The client does not exchange the code. Ask the authentication service administrator to align discovery and callback behavior before starting a new sign-in. The desktop callback listener closes when the flow ends; refreshing an old callback URL cannot resume sign-in.
36
36
 
37
- Production endpoints require HTTPS. An isolated experiment may reuse `allowInsecureDevelopment: true` and `insecureDevelopmentOrigin`, with a mandatory `expectedIssuer` pin. HTTP is restricted to loopback or that exact origin, never an entire private network. LiteLLM native retains its loopback-only exception.
37
+ `allowInsecureDevelopment` is the only HTTP switch, at the profile root beside `auth`/`oidc`. Its default is `false` (HTTPS only); `true` also accepts HTTP service URLs. `insecureDevelopmentOrigin` is obsolete and ignored. Issuer identity, resource-origin validation and OAuth/PKCE checks still apply. `discoveryUrl` is the complete discovery URL; `expectedIssuer` is optional and pins the exact issuer when provided.
38
38
 
39
39
  ## Shared implementation and limits
40
40
 
41
41
  Both gateway adapters reuse discovery transport, Code+PKCE, browser/callbacks, the Host credential vault, single-flight refresh, model catalogs, the DSH Provider and streaming authorization isolation. The OIDC identity mode directly reuses the existing strict verifier. Standalone identity-only `oidc` remains supported. Legacy model Key Binding has been removed; see the [migration guide](../key-binding-protocol.en.md).
42
42
 
43
- Access tokens stay opaque. Discovered `api_base` and UserInfo must share the resource origin; authentication endpoints only receive their designated credentials, without redirects. Changed configuration/discovery bindings cannot reuse old authorization. Token responses require actual scope, positive integer expiry and a refresh token; grants cannot expand or omit required connection permissions. The server's expiry is honored without enforcing the unagreed 15-minute proposal.
43
+ Access tokens stay opaque. Discovered `api_base` and UserInfo must share the resource origin; authentication endpoints only receive their designated credentials, without redirects. Changed configuration/discovery bindings cannot reuse old authorization. Token responses require actual scope, positive integer expiry and a refresh token; grants cannot expand or omit required connection permissions. The server's expiry is honored without a 15-minute limit. The current client accepts 1 second through 365 days as validity bounds, not recommended lifetimes.
44
+
45
+ Before using authorization, the Host attempts refresh when remaining validity reaches `min(30 minutes, half the issued Token lifetime)`, coalescing concurrent refreshes for the same authorization. This client policy does not change the server's Token lifetime. A new Token cannot replace credentials in an already-sent stream. Stream behavior across expiry requires separate server acceptance; model generation is not replayed automatically.
44
46
 
45
47
  Refresh may omit an ID Token. If present, its original issuer, subject, audience, optional auth_time and nonce are checked. Failed validation after rotation revokes the new credentials and requires sign-in instead of continuing with a possibly consumed old refresh token. Temporary Token endpoint failures preserve still-valid authorization.
46
48
 
47
- Catalogs retain shared conservative capability mapping: ID-only rows support text without inferred vision or reasoning. Optional draft model capability fields are not consumed yet; use reviewed provider configuration when needed.
49
+ The optional `/models` entry `type` classifies model purpose; reviewed `provider.models[].type` fills missing server metadata. Only `llm` registers for conversation; unresolved types are excluded. Input modalities and reasoning remain reviewed provider facts: image-capable LLMs stay conversational. See the [Profile reference](../enterprise-profile.en.md#provider-object).
48
50
 
49
- Institution extensions can use the existing Host-only `modelResourceFetch(profileID, relativePath)`. Legacy connections use their managed model Key; experimental connections use the current authorized Access Token. Both reuse path restrictions, GET, bounded bodies and logout isolation. The public plugin makes no implicit quota request and adds no quota RPC/fields. Extensions must be installed explicitly; the server must define and enforce their authorization.
51
+ Institution extensions can use the existing Host-only `modelResourceFetch(profileID, relativePath)`. Model connections use the current authorized Access Token, sharing path restrictions, GET, bounded bodies and logout isolation. The public plugin makes no implicit quota request and adds no quota RPC/fields. Extensions must be installed explicitly; the server must define and enforce their authorization.
50
52
 
51
- Logout stops local requests before submitting refresh-token revocation; 200 empty and JSON responses are accepted. Remote failure only produces a sanitized warning. A persistent revocation retry queue is not implemented, so that draft proposal is not claimed. Refresh-family revocation, remaining Access Token validity, scope enforcement and model filtering still require real-server acceptance.
53
+ Logout clears local authorization and stops related requests before attempting Refresh Token revocation; 200 empty and JSON responses are accepted. Clients accept natural server-side expiry of issued Access Tokens without requiring immediate global invalidation. Browser SSO is not signed out. Remote failure only produces a sanitized warning; no persistent revocation retry queue exists. Refresh-family reuse detection and revocation scope, scope enforcement, model filtering and stream behavior at expiry require server documentation and acceptance.
52
54
 
53
55
  References: [OIDC refresh response](https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse), [authorization response issuer](https://www.rfc-editor.org/rfc/rfc9207.html).
@@ -2,7 +2,7 @@
2
2
 
3
3
  **简体中文** | [English](experimental-oidc-llm.en.md)
4
4
 
5
- 本分支提供 oidc-llm 0.1 的实验适配器,默认关闭,尚未发布。协议仍在讨论;本页描述已实现的客户端边界,不表示[完整草案](oidc-llm-draft.md)已经定稿或服务端已经通过验收。
5
+ oidc-llm 0.1 的实验适配器已包含在 `@eduwork/dsh-oidc@0.3.0-dev.2` 与 EduWork `0.3.6-dev.20260921.1` 中,默认关闭,需显式启用。协议仍在讨论;本页描述已实现的客户端边界,不表示[完整草案](oidc-llm-draft.md)已经定稿或任意服务端均已通过验收。
6
6
 
7
7
  ## 配置
8
8
 
@@ -23,31 +23,33 @@
23
23
  }
24
24
  ```
25
25
 
26
- 必须显式选择 `identityMode`。两种模式不自动互相降级:
26
+ 组织登录建议显式选择 `identityMode: "oidc"`,与 EduWork@ECNU 当前配置一致。两种模式不自动互相降级:
27
27
 
28
28
  | 模式 | 请求范围 | 身份验证 |
29
29
  | --- | --- | --- |
30
30
  | `oidc` | `openid profile offline_access llm:models:read llm:invoke`,并请求 consent | 首次必须有 RS256 ID Token;验证签名、issuer、aud/azp、nonce、时间、可选 at_hash 与 UserInfo sub |
31
31
  | `oauth` | `llm:profile llm:models:read llm:invoke` | 从发现的 UserInfo 格式接口读取 sub;不宣称完成 OIDC 身份验证 |
32
32
 
33
- 目前仅实现预注册 public client,不能填写 client secret。服务器注册须允许实际 IPv4 loopback 回调 `http://127.0.0.1:<随机端口>/oauth/callback`。动态注册留待后续实现。
33
+ 目前仅实现预注册 public client,不能填写 client secret。服务器注册须允许 IPv4 loopback 回调 `http://127.0.0.1:<随机端口>/oauth/callback` 的实际端口变化,并约束 host/path。动态注册不属于当前接入要求。ChatECNU 的公开发现、端点和能力对照见[草案参考部署](oidc-llm-draft.md#8-chatecnu-参考部署);公共示例保留中立域名,发行版自行提供地址和注册的 client ID。
34
34
 
35
35
  发现声明 `authorization_response_iss_parameter_supported: true` 时,成功和错误授权回调都必须带 `iss`,且与发现中的 `issuer` 完全一致。缺失会报告 `gateway_callback_issuer_missing`;重复、空值或不匹配会报告 `gateway_callback_issuer_invalid`。客户端不会继续换取 Token,应由认证服务管理员核对发现声明与回调实现后重新发起登录。桌面回调监听在流程结束后关闭,刷新旧回调地址不能恢复登录。
36
36
 
37
- 生产端点须为 HTTPS。独立实验环境可复用既有 `allowInsecureDevelopment: true` 和 `insecureDevelopmentOrigin`,但必须同时固定 `expectedIssuer`;HTTP 端点只允许 loopback 或该完整原点,不扩大到整个内网。此例外不改变 LiteLLM native 的 loopback 限制。
37
+ `allowInsecureDevelopment` 是唯一的 HTTP 开关,放在企业对象中,与 `auth`/`oidc` 同级;默认 `false` 仅接受 HTTPS,改为 `true` 也接受 HTTP 服务地址。旧 `insecureDevelopmentOrigin` 已废弃并忽略。issuer 身份、资源同源关系及 OAuth/PKCE 校验仍按协议执行。`discoveryUrl` 填完整发现地址,`expectedIssuer` 可选,填写后必须与发现文档完全一致。
38
38
 
39
39
  ## 共享实现与边界
40
40
 
41
41
  两套网关适配器共用发现请求、Code+PKCE、浏览器/回调、Host 凭据库、并发刷新合并、模型目录、DSH Provider 与流式请求隔离。OIDC 身份分支直接复用现有严格验证器;保留独立的纯身份 `oidc` 登录;旧模型 Key Binding 已移除,见[迁移说明](../key-binding-protocol.md)。
42
42
 
43
- Access Token 按不透明 Bearer 处理。发现中的 `api_base` 与 UserInfo 必须和 resource 同源;认证端点只接收其角色对应的凭据,禁自动重定向。配置或发现绑定改变时不复用旧授权。Token 响应必须提供实际 scope、正整数有效期和 refresh token;scope 不得扩大或缺少本连接需要的权限。有效期按服务器响应执行,不把尚未议定的 15 分钟建议写成硬性上限。
43
+ Access Token 按不透明 Bearer 处理。发现中的 `api_base` 与 UserInfo 必须和 resource 同源;认证端点只接收其角色对应的凭据,禁自动重定向。配置或发现绑定改变时不复用旧授权。Token 响应必须提供实际 scope、正整数有效期和 refresh token;scope 不得扩大或缺少本连接需要的权限。有效期按服务器响应执行,不规定 15 分钟上限;当前客户端校验范围为 1 秒至 365 天,不代表推荐寿命。
44
+
45
+ 使用授权前,Host 在剩余有效期不超过 `min(30 分钟, 本次 Token 有效期的一半)` 时尝试刷新,并合并同一授权的并发刷新。这是客户端策略,不改变服务端的 Token 寿命。新 Token 不能替换已经发出的流式请求凭据;服务器是否允许长流跨越到期时刻仍需单独验收,模型生成不自动重放。
44
46
 
45
47
  刷新可以省略 ID Token;返回时检查原 issuer、sub、aud、可选 auth_time 和 nonce。轮换后的 Token 若验证失败,客户端撤销新凭据并要求重登,避免继续使用可能已消费的旧 refresh token。临时 Token endpoint 故障不会删除仍有效的旧授权。
46
48
 
47
- 模型目录仍使用共享的保守能力映射:仅有 ID 时启用文本,不从名称猜图像或思考能力。草案中的可选模型能力扩展尚未消费,需要时使用显式审核的 provider 配置。
49
+ `/models` 条目的可选 `type` 用于区分模型用途,服务端未提供时由已审核的 `provider.models[].type` 补充。仅 `llm` 注册到对话,类型无法确定时不注册。输入模态与思考能力仍使用已审核的 Provider 配置,能看图的 LLM 不会被排除。详见 [Profile 说明](../enterprise-profile.md#provider-对象)。
48
50
 
49
51
  机构扩展可以调用既有 Host-only `modelResourceFetch(profileID, relativePath)`。模型连接统一使用当前获准的 Access Token,共享路径限制、GET、正文大小限制与退出隔离。公共插件不自动查询配额,也不新增配额 RPC 或字段。扩展须显式安装,服务端须自行声明并执行其授权范围。
50
52
 
51
- 退出立即停止本机请求,再提交 refresh token 撤销;支持 200 空响应或 JSON。远端失败仅记录脱敏警告,当前没有持久化撤销重试队列,不能宣称满足草案中这项提议。服务器的 refresh 家族撤销、Access Token 剩余有效期、scope 隔离和模型过滤仍需真实验收。
53
+ 退出清除本地授权、停止关联请求,再尝试撤销 Refresh Token;支持 200 空响应或 JSON。客户端接受已签发 Access Token 在服务端自然到期,不要求立即全局失效,也不退出浏览器中的组织 SSO。远端失败仅记录脱敏警告,当前没有持久化撤销重试队列。刷新家族重用检测与撤销范围、scope 隔离、模型过滤和长流到期行为需要服务端说明并验收。
52
54
 
53
55
  参考:[OIDC 刷新响应](https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse)、[授权响应 issuer](https://www.rfc-editor.org/rfc/rfc9207.html)。
@@ -4,7 +4,7 @@
4
4
 
5
5
  Let users sign in to a LiteLLM gateway from EduWork, discover authorized models, and chat without manually distributing model API keys.
6
6
 
7
- [LiteLLM](https://github.com/BerriAI/litellm) provides the gateway; EduWork uses its native OAuth flow. This guide is for administrators of an existing LiteLLM deployment and EduWork desktop users. The protocol baseline is **LiteLLM v1.101.0 / native contract 1**; validate other versions using the checks below. EduWork requires a build containing this feature, which has not yet been published as an npm package or desktop release.
7
+ [LiteLLM](https://github.com/BerriAI/litellm) provides the gateway; EduWork uses its native OAuth flow. This guide is for administrators of an existing LiteLLM deployment and EduWork desktop users. The protocol baseline is **LiteLLM v1.101.0 / native contract 1**; validate other versions using the checks below. **EduWork 0.3.6-dev.20260921.1 includes this feature** and a commented `examples/litellm.jsonc`; no additional plugin installation is needed. The underlying implementation is published as `@eduwork/dsh-oidc@0.3.0-dev.2`.
8
8
 
9
9
  ## 1. Prepare the server
10
10
 
@@ -37,7 +37,7 @@ See the [protocol reference](README_EN.md) for the full response and constraints
37
37
 
38
38
  ## 2. Configure EduWork
39
39
 
40
- Select **Open configuration file** in Settings and add this organization to `config/eduwork.jsonc`. Merge into an existing `organizations` array rather than replacing other settings.
40
+ Select **Open configuration file** in Settings. Add the object inside the example's `organizations` array to the same array in the active `eduwork.jsonc`. On Windows, it is in the application's `config/`; on macOS public editions, it is in `~/Library/Application Support/eduwork-electron/config/`. The adjacent `examples/litellm.jsonc` provides the same format and full comments. Editing only the example has no effect; do not replace existing settings with the entire example.
41
41
 
42
42
  ```json
43
43
  {
@@ -47,6 +47,7 @@ Select **Open configuration file** in Settings and add this organization to `con
47
47
  "schemaVersion": "dsh-oidc/v1alpha1",
48
48
  "id": "example-gateway",
49
49
  "displayName": "Example model gateway",
50
+ "allowInsecureDevelopment": false,
50
51
  "auth": {
51
52
  "discoveryUrl": "https://gateway.example.org/.well-known/litellm-cli-auth",
52
53
  "expectedIssuer": "https://gateway.example.org"
@@ -60,14 +61,15 @@ Select **Open configuration file** in Settings and add this organization to `con
60
61
  | --- | --- |
61
62
  | `id` | A stable organization identifier, unique on this client. |
62
63
  | `displayName` | The organization name shown in the account menu. |
64
+ | `allowInsecureDevelopment` | Defaults to `false` for HTTPS only; set `true` for HTTP testing, beside `auth`. |
63
65
  | `auth.discoveryUrl` | The complete discovery document URL supplied by the administrator. |
64
66
  | `auth.expectedIssuer` | The expected issuer; recommended, and must match discovery. |
65
67
 
66
- Save, exit completely through the tray, and restart EduWork. Closing its window usually leaves the process running.
68
+ Save, exit completely through the tray or application menu, and restart EduWork. Closing its window usually leaves the process running.
67
69
 
68
70
  The model API base is the validated issuer with `/v1` appended, preserving any deployment prefix. Do not set `provider.baseURL`. LiteLLM dynamically registers a Client ID for this login: do not configure a static `clientId`, `client_secret`, or scope, or combine this profile with `oidc` or `keyBinding`.
69
71
 
70
- Production uses HTTPS. Explicit `allowInsecureDevelopment: true` on the organization entry, alongside `auth`, permits loopback HTTP for local development only, not arbitrary remote HTTP addresses. Keep passwords, Tokens, and upstream keys out of the client configuration.
72
+ `allowInsecureDevelopment` is the only HTTP switch, at the profile root beside `auth`/`oidc`. Its default is `false` (HTTPS only); `true` also accepts HTTP service URLs. `insecureDevelopmentOrigin` is obsolete and ignored. Issuer identity, resource-origin validation and OAuth/PKCE checks still apply. `discoveryUrl` is the complete discovery URL; `expectedIssuer` is optional and pins the exact issuer when provided.
71
73
 
72
74
  Developers integrating another client can use the [single Profile example](../../examples/litellm.enterprise-profile.example.json) and [desktop Host guide](../desktop-host.en.md) to reuse the same authorization and model modules. Desktop users do not need to assemble a Host themselves.
73
75
 
@@ -78,6 +80,10 @@ Developers integrating another client can use the [single Profile example](../..
78
80
  3. The browser returns to a local callback page. Once it confirms completion, return to EduWork.
79
81
  4. Select an organization model and send a short message to verify a real upstream response.
80
82
 
83
+ ![Selecting deepseek-v4-flash in the organization group after signing in to LiteLLM from EduWork](https://raw.githubusercontent.com/ECNU/EduWork/main/docs/images/litellm-models.png)
84
+
85
+ The local LiteLLM group is the configured organization name, and `deepseek-v4-flash` is an authorized gateway model. Server permissions determine your model list; the DeepSeek group above belongs to a separately configured provider.
86
+
81
87
  The `127.0.0.1` callback is a temporary EduWork listener on the user's computer, not the LiteLLM server. Complete sign-in in a browser on the same computer as the client. Do not bookmark or reuse an old authorization link.
82
88
 
83
89
  The client discovers the current authorization's `/v1/models`, sends requests with the Access Token, and refreshes near expiry. Protected local storage restores authorization after restart. Logout clears the local session and attempts Refresh Token revocation; the server controls the remaining lifetime of issued Access Tokens.
@@ -4,7 +4,7 @@
4
4
 
5
5
  让用户在 EduWork 中登录 LiteLLM 网关,自动发现获授权的模型并直接对话,无需手动分发模型 API Key。
6
6
 
7
- [LiteLLM 项目](https://github.com/BerriAI/litellm)提供网关;EduWork 使用其 native OAuth 流程。本文适用于已有 LiteLLM 部署的管理员和使用 EduWork 桌面的用户。协议基线为 **LiteLLM v1.101.0 / native contract 1**,其他版本需按本文验收。EduWork 端需使用包含本功能的构建;本分支尚未发布 npm 包或桌面版本。
7
+ [LiteLLM 项目](https://github.com/BerriAI/litellm)提供网关;EduWork 使用其 native OAuth 流程。本文适用于已有 LiteLLM 部署的管理员和使用 EduWork 桌面的用户。协议基线为 **LiteLLM v1.101.0 / native contract 1**,其他版本需按本文验收。**EduWork 0.3.6-dev.20260921.1 已内置本功能**,安装包附带 `examples/litellm.jsonc`,无需另装插件。底层实现已发布为 `@eduwork/dsh-oidc@0.3.0-dev.2`。
8
8
 
9
9
  ## 1. 管理员准备服务端
10
10
 
@@ -37,7 +37,7 @@ curl --fail --silent --show-error https://gateway.example.org/.well-known/litell
37
37
 
38
38
  ## 2. 配置 EduWork
39
39
 
40
- 在设置中选择 **打开配置文件**,将下面的机构条目加入 `config/eduwork.jsonc`。已有配置时合并 `organizations` 数组,不要覆盖其他配置。
40
+ 在设置中选择 **打开配置文件**,将下面 `organizations` 数组内的机构对象加入生效 `eduwork.jsonc` 的同名数组。Windows 文件在程序目录的 `config/`,macOS 公版在 `~/Library/Application Support/eduwork-electron/config/`;旁边 `examples/litellm.jsonc` 提供相同格式和完整注释。只修改示例不会生效,也不要用整个示例覆盖已有配置。
41
41
 
42
42
  ```json
43
43
  {
@@ -47,6 +47,7 @@ curl --fail --silent --show-error https://gateway.example.org/.well-known/litell
47
47
  "schemaVersion": "dsh-oidc/v1alpha1",
48
48
  "id": "example-gateway",
49
49
  "displayName": "示例模型网关",
50
+ "allowInsecureDevelopment": false,
50
51
  "auth": {
51
52
  "discoveryUrl": "https://gateway.example.org/.well-known/litellm-cli-auth",
52
53
  "expectedIssuer": "https://gateway.example.org"
@@ -60,14 +61,15 @@ curl --fail --silent --show-error https://gateway.example.org/.well-known/litell
60
61
  | --- | --- |
61
62
  | `id` | 本机唯一且稳定的机构标识;多个机构使用不同值。 |
62
63
  | `displayName` | 账户菜单中显示的机构名称。 |
64
+ | `allowInsecureDevelopment` | 默认 `false`,仅接受 HTTPS;HTTP 测试环境设为 `true`,与 `auth` 同级。 |
63
65
  | `auth.discoveryUrl` | 完整发现文档 URL,由管理员提供。 |
64
66
  | `auth.expectedIssuer` | 预期的服务签发方,建议填写,并与发现文档中的 issuer 一致。 |
65
67
 
66
- 保存后从托盘完全退出 EduWork,再重新启动。只关闭窗口通常不会退出进程。
68
+ 保存后从托盘或应用菜单完全退出 EduWork,再重新启动。只关闭窗口通常不会退出进程。
67
69
 
68
70
  模型 API 地址由校验后的 issuer 追加 `/v1` 得到,保留部署路径前缀;不要另填 `provider.baseURL`。LiteLLM 会动态注册本次登录所需的 Client ID,无需手工填写固定 `clientId`、`client_secret` 或 scope,也不要同时配置 `oidc`、`keyBinding`。
69
71
 
70
- 正式部署使用 HTTPS。本机开发只有在机构条目中显式设置 `allowInsecureDevelopment: true`(与 `auth` 同级)才允许 loopback HTTP,不能用它连接任意明文远程地址。密码、Token 和上游 Key 均不应写入客户端配置。
72
+ `allowInsecureDevelopment` 是唯一的 HTTP 开关,放在企业对象中,与 `auth`/`oidc` 同级;默认 `false` 仅接受 HTTPS,改为 `true` 也接受 HTTP 服务地址。旧 `insecureDevelopmentOrigin` 已废弃并忽略。issuer 身份、资源同源关系及 OAuth/PKCE 校验仍按协议执行。`discoveryUrl` 填完整发现地址,`expectedIssuer` 可选,填写后必须与发现文档完全一致。
71
73
 
72
74
  其他客户端开发者可使用[单个 Profile 示例](../../examples/litellm.enterprise-profile.example.json)和[桌面宿主指南](../desktop-host.md),复用同一授权与模型模块;桌面安装用户无需自行装配 Host。
73
75
 
@@ -78,6 +80,10 @@ curl --fail --silent --show-error https://gateway.example.org/.well-known/litell
78
80
  3. 浏览器返回本机回调页面,确认登录完成后回到 EduWork。
79
81
  4. 在模型选择器中选择该机构的模型,发送一条简短消息确认真实模型响应。
80
82
 
83
+ ![EduWork 登录 LiteLLM 后,在机构分组中选择 deepseek-v4-flash](https://raw.githubusercontent.com/ECNU/EduWork/main/docs/images/litellm-models.png)
84
+
85
+ 图中「本机 LiteLLM」是配置的机构名称,`deepseek-v4-flash` 是网关授权的模型。你的模型列表由服务端权限决定;上方 DeepSeek 分组属于另外配置的服务商。
86
+
81
87
  `127.0.0.1` 回调是 EduWork 在用户电脑上临时监听的地址,不是 LiteLLM 服务器地址。浏览器必须在运行客户端的同一台电脑上完成回调;不要收藏或重复打开旧授权链接。
82
88
 
83
89
  客户端自动读取当前授权的 `/v1/models`,请求使用 Access Token,并在临近到期时刷新。重启后通过本机受保护存储恢复授权。退出登录会清理本地会话并尝试撤销 Refresh Token;Access Token 的剩余有效期由服务端控制。
@@ -2,101 +2,104 @@
2
2
 
3
3
  [简体中文](oidc-llm-draft.md) | **English**
4
4
 
5
- **Status: for review, not a released deployment contract.** This branch includes a disabled-by-default [experimental adapter](experimental-oidc-llm.en.md); that guide describes implemented behavior and limits. Server behavior requires separate acceptance. The name, extension fields, scopes, lifetimes and revocation requirements remain proposals, not a published OpenID standard. Existing OIDC integrations continue to use the [current contract](../server-integration-contract.en.md). [Gateway guide](README_EN.md).
5
+ **Status: an open draft with an experimental implementation; not finalized and not an official OpenID standard.** This document derives a reusable model-platform contract from the integration adopted by ChatECNU and the current EduWork adapter. Public clients still require explicit opt-in; see [experimental integration](experimental-oidc-llm.en.md). [Gateway overview](README_EN.md) · [Server integration contract](../server-integration-contract.en.md)
6
6
 
7
- ## 1. Scope
7
+ ## 1. Scope and flow
8
8
 
9
- After browser sign-in, a public desktop client uses an Access Token to read its own user information, list authorized models and invoke them. Reuse OAuth authorization code, PKCE S256, loopback callbacks and standard OIDC claims. Basic OAuth mode does not require an ID Token; explicit full OIDC mode must still validate one.
9
+ Users sign in to their organization in the system browser. A public desktop client obtains Tokens through Authorization Code + PKCE S256, then uses the Access Token directly for user information, model discovery and inference, without obtaining a separate model API key. Other conforming clients can integrate without EduWork branding or school-specific APIs.
10
10
 
11
- Key Binding, quota standardization, teams, billing and remote executable plugins are excluded. An arbitrary ordinary OIDC token must not automatically become a model credential.
11
+ For organizational sign-in, full `oidc` identity mode is recommended and is used by EduWork@ECNU. The base `oauth` mode remains available. These are explicit identity choices within one model protocol, without automatic fallback. Ordinary identity-only OIDC does not automatically authorize model calls.
12
+
13
+ This draft does not define Key Binding, quotas, billing, teams or remote plugin execution. Institution features belong in separate extensions; school-specific model names, concurrency limits and quota fields are not public protocol requirements.
12
14
 
13
15
  ## 2. Discovery
14
16
 
15
- Configuration supplies the complete discovery URL, optional `expectedIssuer`, and a public `clientId` for static registration. Account endpoint paths are discovered. An unauthenticated GET returns 200 JSON:
17
+ Clients configure a complete discovery URL, an optional `expectedIssuer` and a preregistered public `clientId`. Discovery is an unauthenticated GET returning 200 JSON. This neutral example corresponds to the capabilities currently advertised by ChatECNU; its domain and paths are not mandatory:
16
18
 
17
19
  ```json
18
20
  {
19
- "issuer": "https://login.example.org",
20
- "authorization_endpoint": "https://login.example.org/oauth/authorize",
21
- "token_endpoint": "https://login.example.org/oauth/token",
22
- "revocation_endpoint": "https://login.example.org/oauth/revoke",
23
- "userinfo_endpoint": "https://models.example.org/userinfo",
21
+ "issuer": "https://models.example.org",
22
+ "authorization_endpoint": "https://models.example.org/oauth/authorize",
23
+ "token_endpoint": "https://models.example.org/oauth/token",
24
+ "userinfo_endpoint": "https://models.example.org/oauth/userinfo",
25
+ "revocation_endpoint": "https://models.example.org/oauth/revoke",
26
+ "jwks_uri": "https://models.example.org/oauth/jwks.json",
24
27
  "response_types_supported": ["code"],
28
+ "response_modes_supported": ["query"],
25
29
  "grant_types_supported": ["authorization_code", "refresh_token"],
26
- "code_challenge_methods_supported": ["S256"],
30
+ "scopes_supported": ["openid", "profile", "offline_access", "llm:profile", "llm:models:read", "llm:invoke"],
31
+ "subject_types_supported": ["public"],
32
+ "id_token_signing_alg_values_supported": ["RS256"],
27
33
  "token_endpoint_auth_methods_supported": ["none"],
28
34
  "revocation_endpoint_auth_methods_supported": ["none"],
29
- "scopes_supported": ["llm:profile", "llm:models:read", "llm:invoke"],
35
+ "code_challenge_methods_supported": ["S256"],
36
+ "authorization_response_iss_parameter_supported": true,
30
37
  "oidc_llm": {
31
38
  "version": "0.1",
32
39
  "resource": "https://models.example.org",
33
- "api_base": "https://models.example.org/v1",
34
- "identity_modes_supported": ["oauth"],
40
+ "api_base": "https://models.example.org/open/api/v1",
41
+ "identity_modes_supported": ["oauth", "oidc"],
35
42
  "client_registration_methods_supported": ["static"]
36
43
  }
37
44
  }
38
45
  ```
39
46
 
40
- These fields are proposed as required for basic mode. Version matching is exact; 0.x does not promise cross-version compatibility. There is one model resource, sent unchanged in authorization/token requests. `api_base` is a configurable prefix to which clients append `/models` and `/chat/completions`; `/v1` is not mandatory. This draft puts api_base and UserInfo on the resource origin; issuer may be on a separate sign-in origin. This is a draft-specific restriction, not an OIDC same-origin rule.
47
+ `oidc_llm.version` must match `0.1` exactly; 0.x versions do not promise compatibility. A single `resource` is sent unchanged in authorization, exchange and refresh requests. Append `/models` and `/chat/completions` to `api_base`; the prefix need not be `/v1`. UserInfo and `api_base` must share the resource origin, while the issuer may use a separate login origin. This resource boundary is specific to this draft, not general OIDC.
41
48
 
42
- OIDC Discovery recommends `userinfo_endpoint`; this profile proposes requiring it. Metadata must advertise the scopes needed for each supported identity mode. Standard discovery locations obey the applicable issuer rules. Explicit expectedIssuer requires exact equality; otherwise the initial issuer shares the discovery origin. Cross-origin hosting requires explicit issuer pinning.
49
+ The current adapter requires the base `oauth` mode to be advertised. Full OIDC deployments additionally advertise `oidc`, JWKS, supported subject types and RS256. OAuth-only deployments do not require OIDC signing metadata, but still need UserInfo, revocation, refresh, PKCE and the corresponding scopes. Both modes use `static` registration and `none` client authentication.
43
50
 
44
- Production endpoints use HTTPS. Discovery and credential-bearing requests do not automatically follow redirects; browser navigation is separate. Trusted metadata assigns each endpoint a role, not permission to receive every token. Saved authorization binds protocol, issuer, resource, client and token/revoke/model endpoints. A changed configuration or binding requires reauthorization, not silent token migration.
51
+ A discovery URL ending in `/.well-known/openid-configuration` must match the standard location for its issuer. `expectedIssuer`, when configured, pins an exact match; otherwise issuer and discovery must share an origin. Production endpoints use HTTPS. Discovery and credential-bearing requests do not follow redirects automatically, except for browser navigation. Authentication endpoints receive only the credentials intended for their role.
45
52
 
46
- Conflicting LiteLLM/oidc-llm markers, unknown versions, HTML and missing capabilities fail explicitly. Never downgrade by disabling ID Token validation.
53
+ Stored authorization binds protocol, issuer, resource, client and related endpoints. Changed bindings require new authorization. Unknown versions, simultaneous LiteLLM and oidc-llm markers, HTML and missing required capabilities fail explicitly; switching protocols, disabling signature checks or forwarding old Tokens cannot bypass validation.
47
54
 
48
- ## 3. Registration
55
+ ## 3. Client registration
49
56
 
50
- The default institutional arrangement is one preregistered public client ID shared by all installations, with independent user authorizations. No shared client secret belongs in the app. Register the loopback host/path and permit actual port variation as RFC 8252 allows; do not permit arbitrary redirect URLs.
57
+ The current integration uses **statically preregistered public clients**. An organization assigns an application client ID, while users authorize individually. The ID is neither a user account nor a secret. Desktop clients do not store a shared client secret.
51
58
 
52
- Optional dynamic registration advertises `dynamic` and `registration_endpoint`. Accept an RFC 7591 JSON POST:
59
+ The server registers allowed callback hosts and paths. EduWork uses `http://127.0.0.1:<random-port>/oauth/callback`, permitting variable loopback ports under RFC 8252 without permitting arbitrary hosts or paths. Exchange uses the same redirect URI as that authorization; refresh reuses its original client ID.
53
60
 
54
- ```json
55
- {
56
- "client_name": "EduWork",
57
- "redirect_uris": ["http://127.0.0.1:53187/oauth/callback"],
58
- "token_endpoint_auth_method": "none",
59
- "grant_types": ["authorization_code", "refresh_token"],
60
- "response_types": ["code"]
61
- }
62
- ```
61
+ Dynamic registration is outside the current oidc-llm integration requirements. ChatECNU's public discovery advertises only `static`. Any future dynamic extension needs separate discovery, registration authorization and callback rules; copying LiteLLM's registration flow does not establish compatibility.
63
62
 
64
- Return 201 with registered client ID, redirects, auth method, grants/response types and optional issue time. Version 0.1 automatic DCR covers public registration without a bootstrap secret; deployments requiring initial registration credentials use static registration first. Stateful or stateless registration is allowed; a client per user is not required. Refresh reuses the original registration.
63
+ ## 4. Authorization, exchange and identity
65
64
 
66
- ## 4. Authorization and tokens
65
+ | Identity mode | Scopes requested for this connection | Identity validation |
66
+ | --- | --- | --- |
67
+ | `oidc` | `openid profile offline_access llm:models:read llm:invoke` | Initial authorization requires an ID Token, followed by matching UserInfo |
68
+ | `oauth` | `llm:profile llm:models:read llm:invoke` | Reads UserInfo-shaped claims without claiming OIDC authentication |
67
69
 
68
- Proposed basic scopes are `llm:profile`, `llm:models:read`, `llm:invoke`: own profile, authorized catalog and inference. They do not grant key/user/team/billing administration. Model membership remains authorization-specific; server enforcement is required.
70
+ `llm:models:read` authorizes the user's available catalog and `llm:invoke` authorizes inference. OAuth mode uses `llm:profile` for user information. Full OIDC uses standard identity scopes without additionally requesting `llm:profile`. Other advertised worker, conversation, notebook or administration scopes are not requested automatically. The server must still enforce actual user permissions.
69
71
 
70
- Browser GET authorization endpoint includes code response type, client ID, exact redirect URI, random one-use state of at least 128 bits, S256 challenge, resource and scopes. The verifier has 43–128 characters. Consent shows app, account and permissions. Denial returns access_denied + state. Invalid clients/redirects fail directly without navigating an untrusted target.
72
+ Browser authorization sends `response_type=code`, `client_id`, `redirect_uri`, random `state`, PKCE `code_challenge` / `S256`, `resource` and `scope`. OIDC mode also sends a random `nonce` and `prompt=consent`. Rejection returns `error=access_denied` and the original state. Invalid clients or callbacks must not redirect to untrusted destinations.
71
73
 
72
- Success returns opaque code + original state. Proposed code lifetime is at most 120 seconds, one successful exchange, bound to subject, client, redirect, PKCE, resource and scopes.
74
+ Success returns `code` and the original state. When discovery declares `authorization_response_iss_parameter_supported: true`, both success and error callbacks must include an exactly matching `iss`. Codes are short-lived, single-use and bound to subject, client, redirect, PKCE, resource and approved scopes. Their lifetime follows server policy; this draft does not fix it at 120 seconds.
73
75
 
74
- Token endpoint accepts form POST:
76
+ The token endpoint accepts `application/x-www-form-urlencoded` POST:
75
77
 
76
78
  | Operation | Required fields |
77
79
  | --- | --- |
78
- | Exchange | authorization_code grant, client_id, code, redirect_uri, code_verifier, resource |
79
- | Refresh | refresh_token grant, client_id, refresh_token, resource |
80
+ | Exchange | `grant_type=authorization_code`, `client_id`, `code`, `redirect_uri`, `code_verifier`, `resource` |
81
+ | Refresh | `grant_type=refresh_token`, `client_id`, `refresh_token`, `resource` |
80
82
 
81
- Success example:
83
+ Example initial response for full OIDC mode (Tokens are placeholders; 3600 seconds is illustrative):
82
84
 
83
85
  ```json
84
86
  {
85
87
  "access_token": "OPAQUE_ACCESS_TOKEN",
86
88
  "token_type": "Bearer",
87
- "expires_in": 900,
89
+ "expires_in": 3600,
88
90
  "refresh_token": "OPAQUE_REFRESH_TOKEN",
89
- "scope": "llm:profile llm:models:read llm:invoke"
91
+ "scope": "openid profile offline_access llm:models:read llm:invoke",
92
+ "id_token": "SIGNED_ID_TOKEN"
90
93
  }
91
94
  ```
92
95
 
93
- Use `Cache-Control: no-store`. Clients treat access tokens as opaque, use returned expiry and verify granted scopes. A maximum 15-minute Access Token is proposed, pending deployment compatibility review.
96
+ Responses should prohibit caching. Access Tokens are opaque Bearer credentials and need not be JWTs. `expires_in` is a positive integer in seconds governed by the server response, with **no 15-minute limit**. The current client separately rejects values exceeding 365 days as a validity bound, not a recommended lifetime. The explicit `scope` must match this connection's requested set without missing, duplicate or additional permissions. OAuth mode returns its corresponding scope and needs no ID Token.
94
97
 
95
- Refresh rotates a single-use token and returns the complete new pair. Subject/resource do not change and scopes do not expand. Refresh-family reuse detection and authorization-wide revocation are proposed. Clients coalesce concurrent refresh and persist the pair before use. Lost responses leave uncertain submission state; recovery must be defined without unbounded replay of an old refresh token. Lifetime, coordination and persistence belong in server acceptance.
98
+ OIDC mode reuses the existing verifier for RS256 signatures, issuer, audience/azp, nonce, time and `at_hash` when present. A missing or invalid initial ID Token fails without OAuth fallback. Refresh may omit an ID Token; when returned, the original subject, audience and related claims are checked, preventing identity changes through refresh.
96
99
 
97
- ## 5. UserInfo
100
+ ## 5. UserInfo and model resources
98
101
 
99
- GET discovered `userinfo_endpoint` with Bearer; return 200 JSON:
102
+ The client sends the same Access Token to the discovered `userinfo_endpoint`, which returns 200 JSON, for example:
100
103
 
101
104
  ```json
102
105
  {
@@ -106,36 +109,59 @@ GET discovered `userinfo_endpoint` with Bearer; return 200 JSON:
106
109
  }
107
110
  ```
108
111
 
109
- For ordinary JSON UserInfo, **only a nonempty string sub is required**. Name, preferred_username, picture, email and email_verified retain standard OIDC meanings and remain optional under granted permissions. Missing presentation claims do not fail login. Identity uses issuer + sub, never email/name. Account and catalog caches are separated by authorization context.
112
+ Identity is keyed by issuer + sub, never a name or email address. The current adapter requires 1–255 non-whitespace visible ASCII characters for `sub`. Optional `name`, `preferred_username`, `picture` and `email` claims are strings; `email_verified` is boolean. Missing names or avatars do not fail sign-in. Full OIDC requires UserInfo sub to match the verified ID Token, and refresh must preserve the subject. OAuth mode does not present this profile lookup as OIDC authentication.
113
+
114
+ | Resource | Request and response |
115
+ | --- | --- |
116
+ | `GET api_base + /models` | Bearer; 200 OpenAI-style `{"object":"list","data":[{"id":"example-chat","object":"model"}]}`, limited to the current authorization |
117
+ | `POST api_base + /chat/completions` | Same Bearer; OpenAI-compatible requests with JSON or SSE responses (`data:`, ending with `[DONE]`) |
118
+
119
+ The server determines model access and quotas; client configuration cannot grant access. Empty catalogs stay empty. Profiles, catalogs and requests are isolated by authorization. The optional data[].type extension classifies models as llm, embedding, rerank, image, tts or unknown. Only LLMs enter the EduWork conversation directory. This is not a standard OpenAI discovery field: a server type takes precedence, while reviewed local types fill missing declarations. Unknown or unsupported types stay outside conversation. Image input, reasoning and context limits require separate explicit metadata; an image-capable LLM remains an LLM.
120
+
121
+ Image, audio and other APIs need separate agreements between the server and relevant plugins; OpenAI compatibility does not imply every API is supported. Quotas and school services remain institution extensions, not mandatory public endpoints.
122
+
123
+ ## 6. Refresh and long-running streams
110
124
 
111
- OAuth-only mode offers a UserInfo-shaped extension without claiming full OIDC identity validation. A profile outage does not necessarily invalidate model authorization, and clients must not parse an unverified token to invent a subject. Full OIDC mode requires UserInfo sub to match the validated ID Token.
125
+ A successful refresh returns a complete new Access Token / Refresh Token pair without changing subject or resource or expanding scope. Clients coalesce concurrent refreshes for one authorization and persist the pair before use. Refresh Token lifetime, treatment of old rotated values, reuse detection and family revocation must be documented by the server, not inferred from discovery.
112
126
 
113
- ## 6. Model resources
127
+ EduWork checks expiry before using authorization, with a margin of `min(30 minutes, half the issued Token lifetime)`. A two-hour Token refreshes when 30 minutes remain; a 20-minute Token when 10 minutes remain. This is a client policy before use, not the server's Token lifetime or a promise of background renewal while the app is closed.
114
128
 
115
- - GET api_base + `/models`: Bearer, 200 OpenAI-style `{"object":"list","data":[{"id":"example-chat","object":"model"}]}`, restricted to authorized models.
116
- - POST api_base + `/chat/completions`: same Bearer, OpenAI-compatible ordinary JSON or SSE with `data:` and final `[DONE]`.
117
- - Images, audio, Responses and other capabilities require future explicit declarations; “OpenAI-compatible” does not imply every API is supported.
129
+ Temporary network failures, 429 and 5xx do not automatically erase login. The client may use a still-valid Token but stops once it expires. Explicit `invalid_grant` / `invalid_client`, changed identity or newly returned credentials that cannot be accepted safely require sign-in. Recovery after a lost rotation response still needs a server contract; unlimited retries of an old Refresh Token are not assumed safe.
118
130
 
119
- Optional capability metadata for context/output limits, input modalities and reasoning remains to be agreed. An ID alone does not establish vision/reasoning support. No quota endpoint is defined.
131
+ **A new Token cannot replace the old Token in an already-sent SSE request.** Refreshing the same authorization does not itself cancel that request. Whether an accepted stream may cross Token expiry must be stated and tested by the server. A 30-minute margin is not an unlimited-duration guarantee. The authentication layer never automatically replays model generation, particularly after output has begun, avoiding duplicate generation and billing.
120
132
 
121
- ## 7. Revocation and errors
133
+ ## 7. Logout, revocation and errors
122
134
 
123
- RFC 7009 form POST takes token, public client_id and optional refresh_token type hint. Success is 200 and idempotent for unknown/invalid tokens. Revocation of the authorization's refresh family is proposed. Access tokens expire at their short lifetime unless the server invalidates them sooner; do not promise immediate global sign-out unconditionally.
135
+ Logout immediately stops local authorization use, clears credentials and associated model caches, cancels related requests, then attempts Refresh Token revocation. Late refresh, catalog or stream results cannot restore signed-out authorization. This does not sign the user out of the organization's browser SSO session.
124
136
 
125
- Local logout immediately stops authorization use and related cache writes. Report remote revocation failures separately. Late refresh/catalog responses cannot restore a signed-out or replaced account.
137
+ The revocation endpoint accepts RFC 7009 form fields `token`, public `client_id` and `token_type_hint=refresh_token`, returning 200 with an empty or JSON response. Revocation prevents subsequent use of the revoked Refresh Token; unknown or expired Tokens are handled idempotently. Clients accept natural server-side expiry of issued Access Tokens without requiring immediate global invalidation. The server must specify whether revocation covers an entire refresh family; this draft does not assume it.
126
138
 
127
- Use OAuth errors invalid_request, invalid_client, invalid_grant, invalid_scope, unsupported_grant_type and invalid_target for resource mismatch. 401 denotes invalid credentials, 403 denied permission, 429 throttling, 5xx server failures. Do not erase login for every failure. Never refresh and replay a generation after output has begun. Errors must not expose tokens, codes or account secrets.
139
+ A network failure during revocation does not block local logout; the client logs a sanitized warning. There is currently no persistent revocation retry queue across restarts. Local logout does not imply confirmed remote revocation.
128
140
 
129
- ## 8. Optional full OIDC
141
+ Use OAuth errors `invalid_request`, `invalid_client`, `invalid_grant`, `invalid_scope`, `unsupported_grant_type`, and `invalid_target` for resource mismatch. 401 is an authentication failure, 403 a permission failure, 429 throttling and 5xx a service failure; they must not all become sign-in prompts. Safe reads may refresh and retry once after 401; generation is not replayed automatically. Errors and logs must not disclose credentials.
142
+
143
+ ## 8. Example deployment
144
+
145
+ The following uses `models.example.org` to illustrate a deployment adopting this draft, consistent with the discovery example in section 2. These are example values, not metadata or acceptance results from a real service.
146
+
147
+ | Item | Example metadata |
148
+ | --- | --- |
149
+ | issuer / resource | `https://models.example.org` |
150
+ | Model API base | `https://models.example.org/open/api/v1` |
151
+ | Extension version | `oidc_llm.version: "0.1"` |
152
+ | Identity modes | `oauth`, `oidc`; the client explicitly selects `oidc` through configuration |
153
+ | Client registration | `static`, public-client authentication `none` |
154
+ | Authorization and refresh | Code, PKCE S256, Refresh Token |
155
+ | Identity validation | RS256, public JWKS, UserInfo, authorization callback `iss` |
130
156
 
131
- Advertise `oidc` identity mode and standard jwks_uri, signing algorithms and required OIDC metadata. Clients explicitly choose it, request openid/profile (optional email) plus model scopes, and use nonce. The basic OAuth llm:profile scope is not additionally required in this mode.
157
+ Servers may advertise additional business scopes, but this connection requests only those in section 4. Editions provide deployment addresses and a public client ID through configuration, not hardcoded public-plugin defaults.
132
158
 
133
- Validate signature, algorithm, issuer, audience/azp, nonce, time and applicable token bindings; UserInfo sub must match. Missing ID Token fails instead of silently switching modes. Existing OIDC validation remains intact and may be reused by the future adapter.
159
+ Discovery establishes advertised capabilities, not exact Token lifetimes, refresh-family reuse detection, immediate revocation, filtering for every account or stream behavior at expiry. These require real-server acceptance; an integration test or client implementation is not a complete server guarantee.
134
160
 
135
- ## 9. LiteLLM boundary and open decisions
161
+ ## 9. Compatibility and future extensions
136
162
 
137
- LiteLLM native is another supported compatibility protocol, not a wire-level subset of this draft. It uses its own discovery, registration and account responses; it lacks the same application scopes, revokes only the submitted refresh token and defaults to longer access lifetime. Adapters preserve these facts instead of attributing proposed guarantees to upstream.
163
+ LiteLLM native OAuth is another supported model integration route with its own discovery, dynamic registration and account responses. It is not a wire-level subset of this draft. Both adapters share credential storage, refresh coordination and request isolation while preserving protocol differences.
138
164
 
139
- Before implementation, agree scopes and enforcement, lifetimes/family guarantees, capability metadata, and offline/revocation/concurrent-refresh recovery. Confirm protocol and server first, add the new client adapter in a separate PR, then explicitly migrate institution configuration. Existing OIDC users do not switch automatically.
165
+ Future work concerns dynamic registration, shared model capability metadata and server contracts for lost refresh responses, Token families and long-running streams. Current integrations can implement and validate this document and the [experimental guide](experimental-oidc-llm.en.md) without waiting for those extensions. Existing deployments still require explicit configuration migration and new authorization.
140
166
 
141
- References: [OIDC UserInfo](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo), [Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html), [RFC 8414](https://www.rfc-editor.org/rfc/rfc8414.html), [PKCE](https://www.rfc-editor.org/rfc/rfc7636.html), [Native apps](https://www.rfc-editor.org/rfc/rfc8252.html), [DCR](https://www.rfc-editor.org/rfc/rfc7591.html), [Resources](https://www.rfc-editor.org/rfc/rfc8707.html), [Revocation](https://www.rfc-editor.org/rfc/rfc7009.html).
167
+ References: [OIDC Core](https://openid.net/specs/openid-connect-core-1_0.html), [OIDC Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html), [PKCE](https://www.rfc-editor.org/rfc/rfc7636.html), [native clients](https://www.rfc-editor.org/rfc/rfc8252.html), [Resource Indicators](https://www.rfc-editor.org/rfc/rfc8707.html), [authorization response issuer](https://www.rfc-editor.org/rfc/rfc9207.html), [revocation](https://www.rfc-editor.org/rfc/rfc7009.html).