@eduwork/dsh-oidc 0.2.3 → 0.3.0-dev.1
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.
- package/CHANGELOG.en.md +6 -38
- package/CHANGELOG.md +7 -39
- package/CODE_OF_CONDUCT.en.md +1 -1
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTING.en.md +1 -1
- package/CONTRIBUTING.md +1 -1
- package/README.md +21 -130
- package/README_EN.md +36 -0
- package/SECURITY.en.md +1 -1
- package/SECURITY.md +1 -1
- package/SUPPORT.en.md +1 -1
- package/SUPPORT.md +1 -1
- package/docs/EDUWORK-MIGRATION.en.md +3 -31
- package/docs/EDUWORK-MIGRATION.md +3 -31
- package/docs/account-extensions.en.md +6 -6
- package/docs/account-extensions.md +3 -3
- package/docs/architecture.en.md +12 -117
- package/docs/architecture.md +12 -118
- package/docs/compatibility.en.md +6 -62
- package/docs/compatibility.md +6 -62
- package/docs/decisions/0001-one-plugin-composition-boundary.en.md +2 -2
- package/docs/decisions/0001-one-plugin-composition-boundary.md +2 -2
- package/docs/desktop-host.en.md +54 -55
- package/docs/desktop-host.md +63 -64
- package/docs/development.en.md +44 -44
- package/docs/development.md +6 -6
- package/docs/dsh-integration.en.md +13 -48
- package/docs/dsh-integration.md +14 -49
- package/docs/ecnu-reference.en.md +4 -61
- package/docs/ecnu-reference.md +4 -61
- package/docs/enterprise-profile.en.md +10 -31
- package/docs/enterprise-profile.md +11 -32
- package/docs/gateway-auth/README.md +168 -0
- package/docs/gateway-auth/README_EN.md +70 -0
- package/docs/gateway-auth/experimental-oidc-llm.en.md +53 -0
- package/docs/gateway-auth/experimental-oidc-llm.md +53 -0
- package/docs/gateway-auth/litellm-setup.en.md +114 -0
- package/docs/gateway-auth/litellm-setup.md +114 -0
- package/docs/gateway-auth/oidc-llm-draft.en.md +141 -0
- package/docs/gateway-auth/oidc-llm-draft.md +141 -0
- package/docs/getting-started.en.md +17 -122
- package/docs/getting-started.md +16 -225
- package/docs/key-binding-protocol.en.md +10 -220
- package/docs/key-binding-protocol.md +10 -220
- package/docs/oidc-interoperability.en.md +1 -1
- package/docs/oidc-interoperability.md +1 -1
- package/docs/open-integration.en.md +11 -0
- package/docs/open-integration.md +11 -0
- package/docs/public-resource-protocol.en.md +8 -44
- package/docs/public-resource-protocol.md +8 -43
- package/docs/release-checklist.en.md +7 -61
- package/docs/release-checklist.md +7 -61
- package/docs/security-model.en.md +10 -132
- package/docs/security-model.md +10 -132
- package/docs/server-integration-contract.en.md +12 -811
- package/docs/server-integration-contract.md +12 -823
- package/examples/ecnu.enterprise-profile.example.json +14 -19
- package/examples/enterprise-profile.example.json +73 -78
- package/examples/identity-only.example.json +1 -1
- package/examples/litellm.enterprise-profile.example.json +9 -0
- package/examples/oidc-llm.enterprise-profile.example.json +12 -0
- package/examples/resources-discovery.example.json +6 -9
- package/lib/callback-page.js +5 -5
- package/lib/client.js +31 -48
- package/lib/desktop-oidc.js +7 -11
- package/lib/gateway-backend.js +443 -0
- package/lib/gateway-protocol.js +6 -0
- package/lib/index.js +14 -15
- package/lib/litellm-protocol.js +117 -0
- package/lib/model-resource-transport.js +19 -0
- package/lib/oidc-llm-protocol.js +88 -0
- package/lib/oidc.js +37 -323
- package/lib/profile.js +34 -40
- package/lib/provider/authorization-scope.js +46 -0
- package/lib/provider/index.js +11 -2
- package/lib/provider/transforms.js +0 -1
- package/lib/typert-schemas.js +1 -1
- package/lib/typert.host.js +38 -38
- package/lib/typert.remote-client.js +37 -37
- package/package.json +7 -6
- package/protocol/openapi.yaml +33 -240
- package/protocol/resources.openapi.yaml +8 -9
- package/schema/enterprise-profile.v1alpha1.schema.json +595 -193
- package/README.en.md +0 -186
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# oidc-llm 实验接入
|
|
2
|
+
|
|
3
|
+
**简体中文** | [English](experimental-oidc-llm.en.md)
|
|
4
|
+
|
|
5
|
+
本分支提供 oidc-llm 0.1 的实验适配器,默认关闭,尚未发布。协议仍在讨论;本页描述已实现的客户端边界,不表示[完整草案](oidc-llm-draft.md)已经定稿或服务端已经通过验收。
|
|
6
|
+
|
|
7
|
+
## 配置
|
|
8
|
+
|
|
9
|
+
在桌面配置的 `organizations` 数组或插件的 `profiles` 中填写以下对象。服务器须明确发布完整的 `oidc_llm` 扩展;普通 OIDC 文档不会自动启用模型访问。
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"schemaVersion": "dsh-oidc/v1alpha1",
|
|
14
|
+
"id": "example-models",
|
|
15
|
+
"displayName": "示例模型服务",
|
|
16
|
+
"auth": {
|
|
17
|
+
"discoveryUrl": "https://models.example.org/.well-known/openid-configuration",
|
|
18
|
+
"expectedIssuer": "https://models.example.org",
|
|
19
|
+
"experimentalOidcLlm": true,
|
|
20
|
+
"clientId": "replace-with-public-client-id",
|
|
21
|
+
"identityMode": "oidc"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
必须显式选择 `identityMode`。两种模式不自动互相降级:
|
|
27
|
+
|
|
28
|
+
| 模式 | 请求范围 | 身份验证 |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| `oidc` | `openid profile offline_access llm:models:read llm:invoke`,并请求 consent | 首次必须有 RS256 ID Token;验证签名、issuer、aud/azp、nonce、时间、可选 at_hash 与 UserInfo sub |
|
|
31
|
+
| `oauth` | `llm:profile llm:models:read llm:invoke` | 从发现的 UserInfo 格式接口读取 sub;不宣称完成 OIDC 身份验证 |
|
|
32
|
+
|
|
33
|
+
目前仅实现预注册 public client,不能填写 client secret。服务器注册须允许实际 IPv4 loopback 回调 `http://127.0.0.1:<随机端口>/oauth/callback`。动态注册留待后续实现。
|
|
34
|
+
|
|
35
|
+
发现声明 `authorization_response_iss_parameter_supported: true` 时,成功和错误授权回调都必须带 `iss`,且与发现中的 `issuer` 完全一致。缺失会报告 `gateway_callback_issuer_missing`;重复、空值或不匹配会报告 `gateway_callback_issuer_invalid`。客户端不会继续换取 Token,应由认证服务管理员核对发现声明与回调实现后重新发起登录。桌面回调监听在流程结束后关闭,刷新旧回调地址不能恢复登录。
|
|
36
|
+
|
|
37
|
+
生产端点须为 HTTPS。独立实验环境可复用既有 `allowInsecureDevelopment: true` 和 `insecureDevelopmentOrigin`,但必须同时固定 `expectedIssuer`;HTTP 端点只允许 loopback 或该完整原点,不扩大到整个内网。此例外不改变 LiteLLM native 的 loopback 限制。
|
|
38
|
+
|
|
39
|
+
## 共享实现与边界
|
|
40
|
+
|
|
41
|
+
两套网关适配器共用发现请求、Code+PKCE、浏览器/回调、Host 凭据库、并发刷新合并、模型目录、DSH Provider 与流式请求隔离。OIDC 身份分支直接复用现有严格验证器;保留独立的纯身份 `oidc` 登录;旧模型 Key Binding 已移除,见[迁移说明](../key-binding-protocol.md)。
|
|
42
|
+
|
|
43
|
+
Access Token 按不透明 Bearer 处理。发现中的 `api_base` 与 UserInfo 必须和 resource 同源;认证端点只接收其角色对应的凭据,禁自动重定向。配置或发现绑定改变时不复用旧授权。Token 响应必须提供实际 scope、正整数有效期和 refresh token;scope 不得扩大或缺少本连接需要的权限。有效期按服务器响应执行,不把尚未议定的 15 分钟建议写成硬性上限。
|
|
44
|
+
|
|
45
|
+
刷新可以省略 ID Token;返回时检查原 issuer、sub、aud、可选 auth_time 和 nonce。轮换后的 Token 若验证失败,客户端撤销新凭据并要求重登,避免继续使用可能已消费的旧 refresh token。临时 Token endpoint 故障不会删除仍有效的旧授权。
|
|
46
|
+
|
|
47
|
+
模型目录仍使用共享的保守能力映射:仅有 ID 时启用文本,不从名称猜图像或思考能力。草案中的可选模型能力扩展尚未消费,需要时使用显式审核的 provider 配置。
|
|
48
|
+
|
|
49
|
+
机构扩展可以调用既有 Host-only `modelResourceFetch(profileID, relativePath)`。模型连接统一使用当前获准的 Access Token,共享路径限制、GET、正文大小限制与退出隔离。公共插件不自动查询配额,也不新增配额 RPC 或字段。扩展须显式安装,服务端须自行声明并执行其授权范围。
|
|
50
|
+
|
|
51
|
+
退出立即停止本机请求,再提交 refresh token 撤销;支持 200 空响应或 JSON。远端失败仅记录脱敏警告,当前没有持久化撤销重试队列,不能宣称满足草案中这项提议。服务器的 refresh 家族撤销、Access Token 剩余有效期、scope 隔离和模型过滤仍需真实验收。
|
|
52
|
+
|
|
53
|
+
参考:[OIDC 刷新响应](https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse)、[授权响应 issuer](https://www.rfc-editor.org/rfc/rfc9207.html)。
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# LiteLLM setup guide
|
|
2
|
+
|
|
3
|
+
[简体中文](litellm-setup.md) | **English**
|
|
4
|
+
|
|
5
|
+
Let users sign in to a LiteLLM gateway from EduWork, discover authorized models, and chat without manually distributing model API keys.
|
|
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.
|
|
8
|
+
|
|
9
|
+
## 1. Prepare the server
|
|
10
|
+
|
|
11
|
+
Follow the [official LiteLLM deployment guide](https://docs.litellm.ai/docs/proxy/quick_start) to prepare a gateway, database, and at least one working real model upstream. Configure a test user's account, teams, and model permissions, and verify browser sign-in. Enterprise SSO deployment and licensing requirements are governed by LiteLLM; this guide does not require a new identity provider.
|
|
12
|
+
|
|
13
|
+
Set these environment variables on the existing LiteLLM Proxy process or container, replacing the example with the public HTTPS address users reach, then restart it:
|
|
14
|
+
|
|
15
|
+
```dotenv
|
|
16
|
+
PROXY_BASE_URL=https://gateway.example.org
|
|
17
|
+
EXPERIMENTAL_UI_LOGIN=True
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`PROXY_BASE_URL` sets the public issuer and endpoints; do not advertise an internal reverse-proxy address. `EXPERIMENTAL_UI_LOGIN` enables the required login feature. See [LiteLLM CLI Authentication](https://docs.litellm.ai/docs/proxy/cli_sso).
|
|
21
|
+
|
|
22
|
+
Forward discovery, authorization, registration, token, revocation, and model routes through the proxy. Preserve query parameters and form bodies, allow streaming SSE, and avoid redirects of authentication POSTs to another origin. Multiple workers or replicas need shared Redis, for example through `general_settings.coordination_redis`, so refresh rotation and revocation are not limited to one process. See the [configuration reference](https://docs.litellm.ai/docs/proxy/config_settings) and [native authentication implementation](https://github.com/BerriAI/litellm/blob/v1.101.0/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py).
|
|
23
|
+
|
|
24
|
+
From the user's network, check public discovery without credentials:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
curl --fail --silent --show-error https://gateway.example.org/.well-known/litellm-cli-auth
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The JSON response must satisfy:
|
|
31
|
+
|
|
32
|
+
- `contract_version` is `1`; `issuer` and `resource` are the configured public gateway address.
|
|
33
|
+
- It includes `authorization_endpoint`, `token_endpoint`, `registration_endpoint`, and `revocation_endpoint`.
|
|
34
|
+
- It advertises authorization codes, refresh, PKCE `S256`, and public-client authentication with `none`.
|
|
35
|
+
|
|
36
|
+
See the [protocol reference](README_EN.md) for the full response and constraints. EduWork rejects missing capabilities or inconsistent endpoint bindings; a reachable login page alone does not establish compatibility.
|
|
37
|
+
|
|
38
|
+
## 2. Configure EduWork
|
|
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.
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"schemaVersion": 1,
|
|
45
|
+
"organizations": [
|
|
46
|
+
{
|
|
47
|
+
"schemaVersion": "dsh-oidc/v1alpha1",
|
|
48
|
+
"id": "example-gateway",
|
|
49
|
+
"displayName": "Example model gateway",
|
|
50
|
+
"auth": {
|
|
51
|
+
"discoveryUrl": "https://gateway.example.org/.well-known/litellm-cli-auth",
|
|
52
|
+
"expectedIssuer": "https://gateway.example.org"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
| Field | Value |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| `id` | A stable organization identifier, unique on this client. |
|
|
62
|
+
| `displayName` | The organization name shown in the account menu. |
|
|
63
|
+
| `auth.discoveryUrl` | The complete discovery document URL supplied by the administrator. |
|
|
64
|
+
| `auth.expectedIssuer` | The expected issuer; recommended, and must match discovery. |
|
|
65
|
+
|
|
66
|
+
Save, exit completely through the tray, and restart EduWork. Closing its window usually leaves the process running.
|
|
67
|
+
|
|
68
|
+
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
|
+
|
|
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.
|
|
71
|
+
|
|
72
|
+
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
|
+
|
|
74
|
+
## 3. Sign in and use models
|
|
75
|
+
|
|
76
|
+
1. Select the configured organization in EduWork's account menu and start sign-in. Keep the client running.
|
|
77
|
+
2. Complete sign-in in the system browser. If the gateway asks for a team, select one with the required model permissions and approve access.
|
|
78
|
+
3. The browser returns to a local callback page. Once it confirms completion, return to EduWork.
|
|
79
|
+
4. Select an organization model and send a short message to verify a real upstream response.
|
|
80
|
+
|
|
81
|
+
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
|
+
|
|
83
|
+
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.
|
|
84
|
+
|
|
85
|
+
LiteLLM user and team settings continue to govern model authorization and billing; client configuration cannot grant permissions. This integration does not provide a unified quota panel. Model capabilities come from the catalog and administrator-reviewed configuration. ID-only catalogs default to text models without inferring image or reasoning support from names. See [Profile configuration](../enterprise-profile.en.md) for capability supplements.
|
|
86
|
+
|
|
87
|
+
## 4. Validate before rollout
|
|
88
|
+
|
|
89
|
+
Use a regular account for these checks. An administrator account, model listing, or mock response cannot substitute for actual inference:
|
|
90
|
+
|
|
91
|
+
| Check | Expected result |
|
|
92
|
+
| --- | --- |
|
|
93
|
+
| Sign-in, denied consent, and new sign-in | Success or denial is explicit; an old callback cannot finish a new login. |
|
|
94
|
+
| Catalog and real conversation | Only authorized models appear and a real upstream returns content. |
|
|
95
|
+
| Streaming | Actual conversation content arrives incrementally and ends normally; the proxy does not buffer the entire SSE. |
|
|
96
|
+
| Refresh during use and restart | Requests work after token refresh and sign-in survives restart, without copying Tokens. |
|
|
97
|
+
| User / team isolation | A restricted user cannot list or invoke unauthorized models. |
|
|
98
|
+
| Logout | New organization model requests are unavailable locally and the revoked Refresh Token cannot restore authorization. |
|
|
99
|
+
|
|
100
|
+
A test deployment can shorten token lifetime to exercise refresh. If test tooling simulates near-expiry, record that method. Successful sign-in alone does not validate refresh; a real gateway with a mock upstream does not validate real model inference.
|
|
101
|
+
|
|
102
|
+
## 5. Troubleshooting
|
|
103
|
+
|
|
104
|
+
| Symptom | What to check |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| Discovery returns 404 or HTML | Check the LiteLLM version, public route, and reverse proxy; expect native contract 1 JSON. |
|
|
107
|
+
| Issuer / endpoint validation fails | Check `PROXY_BASE_URL`, public HTTPS addresses, and `expectedIssuer`; correct deployment settings and sign in again. |
|
|
108
|
+
| Browser reports connection refused at `127.0.0.1` | Keep EduWork running, use a browser on the same computer, and start a new sign-in from its account menu. |
|
|
109
|
+
| Sign-in works but the catalog is empty or inference returns 403 | Check the user, selected team, and model permissions; sign-in does not authorize every model. |
|
|
110
|
+
| Exchange / refresh fails or a request returns 401 | Check token expiry, gateway account status, and server logs; replicas need shared Redis for rotation and revocation. |
|
|
111
|
+
| A reasoning level or input type is unsupported | Follow the current catalog's capabilities; do not force unsupported `reasoningEffort` values or attachments. |
|
|
112
|
+
| Streamed content arrives all at once | Check reverse-proxy SSE buffering and connection timeouts. |
|
|
113
|
+
|
|
114
|
+
When reporting problems, include versions, the failing stage, HTTP status, and redacted errors. Exclude authorization codes, Tokens, and full callback URLs. See the [gateway protocol reference](README_EN.md) for detailed wire fields.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# LiteLLM 接入指南
|
|
2
|
+
|
|
3
|
+
**简体中文** | [English](litellm-setup.en.md)
|
|
4
|
+
|
|
5
|
+
让用户在 EduWork 中登录 LiteLLM 网关,自动发现获授权的模型并直接对话,无需手动分发模型 API Key。
|
|
6
|
+
|
|
7
|
+
[LiteLLM 项目](https://github.com/BerriAI/litellm)提供网关;EduWork 使用其 native OAuth 流程。本文适用于已有 LiteLLM 部署的管理员和使用 EduWork 桌面的用户。协议基线为 **LiteLLM v1.101.0 / native contract 1**,其他版本需按本文验收。EduWork 端需使用包含本功能的构建;本分支尚未发布 npm 包或桌面版本。
|
|
8
|
+
|
|
9
|
+
## 1. 管理员准备服务端
|
|
10
|
+
|
|
11
|
+
先按 [LiteLLM 官方部署说明](https://docs.litellm.ai/docs/proxy/quick_start)准备网关、数据库和至少一个可用的真实模型上游。为测试用户配置账号、团队与模型权限,并确认该账号能登录网关网页。企业 SSO 的部署和授权要求以 LiteLLM 为准;本指南不要求新增身份提供商。
|
|
12
|
+
|
|
13
|
+
在已有 LiteLLM Proxy 进程或容器中设置以下环境变量,然后重启服务。将示例域名换成用户实际访问的 HTTPS 地址:
|
|
14
|
+
|
|
15
|
+
```dotenv
|
|
16
|
+
PROXY_BASE_URL=https://gateway.example.org
|
|
17
|
+
EXPERIMENTAL_UI_LOGIN=True
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`PROXY_BASE_URL` 决定公开发现地址中的 issuer 与端点;反向代理后的内网地址不能冒充公开地址。`EXPERIMENTAL_UI_LOGIN` 启用该版本要求的登录功能。配置依据见 [LiteLLM CLI Authentication](https://docs.litellm.ai/docs/proxy/cli_sso)。
|
|
21
|
+
|
|
22
|
+
代理需转发发现、授权、注册、换码、撤销和模型路由,保留查询参数与表单请求体,并允许模型 SSE 持续输出。不要将认证 POST 重定向到另一域名。多 worker / 副本需共享 Redis;可按官方说明配置 `general_settings.coordination_redis`,避免刷新轮换或撤销只在某个进程生效。参见 [LiteLLM 配置参考](https://docs.litellm.ai/docs/proxy/config_settings)和[原生认证实现](https://github.com/BerriAI/litellm/blob/v1.101.0/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py)。
|
|
23
|
+
|
|
24
|
+
从用户所在网络检查公开发现入口,无需登录或附带 Token:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
curl --fail --silent --show-error https://gateway.example.org/.well-known/litellm-cli-auth
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
应返回 JSON,满足:
|
|
31
|
+
|
|
32
|
+
- `contract_version` 为 `1`,`issuer` 和 `resource` 都是管理员配置的公开网关地址。
|
|
33
|
+
- 包含 `authorization_endpoint`、`token_endpoint`、`registration_endpoint` 和 `revocation_endpoint`。
|
|
34
|
+
- 声明授权码、刷新、PKCE `S256` 和 public client 的 `none` 认证能力。
|
|
35
|
+
|
|
36
|
+
完整发现响应及字段约束见[协议参考](README.md)。EduWork 会检查发现文档,拒绝缺失能力或端点绑定不一致的服务;仅能访问登录页不代表接入完成。
|
|
37
|
+
|
|
38
|
+
## 2. 配置 EduWork
|
|
39
|
+
|
|
40
|
+
在设置中选择 **打开配置文件**,将下面的机构条目加入 `config/eduwork.jsonc`。已有配置时合并 `organizations` 数组,不要覆盖其他配置。
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"schemaVersion": 1,
|
|
45
|
+
"organizations": [
|
|
46
|
+
{
|
|
47
|
+
"schemaVersion": "dsh-oidc/v1alpha1",
|
|
48
|
+
"id": "example-gateway",
|
|
49
|
+
"displayName": "示例模型网关",
|
|
50
|
+
"auth": {
|
|
51
|
+
"discoveryUrl": "https://gateway.example.org/.well-known/litellm-cli-auth",
|
|
52
|
+
"expectedIssuer": "https://gateway.example.org"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
| 字段 | 如何填写 |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| `id` | 本机唯一且稳定的机构标识;多个机构使用不同值。 |
|
|
62
|
+
| `displayName` | 账户菜单中显示的机构名称。 |
|
|
63
|
+
| `auth.discoveryUrl` | 完整发现文档 URL,由管理员提供。 |
|
|
64
|
+
| `auth.expectedIssuer` | 预期的服务签发方,建议填写,并与发现文档中的 issuer 一致。 |
|
|
65
|
+
|
|
66
|
+
保存后从托盘完全退出 EduWork,再重新启动。只关闭窗口通常不会退出进程。
|
|
67
|
+
|
|
68
|
+
模型 API 地址由校验后的 issuer 追加 `/v1` 得到,保留部署路径前缀;不要另填 `provider.baseURL`。LiteLLM 会动态注册本次登录所需的 Client ID,无需手工填写固定 `clientId`、`client_secret` 或 scope,也不要同时配置 `oidc`、`keyBinding`。
|
|
69
|
+
|
|
70
|
+
正式部署使用 HTTPS。本机开发只有在机构条目中显式设置 `allowInsecureDevelopment: true`(与 `auth` 同级)才允许 loopback HTTP,不能用它连接任意明文远程地址。密码、Token 和上游 Key 均不应写入客户端配置。
|
|
71
|
+
|
|
72
|
+
其他客户端开发者可使用[单个 Profile 示例](../../examples/litellm.enterprise-profile.example.json)和[桌面宿主指南](../desktop-host.md),复用同一授权与模型模块;桌面安装用户无需自行装配 Host。
|
|
73
|
+
|
|
74
|
+
## 3. 登录并使用模型
|
|
75
|
+
|
|
76
|
+
1. 在 EduWork 账户菜单选择已配置的机构,发起登录;保持客户端运行。
|
|
77
|
+
2. 系统浏览器打开 LiteLLM 登录与授权页面。完成账号登录;网关要求选择团队时,选择有相应模型权限的团队并同意授权。
|
|
78
|
+
3. 浏览器返回本机回调页面,确认登录完成后回到 EduWork。
|
|
79
|
+
4. 在模型选择器中选择该机构的模型,发送一条简短消息确认真实模型响应。
|
|
80
|
+
|
|
81
|
+
`127.0.0.1` 回调是 EduWork 在用户电脑上临时监听的地址,不是 LiteLLM 服务器地址。浏览器必须在运行客户端的同一台电脑上完成回调;不要收藏或重复打开旧授权链接。
|
|
82
|
+
|
|
83
|
+
客户端自动读取当前授权的 `/v1/models`,请求使用 Access Token,并在临近到期时刷新。重启后通过本机受保护存储恢复授权。退出登录会清理本地会话并尝试撤销 Refresh Token;Access Token 的剩余有效期由服务端控制。
|
|
84
|
+
|
|
85
|
+
模型授权和计费仍由 LiteLLM 的用户及团队配置决定,客户端不会给账号增加权限。此接入不提供统一配额面板。模型能力取自目录和管理员核实的配置;目录只有模型 ID 时,客户端按文本模型处理,不根据名称猜测图像或思考能力。能力补充方式见 [Profile 配置](../enterprise-profile.md)。
|
|
86
|
+
|
|
87
|
+
## 4. 上线前验收
|
|
88
|
+
|
|
89
|
+
用普通用户完成以下检查,不能只用管理员账号、模型列表或 mock 响应替代真实调用:
|
|
90
|
+
|
|
91
|
+
| 检查 | 预期结果 |
|
|
92
|
+
| --- | --- |
|
|
93
|
+
| 登录、拒绝授权、重新登录 | 成功或拒绝状态明确,旧回调不能完成新登录。 |
|
|
94
|
+
| 模型目录与真实对话 | 只出现获授权的模型,能收到真实上游返回的内容。 |
|
|
95
|
+
| 流式调用 | 实际对话持续接收内容并正常结束;代理不缓冲整个 SSE。 |
|
|
96
|
+
| 使用中刷新与重启 | 到期刷新后可继续请求,重启后可恢复登录;无需复制 Token。 |
|
|
97
|
+
| 用户 / 团队隔离 | 受限用户看不到也不能调用未授权模型。 |
|
|
98
|
+
| 退出 | 本地不再能发起机构模型请求,已撤销的 Refresh Token 不能恢复授权。 |
|
|
99
|
+
|
|
100
|
+
测试环境可缩短 Token 有效期验证刷新;若通过测试工具模拟临近到期,应注明测试方式。不要把一次登录成功视为刷新已通过,也不要把真实网关加 mock 上游视为真实模型验收。
|
|
101
|
+
|
|
102
|
+
## 5. 常见问题
|
|
103
|
+
|
|
104
|
+
| 现象 | 排查方向 |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| 发现入口返回 404 或 HTML | 检查 LiteLLM 版本、公开路由和代理转发;应返回 native contract 1 JSON。 |
|
|
107
|
+
| issuer / endpoint 校验失败 | 核对 `PROXY_BASE_URL`、公开 HTTPS 地址和 `expectedIssuer`;修正部署配置后重新登录。 |
|
|
108
|
+
| 浏览器显示 `127.0.0.1` 拒绝连接 | 确认客户端仍在运行、浏览器与客户端在同一电脑,并从账户菜单发起新登录。 |
|
|
109
|
+
| 登录成功但模型列表为空或调用返回 403 | 核对用户、所选团队及模型权限;登录不等于获得全部模型权限。 |
|
|
110
|
+
| 换码、刷新失败或请求返回 401 | 检查令牌有效期、网关账号状态和服务日志;轮换 / 撤销在多副本部署中需共享 Redis。 |
|
|
111
|
+
| 提示不支持某个思考档位或输入类型 | 按当前目录支持的能力调用;不要强制传入模型不支持的 `reasoningEffort` 或附件。 |
|
|
112
|
+
| 流式内容最后一次性出现 | 检查反向代理的 SSE 缓冲和连接超时。 |
|
|
113
|
+
|
|
114
|
+
提供问题报告时,记录版本、发生阶段、HTTP 状态与已脱敏错误;不要附带授权码、Token 或完整回调 URL。更详细的交互字段见[网关认证协议参考](README.md)。
|
|
@@ -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)。
|