@eduwork/dsh-oidc 0.2.4 → 0.3.0-dev.2

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 (72) hide show
  1. package/CHANGELOG.en.md +6 -0
  2. package/CHANGELOG.md +6 -0
  3. package/README.md +20 -132
  4. package/README_EN.md +20 -177
  5. package/docs/EDUWORK-MIGRATION.en.md +3 -25
  6. package/docs/EDUWORK-MIGRATION.md +3 -25
  7. package/docs/account-extensions.en.md +6 -6
  8. package/docs/account-extensions.md +3 -3
  9. package/docs/architecture.en.md +12 -117
  10. package/docs/architecture.md +12 -118
  11. package/docs/compatibility.en.md +6 -40
  12. package/docs/compatibility.md +6 -40
  13. package/docs/decisions/0001-one-plugin-composition-boundary.en.md +2 -2
  14. package/docs/decisions/0001-one-plugin-composition-boundary.md +2 -2
  15. package/docs/desktop-host.en.md +4 -5
  16. package/docs/desktop-host.md +8 -9
  17. package/docs/development.en.md +2 -2
  18. package/docs/development.md +2 -2
  19. package/docs/dsh-integration.en.md +11 -46
  20. package/docs/dsh-integration.md +12 -47
  21. package/docs/ecnu-reference.en.md +4 -61
  22. package/docs/ecnu-reference.md +4 -61
  23. package/docs/enterprise-profile.en.md +11 -32
  24. package/docs/enterprise-profile.md +12 -33
  25. package/docs/gateway-auth/README.md +168 -0
  26. package/docs/gateway-auth/README_EN.md +70 -0
  27. package/docs/gateway-auth/experimental-oidc-llm.en.md +53 -0
  28. package/docs/gateway-auth/experimental-oidc-llm.md +53 -0
  29. package/docs/gateway-auth/litellm-setup.en.md +114 -0
  30. package/docs/gateway-auth/litellm-setup.md +114 -0
  31. package/docs/gateway-auth/oidc-llm-draft.en.md +141 -0
  32. package/docs/gateway-auth/oidc-llm-draft.md +141 -0
  33. package/docs/getting-started.en.md +17 -122
  34. package/docs/getting-started.md +16 -225
  35. package/docs/key-binding-protocol.en.md +10 -220
  36. package/docs/key-binding-protocol.md +10 -220
  37. package/docs/oidc-interoperability.en.md +1 -1
  38. package/docs/oidc-interoperability.md +1 -1
  39. package/docs/open-integration.en.md +5 -67
  40. package/docs/open-integration.md +5 -67
  41. package/docs/public-resource-protocol.en.md +8 -44
  42. package/docs/public-resource-protocol.md +8 -43
  43. package/docs/security-model.en.md +10 -132
  44. package/docs/security-model.md +10 -132
  45. package/docs/server-integration-contract.en.md +12 -813
  46. package/docs/server-integration-contract.md +12 -825
  47. package/examples/ecnu.enterprise-profile.example.json +16 -20
  48. package/examples/enterprise-profile.example.json +16 -20
  49. package/examples/identity-only.example.json +7 -2
  50. package/examples/litellm.enterprise-profile.example.json +10 -0
  51. package/examples/oidc-llm.enterprise-profile.example.json +13 -0
  52. package/examples/resources-discovery.example.json +7 -9
  53. package/lib/callback-page.js +5 -5
  54. package/lib/client.js +31 -48
  55. package/lib/desktop-oidc.js +7 -11
  56. package/lib/gateway-backend.js +443 -0
  57. package/lib/gateway-protocol.js +6 -0
  58. package/lib/index.js +14 -15
  59. package/lib/litellm-protocol.js +117 -0
  60. package/lib/model-resource-transport.js +19 -0
  61. package/lib/oidc-llm-protocol.js +87 -0
  62. package/lib/oidc.js +40 -338
  63. package/lib/profile.js +42 -74
  64. package/lib/provider/authorization-scope.js +46 -0
  65. package/lib/provider/core.js +5 -16
  66. package/lib/provider/index.js +11 -2
  67. package/lib/transport.js +5 -0
  68. package/lib/typert-schemas.js +1 -1
  69. package/package.json +2 -2
  70. package/protocol/openapi.yaml +33 -240
  71. package/protocol/resources.openapi.yaml +8 -9
  72. package/schema/enterprise-profile.v1alpha1.schema.json +508 -107
@@ -0,0 +1,141 @@
1
+ # oidc-llm 0.1 protocol draft
2
+
3
+ [简体中文](oidc-llm-draft.md) | **English**
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).
6
+
7
+ ## 1. Scope
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.
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.
12
+
13
+ ## 2. Discovery
14
+
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:
16
+
17
+ ```json
18
+ {
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",
24
+ "response_types_supported": ["code"],
25
+ "grant_types_supported": ["authorization_code", "refresh_token"],
26
+ "code_challenge_methods_supported": ["S256"],
27
+ "token_endpoint_auth_methods_supported": ["none"],
28
+ "revocation_endpoint_auth_methods_supported": ["none"],
29
+ "scopes_supported": ["llm:profile", "llm:models:read", "llm:invoke"],
30
+ "oidc_llm": {
31
+ "version": "0.1",
32
+ "resource": "https://models.example.org",
33
+ "api_base": "https://models.example.org/v1",
34
+ "identity_modes_supported": ["oauth"],
35
+ "client_registration_methods_supported": ["static"]
36
+ }
37
+ }
38
+ ```
39
+
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.
41
+
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.
43
+
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.
45
+
46
+ Conflicting LiteLLM/oidc-llm markers, unknown versions, HTML and missing capabilities fail explicitly. Never downgrade by disabling ID Token validation.
47
+
48
+ ## 3. Registration
49
+
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.
51
+
52
+ Optional dynamic registration advertises `dynamic` and `registration_endpoint`. Accept an RFC 7591 JSON POST:
53
+
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
+ ```
63
+
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.
65
+
66
+ ## 4. Authorization and tokens
67
+
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.
69
+
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.
71
+
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.
73
+
74
+ Token endpoint accepts form POST:
75
+
76
+ | Operation | Required fields |
77
+ | --- | --- |
78
+ | Exchange | authorization_code grant, client_id, code, redirect_uri, code_verifier, resource |
79
+ | Refresh | refresh_token grant, client_id, refresh_token, resource |
80
+
81
+ Success example:
82
+
83
+ ```json
84
+ {
85
+ "access_token": "OPAQUE_ACCESS_TOKEN",
86
+ "token_type": "Bearer",
87
+ "expires_in": 900,
88
+ "refresh_token": "OPAQUE_REFRESH_TOKEN",
89
+ "scope": "llm:profile llm:models:read llm:invoke"
90
+ }
91
+ ```
92
+
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.
94
+
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.
96
+
97
+ ## 5. UserInfo
98
+
99
+ GET discovered `userinfo_endpoint` with Bearer; return 200 JSON:
100
+
101
+ ```json
102
+ {
103
+ "sub": "opaque-user-id",
104
+ "name": "Example user",
105
+ "preferred_username": "example-user"
106
+ }
107
+ ```
108
+
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.
110
+
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.
112
+
113
+ ## 6. Model resources
114
+
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.
118
+
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.
120
+
121
+ ## 7. Revocation and errors
122
+
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.
124
+
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.
126
+
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.
128
+
129
+ ## 8. Optional full OIDC
130
+
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.
132
+
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.
134
+
135
+ ## 9. LiteLLM boundary and open decisions
136
+
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.
138
+
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.
140
+
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).
@@ -0,0 +1,141 @@
1
+ # oidc-llm 0.1 协议草案
2
+
3
+ **简体中文** | [English](oidc-llm-draft.en.md)
4
+
5
+ **状态:待评审,不作为已发布部署契约。** 本分支已有默认关闭的[实验适配器](experimental-oidc-llm.md),实现范围与限制以其文档为准;服务端行为仍需单独实测。名称、扩展字段、scope、寿命和撤销要求尚未定稿;不是已发布的 OpenID 标准。现有 OIDC 接入继续使用[当前契约](../server-integration-contract.md)。[网关接入入口](README.md) · [English overview](README_EN.md#existing-oidc-and-the-future-draft)
6
+
7
+ ## 1. 范围
8
+
9
+ 浏览器登录后,桌面公共客户端用 Access Token 获取本人的资料、可用模型目录并调用模型。协议复用 OAuth Authorization Code、PKCE S256、loopback 回调和标准 OIDC 字段。基础 OAuth 模式不要求 ID Token;需要严格身份验证的完整 OIDC 模式仍必须验证 ID Token。
10
+
11
+ 不定义 Key Binding、配额标准、团队管理、计费或远程插件执行。客户端和服务端不能把任意普通 OIDC Token 自动当作模型凭据。
12
+
13
+ ## 2. 服务发现
14
+
15
+ 配置提供完整发现地址和可选的 `expectedIssuer`,预注册机构另提供 public `clientId`。客户端不固定账号 API 路径。无凭据 GET 成功返回 200 `application/json`:
16
+
17
+ ```json
18
+ {
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",
24
+ "response_types_supported": ["code"],
25
+ "grant_types_supported": ["authorization_code", "refresh_token"],
26
+ "code_challenge_methods_supported": ["S256"],
27
+ "token_endpoint_auth_methods_supported": ["none"],
28
+ "revocation_endpoint_auth_methods_supported": ["none"],
29
+ "scopes_supported": ["llm:profile", "llm:models:read", "llm:invoke"],
30
+ "oidc_llm": {
31
+ "version": "0.1",
32
+ "resource": "https://models.example.org",
33
+ "api_base": "https://models.example.org/v1",
34
+ "identity_modes_supported": ["oauth"],
35
+ "client_registration_methods_supported": ["static"]
36
+ }
37
+ }
38
+ ```
39
+
40
+ 以上字段拟为基础模式必填。`version` 精确匹配支持版本;0.x 不承诺跨版本兼容。`resource` 只有一个,授权和 Token 请求必须原样发送。`api_base` 是可配置 API 前缀,追加 `/models` 和 `/chat/completions`,不是强制 `/v1`。本草案限定 api_base 与 UserInfo 同 resource 源;issuer 可以是独立登录域名。该同源约束属于本草案,不是标准 OIDC 的要求。
41
+
42
+ `userinfo_endpoint` 在标准 OIDC Discovery 中是推荐项,在本接入草案中提升为必填。发现元数据还须声明每一种实际支持模式的 scopes。标准发现地址按对应规范校验 issuer;配置 `expectedIssuer` 时必须完整字符串相等。未配置时,首次发现的 issuer 与发现地址同源;异源托管需要显式固定 issuer。
43
+
44
+ 所有生产端点使用 HTTPS。发现和携带凭据的请求不自动跟随重定向;浏览器导航除外。认证端点来自可信发现,仅接收其角色对应的凭据;模型 Token 不被任意转发到发现提到的其他 URL。保存的授权绑定协议、issuer、resource、client、token/revoke/model 端点;配置或绑定改变要求重新授权,不能默默迁移旧 Token。
45
+
46
+ 发现同时包含 LiteLLM 与 oidc-llm 标志、未知版本、HTML 或缺关键能力时停止,不通过关闭验签或换协议来降级。
47
+
48
+ ## 3. 客户端注册
49
+
50
+ 默认支持机构预注册的一个公共客户端,所有用户共用 client ID,但各自有独立授权。客户端不保存共享 client secret。允许的 loopback host/path 必须预注册;按照 RFC 8252 允许实际端口变化,不能接受任意回调地址。
51
+
52
+ 可选动态注册需在 `client_registration_methods_supported` 中声明 `dynamic` 并提供 `registration_endpoint`。按 RFC 7591 接受 JSON POST:
53
+
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
+ ```
63
+
64
+ 成功 201,返回已登记的 `client_id`、回调、认证方法、grants/response types 和可选签发时间。0.1 的自动动态注册只覆盖无 bootstrap secret 的 public 注册;需要注册凭据的机构先用预注册模式。注册可以有状态或无状态,不强制“每人一个 client”。刷新复用原注册。
65
+
66
+ ## 4. 授权、换码与刷新
67
+
68
+ 基础 OAuth 模式拟申请 `llm:profile`、`llm:models:read`、`llm:invoke`。分别授权本人资料、获准模型目录、模型调用,不包括管理 Key、用户、团队或账单。具体模型集合仍由授权决定;服务端必须实际约束权限,不能仅靠客户端自律。
69
+
70
+ 浏览器 GET authorization endpoint,必须带 `response_type=code`、client ID、精确 redirect URI、至少 128 bit 随机 state、S256 challenge、resource 和 scopes。verifier 为 43–128 字符。用户看到应用、账户和申请权限,拒绝以 `error=access_denied&state=...` 回调。无效 client/回调直接返回错误,不重定向到未信任地址。
71
+
72
+ 成功回调 `code` 和原 state。授权码不透明、有效期拟不超过 120 秒、单次成功兑换,绑定主体、client、redirect、PKCE、resource 和批准 scopes。
73
+
74
+ token endpoint 接受 form POST:
75
+
76
+ | 操作 | 必须字段 |
77
+ | --- | --- |
78
+ | 换码 | `grant_type=authorization_code`、`client_id`、`code`、`redirect_uri`、`code_verifier`、`resource` |
79
+ | 刷新 | `grant_type=refresh_token`、`client_id`、`refresh_token`、`resource` |
80
+
81
+ 成功响应示例:
82
+
83
+ ```json
84
+ {
85
+ "access_token": "OPAQUE_ACCESS_TOKEN",
86
+ "token_type": "Bearer",
87
+ "expires_in": 900,
88
+ "refresh_token": "OPAQUE_REFRESH_TOKEN",
89
+ "scope": "llm:profile llm:models:read llm:invoke"
90
+ }
91
+ ```
92
+
93
+ 响应应 `Cache-Control: no-store`。客户端不假定 Access Token 是 JWT,以 `expires_in` 判断有效期,核对实际批准 scopes。拟要求 Access Token 不超过 15 分钟;具体上限和部署兼容性尚待确认。
94
+
95
+ Refresh Token 轮换、单次使用;成功时新 Token 对整体返回,授权主体/resource 不变,不扩大 scopes。服务端拟支持刷新家族重用检测和授权级撤销;客户端对同一授权合并并发刷新,保存新 Token 对后再使用。网络中断后的提交状态不确定,需要明确错误恢复策略,不能无边界重复提交旧 refresh。Token 生命周期、并发协调和持久化是服务端验收项。
96
+
97
+ ## 5. UserInfo
98
+
99
+ GET 发现的 `userinfo_endpoint`,`Authorization: Bearer <access_token>`,成功 200 JSON:
100
+
101
+ ```json
102
+ {
103
+ "sub": "opaque-user-id",
104
+ "name": "示例用户",
105
+ "preferred_username": "example-user"
106
+ }
107
+ ```
108
+
109
+ 普通 JSON UserInfo **只要求非空字符串 `sub`**。`name`、`preferred_username`、`picture`、`email`、`email_verified` 使用 OIDC 标准含义、按权限可选;缺少姓名/头像不使登录失败。主体键为 issuer + sub,不能用邮箱或显示名替代。不同授权的模型和资料缓存隔离,不因同名账户而合并。
110
+
111
+ OAuth-only 模式提供 UserInfo 格式兼容扩展,不宣称完成 OIDC 身份验证。资料暂时不可用不等于模型授权一定失效;客户端不能自行解析未经验证的 Token 来填充身份。完整 OIDC 模式必须校验返回 sub 与已验证 ID Token 的 sub 相同。
112
+
113
+ ## 6. 模型资源
114
+
115
+ - GET `api_base + /models`:Bearer,200 OpenAI 风格 `{"object":"list","data":[{"id":"example-chat","object":"model"}]}`,仅列当前授权可见模型。
116
+ - POST `api_base + /chat/completions`:相同 Bearer,使用 OpenAI-compatible 请求与普通 JSON/SSE(`data:`,最终 `[DONE]`)响应。
117
+ - 其他图像、音频、Responses 等能力需以后显式声明;不因“OpenAI-compatible”就假定全部支持。
118
+
119
+ 上下文、输出上限、输入模态、思考能力等可选元数据的字段格式尚待确认。只有模型 ID 时不能推断图像或 reasoning 支持。本轮不定义配额接口。
120
+
121
+ ## 7. 撤销与错误
122
+
123
+ revocation endpoint 按 RFC 7009 接受 form `token`、public `client_id` 和可选 `token_type_hint=refresh_token`。成功 200,未知或已失效 Token 保持幂等。拟要求撤销该授权的 Refresh Token 家族;Access Token 最迟随其短寿命失效,服务端可额外主动失效。不能宣传无条件、即时的全服务下线。
124
+
125
+ 退出立即停止本地授权使用和相关缓存写回;撤销网络失败与本地退出分开报告。迟到的刷新/目录结果不能恢复已退出或被新账户替换的授权。
126
+
127
+ OAuth 错误沿用 `invalid_request`、`invalid_client`、`invalid_grant`、`invalid_scope`、`unsupported_grant_type`,resource 不匹配使用 `invalid_target`。401 表示凭据无效,403 表示无权限,429 表示限流,5xx 表示服务异常;不要用清空账户处理所有失败。模型请求开始输出后不得自动刷新并重放整个生成。错误响应不能泄露令牌、验证码或账户秘密。
128
+
129
+ ## 8. 可选完整 OIDC 模式
130
+
131
+ 服务端在 `identity_modes_supported` 中额外声明 `oidc`,提供标准 `jwks_uri`、签名算法与 OIDC 必需元数据。客户端显式选择该模式,请求 `openid profile`(按需 `email`)和模型 scopes,使用 nonce;不额外要求基础 OAuth 模式的 `llm:profile`。
132
+
133
+ 客户端按 OIDC 规范验证签名、算法、issuer、audience/azp、nonce、时间及相关 Token 绑定,UserInfo sub 必须一致。缺少 ID Token 时失败,不能临时切回 OAuth-only。当前旧 OIDC 的安全校验应继续保留,未来实现可以复用,不能为另一 adapter 全局关闭。
134
+
135
+ ## 9. 与 LiteLLM 的关系及待定项
136
+
137
+ LiteLLM native 是客户端支持的另一兼容协议,不是本草案逐接口的子集:它使用动态注册、自己的发现与账户响应,未提供相同应用 scopes,且只撤销提交的 refresh、默认 Access Token 更长。这些差异由 adapter 保留,不把期望写成上游事实。
138
+
139
+ 定稿前需确认:scope 名及强制范围;Token 有效期与刷新家族保证;模型能力元数据;掉线、撤销失败和并发刷新恢复契约。实现顺序是先确认文档与服务端,再在独立 PR 中增加客户端新 adapter,最后由机构配置显式迁移。旧 OIDC 用户不会自动切协议。
140
+
141
+ 规范依据:[OIDC Core / UserInfo](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo)、[OIDC 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)、[原生客户端](https://www.rfc-editor.org/rfc/rfc8252.html)、[动态注册](https://www.rfc-editor.org/rfc/rfc7591.html)、[Resource Indicators](https://www.rfc-editor.org/rfc/rfc8707.html)、[撤销](https://www.rfc-editor.org/rfc/rfc7009.html)。
@@ -1,133 +1,28 @@
1
- # Getting started: connect enterprise OIDC and models to DSH
1
+ # Integration guide
2
2
 
3
3
  [简体中文](getting-started.md) | **English**
4
4
 
5
- This guide is for operators integrating `dsh-oidc` into their own DeepSeek Harness deployment. The result is a closed loop: users sign in through the organization's OIDC Provider, explicitly bind a revocable model runtime credential, inspect the enterprise Provider and model catalog, and call those models from ordinary DSH Web.
5
+ Organization model requests use the Access Token obtained at sign-in. Choose the actual server contract before configuring the complete discovery URL.
6
6
 
7
- The same OIDC implementation supports both desktop shells and local Web validation. It does not require Wails or an ECNU service. This guide uses Web commands; new desktop compositions use `backend: desktop` as described in [desktop integration](desktop-host.en.md).
7
+ | Service | Example | Registration |
8
+ | --- | --- | --- |
9
+ | LiteLLM native contract 1 | [LiteLLM](../examples/litellm.enterprise-profile.example.json) | Contract-defined dynamic registration |
10
+ | Experimental oidc-llm 0.1 | [OIDC Token](../examples/oidc-llm.enterprise-profile.example.json) | Static public client; explicit oidc or oauth mode |
11
+ | Standard identity-only OIDC | [Identity](../examples/identity-only.example.json) | Static public client; no organization models |
8
12
 
9
- ## 1. Prerequisites
13
+ OIDC mode requires browser sign-in, Code + PKCE, a valid ID Token and UserInfo. The server must authorize the Access Token for model access; entering a model URL cannot grant permission. See the [server contract](server-integration-contract.en.md).
10
14
 
11
- | Component | Minimum requirement |
12
- | --- | --- |
13
- | DSH Host | This candidate targets DeepSeek Harness `0.1.5-rc.1` on Node.js 22+; do not mix it with the historical stable package. |
14
- | Institutional enterprise-model integration service | Identity-only mode needs OIDC. Managed models additionally require the fixed Key Binding API and OpenAI-compatible gateway. |
15
- | Enterprise Profile | Trusted local JSON containing public configuration only. |
16
- | Credential Provider | The current Web backend supports trusted single-user local storage only. |
15
+ ## Host configuration
17
16
 
18
- Have the identity and model-platform teams jointly review the [server API specification](server-integration-contract.en.md). OIDC alone establishes identity; without Key Binding and the model gateway it does not safely complete model authorization. Do not place a shared model API key in an Enterprise Profile.
17
+ Load trusted JSON through config.profile, config.profiles or EDUWORK_OIDC_PROFILE. Desktop uses backend: desktop, Host credential storage and desktopServices.openExternal, with a temporary 127.0.0.1 callback. Local Web uses backend: web with WebServer bound to 127.0.0.1 and its actual port.
19
18
 
20
- ## 2. Register the OIDC Public Client
19
+ These changes are for development assemblies containing this branch, not yet published. Follow [development](development.en.md) for validation. Released assemblies must pin reviewed published packages; old packages cannot consume these new profiles.
21
20
 
22
- Register a public client without a client secret. Its redirect URI must exactly equal:
21
+ ## Sign-in and use
23
22
 
24
- ```text
25
- http://127.0.0.1:3080/oauth/callback
26
- ```
23
+ 1. Complete browser consent. The client checks state, PKCE and applicable identity claims.
24
+ 2. Discover models and connect the DSH Provider using the same authorization, without model-key provisioning.
25
+ 3. Refresh near expiry while retaining identity and authorization context.
26
+ 4. Logout clears local state and stops using the old authorization. Server token lifetime follows deployment policy.
27
27
 
28
- This fixed-port example is for local Web validation. New desktops default to a temporary random port; see [desktop integration](desktop-host.en.md) for fixed-port IdPs. `127.0.0.1` and `/oauth/callback` are fixed. `3080` is the recommended DSH port; if the DSH WebServer port changes, the registered URI must use the same port. Do not register `localhost`, a LAN address, or a public address. Discovery must publish `authorization_endpoint`, `token_endpoint`, `jwks_uri`, and `userinfo_endpoint`. UserInfo must contain a non-empty `sub` matching the ID Token; provide the standard `name` claim for a human-readable label. The current release accepts RS256 ID Tokens only.
29
-
30
- See the complete [OIDC interoperability profile](oidc-interoperability.en.md).
31
-
32
- ## 3. Implement the complete institutional service
33
-
34
- For managed enterprise models, provide OIDC, the Key Binding group below and an OpenAI-compatible gateway accepting the bound key. Identity-only deployments may skip this section. Internal systems may host different interfaces, but versioning, security, and operations have one accountable integration boundary. See the [server API specification](server-integration-contract.en.md).
35
-
36
- The configured `keyBinding.baseURL` has four fixed operations:
37
-
38
- ```text
39
- GET /bootstrap
40
- POST /runtime-credential/provision
41
- POST /runtime-credential/resolve
42
- POST /runtime-credential/renew
43
- ```
44
-
45
- The Profile may additionally use optional `keyBinding.credentialRef` to isolate entries in the local DSH Credential Provider. It is not sent over the network and does not alter these operations. Its default is `EDUWORK_API_KEY`; explicit use of that name is also accepted. Use another reference only for independently managed credentials. See the [Profile specification](enterprise-profile.en.md) for legacy migration and ownership checks.
46
-
47
- Every request carries the OIDC Access Token as a Bearer credential. The service must validate issuer, audience, expiry, scopes, subject, and organization authorization. It must not trust `provider_id` by itself. Credential responses are `no-store`, and `api_key` must never enter logs, metrics labels, or error details.
48
-
49
- Use the normative [Key Binding protocol](key-binding-protocol.en.md) and machine-readable [`protocol/openapi.yaml`](../protocol/openapi.yaml). OIDC and the model gateway use their established interfaces, so this project does not duplicate them in another OpenAPI document.
50
-
51
- ## 4. Create an Enterprise Profile
52
-
53
- Copy [`examples/enterprise-profile.example.json`](../examples/enterprise-profile.example.json) and replace the organization, bounded brand values, OIDC public-client facts, Key Binding base URL, OpenAI-compatible Provider base URL, and model catalog. Configure optional `keyBinding.credentialRef` only when local keys must be isolated across environments.
54
-
55
- The Profile is trusted deployment data, not a secret store. It must not contain client secrets, tokens, API keys, signing keys, cookies, or personal records. See the full [Enterprise Profile specification](enterprise-profile.en.md).
56
-
57
- ## 5. Install into ordinary DSH Web
58
-
59
- The package is an installable DSH Bundle. Pin `0.2.0` with a coherent DSH `0.1.5-rc.1` host. Consult the npm registry for publication status; during release preparation use a frozen candidate or source checkout if the version is unavailable:
60
-
61
- ```bash
62
- dsh plugin --profile web add @eduwork/dsh-oidc@0.2.0
63
- ```
64
-
65
- For auditing, development, or testing unpublished changes, install from a local checkout instead:
66
-
67
- ```bash
68
- git clone https://github.com/ecnu/EduWork.git
69
- cd EduWork/packages/dsh-oidc
70
- npm ci
71
- npm run check
72
- dsh plugin --profile web add .
73
- ```
74
-
75
- From the parent directory, the equivalent explicit relative-path command is:
76
-
77
- ```bash
78
- dsh plugin --profile web add ./EduWork/packages/dsh-oidc
79
- ```
80
-
81
- DSH links the checkout into `$DSH_HOME/profiles/web`; it does not scan or copy the invoking directory. Keep the checkout in place and pin a reviewed commit for source-based deployments. Production and team Profiles should use an exact npm version and must not mix another DSH prerelease line.
82
-
83
- Set the trusted local Profile path and start DSH on the fixed loopback host:
84
-
85
- ```bash
86
- export EDUWORK_OIDC_PROFILE=/etc/dsh/enterprise-profile.json
87
- dsh --profile web --host 127.0.0.1 --port 3080 --no-open
88
- ```
89
-
90
- The plugin constructs `http://127.0.0.1:<port>/oauth/callback` from the DSH WebServer port. It does not accept `publicBaseURL` and does not infer an address from proxy headers. If the startup port changes, update the OIDC Public Client registration to the same port.
91
-
92
- The package's `cordis.patch.yml` mounts one `enterprise-oidc` plugin instance. It does not traverse the current workspace or replace DSH Web, sessions, Workspace, LLM services, the Credential Provider, or other official capabilities. The invoking directory matters only when resolving a relative package path such as `.` or `./EduWork/packages/dsh-oidc`.
93
-
94
- ## 6. Advanced product composition
95
-
96
- A product-owned Bundle may mount the plugin explicitly instead of using the default package patch:
97
-
98
- ```yaml
99
- - insert:
100
- - id: enterprise-oidc
101
- name: '@eduwork/dsh-oidc'
102
- config:
103
- profilePathEnv: EDUWORK_OIDC_PROFILE
104
- web:
105
- returnPath: /
106
- ```
107
-
108
- New desktop products should use `backend: desktop`, sharing the temporary loopback callback and official `nativeCommand`; see [desktop integration](desktop-host.en.md). Legacy hosts may still use `backend: native`, point `profilePathEnv` at the same standard Enterprise Profile used by Web, and supply the capability-detected `enterpriseAccounts` service. Product-owned institution, tenant, or account catalogs MUST be converted to an Enterprise Profile by the product assembly layer before `dsh-oidc` reads them. Wails, Electron, Tauri, and other hosts can implement that boundary; the plugin depends on neither a desktop framework nor a product catalog format. See [DSH integration](dsh-integration.en.md).
109
-
110
- ## 7. Acceptance checklist
111
-
112
- Before production, verify at least:
113
-
114
- 1. `dsh --profile web --dump-config` contains exactly one `enterprise-oidc` instance;
115
- 2. login and the exact `/oauth/callback` succeed while invalid state and duplicate callback parameters fail;
116
- 3. the UI displays standard UserInfo `name`, falling back to stable `sub`;
117
- 4. provisioning requires explicit user confirmation;
118
- 5. a provisioned model completes a streaming conversation;
119
- 6. no API key appears in the Profile, browser configuration, logs, or exported sessions;
120
- 7. refresh, expiry, revocation, authorization denial, rate limiting, and backend outage behavior are understandable;
121
- 8. logout clears local OIDC and model credentials and attempts standard token revocation;
122
- 9. the DSH WebServer listens only on `127.0.0.1` and is not exposed as a shared site through a proxy or port mapping;
123
- 10. OIDC, all four Key Binding operations, and the model gateway jointly pass the [server API specification](server-integration-contract.en.md).
124
-
125
- The current Web backend does not support shared Web. A shared scenario needs a new host backend with per-user isolation; do not simply relax the loopback restriction. See the [security model](security-model.en.md) and [public release checklist](release-checklist.en.md).
126
-
127
- ## 8. Troubleshooting
128
-
129
- - **Callback fails:** compare the registered URI with `http://127.0.0.1:<DSH-port>/oauth/callback` character for character. Confirm the DSH host is `127.0.0.1` and its actual port matches the registration; do not use `localhost`, a LAN address, or a public address.
130
- - **A subject string is shown instead of a name:** return a non-empty standard `name` from UserInfo under the `profile` scope; private JSON-path mappings are intentionally unsupported.
131
- - **Login works but models do not:** inspect `/bootstrap`, explicit provisioning state, exact `provider_id` equality, and whether the model gateway accepts the bound key.
132
- - **Thinking works but selectable effort does not:** set model `compat.supportsReasoningEffort` to `false`; thinking remains enabled while the unsupported effort parameter is removed.
133
- - **Where enterprise controls live:** the official `settings.models.footer` slot displays configured institutions while keeping personal API keys in the upstream Models settings. Institutions come from trusted distribution files; products may offer a file-opening action. The plugin does not expose a form for arbitrary endpoint changes.
28
+ Quota belongs to optional [account extensions](account-extensions.en.md). Use the [migration guide](key-binding-protocol.en.md) for old profiles. Never put passwords, client secrets or user tokens in configuration or logs.