@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
|
@@ -12,28 +12,17 @@
|
|
|
12
12
|
"loginDescription": "登录后由学校企业服务为当前用户绑定 ChatECNU 运行凭据;学校密码不会进入 DSH。",
|
|
13
13
|
"supportURL": "https://support.example.edu/replace-me"
|
|
14
14
|
},
|
|
15
|
-
"
|
|
16
|
-
"
|
|
15
|
+
"auth": {
|
|
16
|
+
"discoveryUrl": "https://models.example.org/.well-known/openid-configuration",
|
|
17
|
+
"expectedIssuer": "https://models.example.org",
|
|
18
|
+
"experimentalOidcLlm": true,
|
|
17
19
|
"clientId": "replace-with-public-client-id",
|
|
18
|
-
"
|
|
19
|
-
"openid",
|
|
20
|
-
"profile",
|
|
21
|
-
"offline_access",
|
|
22
|
-
"worker.bootstrap.read",
|
|
23
|
-
"worker.credential.provision",
|
|
24
|
-
"worker.credential.read",
|
|
25
|
-
"worker.credential.renew"
|
|
26
|
-
]
|
|
27
|
-
},
|
|
28
|
-
"keyBinding": {
|
|
29
|
-
"baseURL": "https://worker.example.edu/api/worker/v1",
|
|
30
|
-
"credentialRef": "EDUWORK_API_KEY"
|
|
20
|
+
"identityMode": "oidc"
|
|
31
21
|
},
|
|
32
22
|
"provider": {
|
|
33
23
|
"id": "chatecnu",
|
|
34
24
|
"displayName": "ChatECNU",
|
|
35
25
|
"adapter": "openai-compatible",
|
|
36
|
-
"baseURL": "https://models.example.edu/open/api/v1",
|
|
37
26
|
"reasoning": "high",
|
|
38
27
|
"defaultContextWindow": 1000000,
|
|
39
28
|
"defaultMaxTokens": 393216,
|
|
@@ -59,7 +48,9 @@
|
|
|
59
48
|
"name": "ecnu-max",
|
|
60
49
|
"contextWindow": 1000000,
|
|
61
50
|
"maxTokens": 393216,
|
|
62
|
-
"input": [
|
|
51
|
+
"input": [
|
|
52
|
+
"text"
|
|
53
|
+
],
|
|
63
54
|
"reasoningEfforts": {
|
|
64
55
|
"max": "max",
|
|
65
56
|
"high": "high",
|
|
@@ -71,11 +62,15 @@
|
|
|
71
62
|
"name": "ecnu-plus",
|
|
72
63
|
"contextWindow": 262144,
|
|
73
64
|
"maxTokens": 65536,
|
|
74
|
-
"input": [
|
|
65
|
+
"input": [
|
|
66
|
+
"text",
|
|
67
|
+
"image"
|
|
68
|
+
],
|
|
75
69
|
"compat": {
|
|
76
70
|
"supportsReasoningEffort": false
|
|
77
71
|
}
|
|
78
72
|
}
|
|
79
|
-
]
|
|
73
|
+
],
|
|
74
|
+
"modelSource": "discovery"
|
|
80
75
|
}
|
|
81
76
|
}
|
|
@@ -1,78 +1,73 @@
|
|
|
1
|
-
{
|
|
2
|
-
"schemaVersion": "dsh-oidc/v1alpha1",
|
|
3
|
-
"id": "example-university",
|
|
4
|
-
"displayName": "Example University AI",
|
|
5
|
-
"organization": "Example University",
|
|
6
|
-
"brand": {
|
|
7
|
-
"productName": "Example AI Work",
|
|
8
|
-
"organizationName": "Example University",
|
|
9
|
-
"mark": "E",
|
|
10
|
-
"primaryColor": "#5157af",
|
|
11
|
-
"loginTitle": "Sign in with Example University",
|
|
12
|
-
"loginDescription": "Authenticate with the university identity provider and bind a model runtime credential. Your password never enters DSH.",
|
|
13
|
-
"supportURL": "https://support.example.edu/ai"
|
|
14
|
-
},
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
},
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
"
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
]
|
|
77
|
-
}
|
|
78
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "dsh-oidc/v1alpha1",
|
|
3
|
+
"id": "example-university",
|
|
4
|
+
"displayName": "Example University AI",
|
|
5
|
+
"organization": "Example University",
|
|
6
|
+
"brand": {
|
|
7
|
+
"productName": "Example AI Work",
|
|
8
|
+
"organizationName": "Example University",
|
|
9
|
+
"mark": "E",
|
|
10
|
+
"primaryColor": "#5157af",
|
|
11
|
+
"loginTitle": "Sign in with Example University",
|
|
12
|
+
"loginDescription": "Authenticate with the university identity provider and bind a model runtime credential. Your password never enters DSH.",
|
|
13
|
+
"supportURL": "https://support.example.edu/ai"
|
|
14
|
+
},
|
|
15
|
+
"auth": {
|
|
16
|
+
"discoveryUrl": "https://models.example.org/.well-known/openid-configuration",
|
|
17
|
+
"expectedIssuer": "https://models.example.org",
|
|
18
|
+
"experimentalOidcLlm": true,
|
|
19
|
+
"clientId": "replace-with-public-client-id",
|
|
20
|
+
"identityMode": "oidc"
|
|
21
|
+
},
|
|
22
|
+
"provider": {
|
|
23
|
+
"id": "example-ai",
|
|
24
|
+
"displayName": "Example AI",
|
|
25
|
+
"adapter": "openai-compatible",
|
|
26
|
+
"reasoning": "high",
|
|
27
|
+
"defaultContextWindow": 262144,
|
|
28
|
+
"defaultMaxTokens": 32768,
|
|
29
|
+
"streamIdleTimeoutMs": 300000,
|
|
30
|
+
"retryPolicy": {
|
|
31
|
+
"mode": "normal",
|
|
32
|
+
"maxRetries": 2
|
|
33
|
+
},
|
|
34
|
+
"compat": {
|
|
35
|
+
"supportsDeveloperRole": false,
|
|
36
|
+
"supportsStore": false,
|
|
37
|
+
"supportsReasoningEffort": true,
|
|
38
|
+
"supportsUsageInStreaming": true,
|
|
39
|
+
"maxTokensField": "max_tokens",
|
|
40
|
+
"supportsStrictMode": false,
|
|
41
|
+
"supportsLongCacheRetention": false,
|
|
42
|
+
"thinkingFormat": "deepseek",
|
|
43
|
+
"requiresReasoningContentOnAssistantMessages": true
|
|
44
|
+
},
|
|
45
|
+
"models": [
|
|
46
|
+
{
|
|
47
|
+
"id": "example-max",
|
|
48
|
+
"name": "Example Max",
|
|
49
|
+
"input": [
|
|
50
|
+
"text"
|
|
51
|
+
],
|
|
52
|
+
"reasoningEfforts": {
|
|
53
|
+
"max": "max",
|
|
54
|
+
"high": "high",
|
|
55
|
+
"low": "low"
|
|
56
|
+
},
|
|
57
|
+
"defaultReasoningEffort": "high"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "example-plus",
|
|
61
|
+
"name": "Example Plus",
|
|
62
|
+
"input": [
|
|
63
|
+
"text",
|
|
64
|
+
"image"
|
|
65
|
+
],
|
|
66
|
+
"compat": {
|
|
67
|
+
"supportsReasoningEffort": false
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
"modelSource": "discovery"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "dsh-oidc/v1alpha1",
|
|
3
|
+
"id": "example-models",
|
|
4
|
+
"displayName": "示例模型服务(实验)",
|
|
5
|
+
"auth": {
|
|
6
|
+
"discoveryUrl": "https://models.example.org/.well-known/openid-configuration",
|
|
7
|
+
"expectedIssuer": "https://models.example.org",
|
|
8
|
+
"experimentalOidcLlm": true,
|
|
9
|
+
"clientId": "replace-with-public-client-id",
|
|
10
|
+
"identityMode": "oidc"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -2,19 +2,16 @@
|
|
|
2
2
|
"schemaVersion": "dsh-oidc/v1alpha1",
|
|
3
3
|
"id": "example-resources",
|
|
4
4
|
"displayName": "Example Organization",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"type": "eduwork-resources-v1",
|
|
12
|
-
"baseURL": "https://ai.example.edu/api/worker/v1"
|
|
5
|
+
"auth": {
|
|
6
|
+
"discoveryUrl": "https://models.example.org/.well-known/openid-configuration",
|
|
7
|
+
"expectedIssuer": "https://models.example.org",
|
|
8
|
+
"experimentalOidcLlm": true,
|
|
9
|
+
"clientId": "replace-with-public-client-id",
|
|
10
|
+
"identityMode": "oidc"
|
|
13
11
|
},
|
|
14
12
|
"provider": {
|
|
15
13
|
"id": "example-ai",
|
|
16
14
|
"adapter": "openai-compatible",
|
|
17
|
-
"baseURL": "https://ai.example.edu/open/api/v1",
|
|
18
15
|
"modelSource": "discovery"
|
|
19
16
|
}
|
|
20
17
|
}
|
package/lib/callback-page.js
CHANGED
|
@@ -15,13 +15,13 @@ export function callbackPage(profile = {}, outcome = 'failed', language = 'en')
|
|
|
15
15
|
const zh = language === 'zh-CN'
|
|
16
16
|
const brand = profile.brand ?? {}, product = brand.productName || profile.displayName || 'EduWork'
|
|
17
17
|
const organization = brand.organizationName || profile.organization || ''
|
|
18
|
-
const success = outcome === 'completed'
|
|
18
|
+
const success = outcome === 'completed'
|
|
19
19
|
const title = success ? (zh ? '身份认证已完成' : 'You’re signed in')
|
|
20
20
|
: outcome === 'expired' ? (zh ? '此次登录已过期' : 'This sign-in has expired') : (zh ? '此次登录未完成' : 'Sign-in did not complete')
|
|
21
|
-
const description =
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
const description = success ? (zh ? '请返回应用继续工作。' : 'Return to the app to continue your work.')
|
|
22
|
+
: outcome === 'issuer-invalid'
|
|
23
|
+
? (zh ? '认证服务返回的信息与登录配置不一致。请联系管理员检查后重试。' : 'The authentication response does not match the sign-in configuration. Contact your administrator before trying again.')
|
|
24
|
+
: (zh ? '请返回应用重新发起登录。' : 'Return to the app and start sign-in again.')
|
|
25
25
|
const accent = /^#[a-f0-9]{6}$/i.test(brand.primaryColor ?? '') ? brand.primaryColor : '#4f5fd7'
|
|
26
26
|
let logo = '', imageSource = "'none'"
|
|
27
27
|
if (/^data:image\/(png|webp);base64,[A-Za-z0-9+/]+=*$/.test(brand.logoURL ?? '')) { logo = brand.logoURL; imageSource = 'data:' }
|
package/lib/client.js
CHANGED
|
@@ -4247,7 +4247,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4247
4247
|
const pkg$1 = "@eduwork/dsh-oidc";
|
|
4248
4248
|
const profileIdSchema = string().min(1).max(64);
|
|
4249
4249
|
const loginIdSchema = string().regex(/^[A-Za-z0-9_-]{32}$/);
|
|
4250
|
-
const reconcileOptionsSchema = object({
|
|
4250
|
+
const reconcileOptionsSchema = object({}).strict();
|
|
4251
4251
|
const baseURLSchema = string().min(1).max(2048);
|
|
4252
4252
|
const modelCatalogModeSchema = _enum(["discovery", "manual"]);
|
|
4253
4253
|
const configurationTargetSchema = _enum(["config", "examples"]);
|
|
@@ -4648,7 +4648,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4648
4648
|
return next.status;
|
|
4649
4649
|
}
|
|
4650
4650
|
if (next.state === "cancelled") return service.status(profileID);
|
|
4651
|
-
if (next.state === "failed")
|
|
4651
|
+
if (next.state === "failed") {
|
|
4652
|
+
const message = ["gateway_callback_issuer_missing", "gateway_callback_issuer_invalid"].includes(next.errorCode) ? "The authentication response does not match the sign-in configuration. Contact your administrator before trying again." : "Organization sign-in did not complete. Please try again.";
|
|
4653
|
+
throw Object.assign(new Error(message), { code: next.errorCode });
|
|
4654
|
+
}
|
|
4652
4655
|
if (next.state === "expired") break;
|
|
4653
4656
|
await wait(interval, signal);
|
|
4654
4657
|
}
|
|
@@ -4709,7 +4712,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4709
4712
|
configure: "配置",
|
|
4710
4713
|
enable: "启用并重载",
|
|
4711
4714
|
login: "登录",
|
|
4712
|
-
provision: "确认创建并连接",
|
|
4713
4715
|
refresh: "刷新状态",
|
|
4714
4716
|
processing: "处理中…",
|
|
4715
4717
|
profileManaged: "Enterprise Profile 管理",
|
|
@@ -4789,7 +4791,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4789
4791
|
configure: "Configure",
|
|
4790
4792
|
enable: "Enable and reload",
|
|
4791
4793
|
login: "Sign in",
|
|
4792
|
-
provision: "Confirm and connect",
|
|
4793
4794
|
refresh: "Refresh",
|
|
4794
4795
|
processing: "Working…",
|
|
4795
4796
|
profileManaged: "Enterprise Profile managed",
|
|
@@ -5005,7 +5006,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5005
5006
|
};
|
|
5006
5007
|
const login = useSignIn(service);
|
|
5007
5008
|
const signIn = (profileID) => run(() => login.begin(profileID));
|
|
5008
|
-
const connect = (profileID
|
|
5009
|
+
const connect = (profileID) => signIn(profileID);
|
|
5009
5010
|
if (!management) return h$2("p", { style: {
|
|
5010
5011
|
margin: "18px 0",
|
|
5011
5012
|
color: textSecondary,
|
|
@@ -5163,12 +5164,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5163
5164
|
type: "button",
|
|
5164
5165
|
disabled: busy,
|
|
5165
5166
|
style: primaryStyle,
|
|
5166
|
-
onClick: () => connect(profile.id
|
|
5167
|
-
}, busy ? copy.processing :
|
|
5167
|
+
onClick: () => connect(profile.id)
|
|
5168
|
+
}, busy ? copy.processing : copy.login), profile.enabled && !restartRequired && connected && h$2("button", {
|
|
5168
5169
|
type: "button",
|
|
5169
5170
|
disabled: busy,
|
|
5170
5171
|
style: buttonStyle,
|
|
5171
|
-
onClick: () => run(() => service.reconcile(profile.id, {
|
|
5172
|
+
onClick: () => run(() => service.reconcile(profile.id, {}))
|
|
5172
5173
|
}, copy.refresh))));
|
|
5173
5174
|
})) : h$2("div", { style: {
|
|
5174
5175
|
padding: 18,
|
|
@@ -5703,6 +5704,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5703
5704
|
const activate = async (value) => {
|
|
5704
5705
|
if (value?.state === "connected" && value.credentialReady) await connected(value.profileID);
|
|
5705
5706
|
};
|
|
5707
|
+
const reconcile = async (id, select) => {
|
|
5708
|
+
const generation = mutate(id), result = await base.reconcile(id, {});
|
|
5709
|
+
if (epoch(id) === generation && !disposed) {
|
|
5710
|
+
publish(result);
|
|
5711
|
+
if (select) await activate(result);
|
|
5712
|
+
}
|
|
5713
|
+
return result;
|
|
5714
|
+
};
|
|
5706
5715
|
return {
|
|
5707
5716
|
service: {
|
|
5708
5717
|
...base,
|
|
@@ -5737,14 +5746,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5737
5746
|
}
|
|
5738
5747
|
return next;
|
|
5739
5748
|
},
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
if (epoch(id) === generation && !disposed) {
|
|
5743
|
-
publish(result);
|
|
5744
|
-
if (options?.allowProvision === true) await activate(result);
|
|
5745
|
-
}
|
|
5746
|
-
return result;
|
|
5747
|
-
},
|
|
5749
|
+
reconcile: (id) => reconcile(id, false),
|
|
5750
|
+
useModels: (id) => reconcile(id, true),
|
|
5748
5751
|
async logout(id) {
|
|
5749
5752
|
const generation = mutate(id), result = await base.logout(id);
|
|
5750
5753
|
if (epoch(id) === generation) publish(result);
|
|
@@ -5787,7 +5790,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5787
5790
|
check: "刷新账户",
|
|
5788
5791
|
logout: "退出登录",
|
|
5789
5792
|
login: "登录账户",
|
|
5790
|
-
provision: "确认创建模型凭据",
|
|
5791
5793
|
waiting: "请在浏览器中完成登录…",
|
|
5792
5794
|
cancel: "取消登录",
|
|
5793
5795
|
processing: "正在处理…"
|
|
@@ -5796,7 +5798,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5796
5798
|
check: "Refresh account",
|
|
5797
5799
|
logout: "Sign out",
|
|
5798
5800
|
login: "Sign in",
|
|
5799
|
-
provision: "Provision model credential",
|
|
5800
5801
|
waiting: "Complete sign-in in your browser…",
|
|
5801
5802
|
cancel: "Cancel sign-in",
|
|
5802
5803
|
processing: "Working…"
|
|
@@ -5897,7 +5898,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5897
5898
|
style: action,
|
|
5898
5899
|
onClick
|
|
5899
5900
|
}, label);
|
|
5900
|
-
const refreshAccount = () => service.reconcile(profile.id, {
|
|
5901
|
+
const refreshAccount = () => service.reconcile(profile.id, {});
|
|
5901
5902
|
const defaultContent = signedIn && button(busy ? words.processing : words.check, () => void run(refreshAccount));
|
|
5902
5903
|
const content = open && h$1("div", {
|
|
5903
5904
|
ref: panel,
|
|
@@ -5938,7 +5939,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5938
5939
|
}, { fallback: defaultContent }) : defaultContent, h$1("div", { style: {
|
|
5939
5940
|
display: "grid",
|
|
5940
5941
|
gap: 6
|
|
5941
|
-
} },
|
|
5942
|
+
} }, !signedIn && button(busy ? words.processing : words.login, () => void run(() => login.begin(profile.id))), signedIn && button(words.logout, () => void run(() => service.logout(profile.id), true))), login.pending && h$1("p", { role: "status" }, words.waiting, " ", h$1("button", {
|
|
5942
5943
|
type: "button",
|
|
5943
5944
|
role: "menuitem",
|
|
5944
5945
|
style: action,
|
|
@@ -6005,13 +6006,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6005
6006
|
identityConnected: "组织身份已登录",
|
|
6006
6007
|
unavailable: "企业模型服务暂时不可用",
|
|
6007
6008
|
connected: "已连接",
|
|
6008
|
-
authenticated: "身份认证完成,等待创建模型凭据",
|
|
6009
6009
|
disconnected: "尚未连接",
|
|
6010
|
-
description: "使用组织统一身份认证连接企业模型。密码不会进入 DSH
|
|
6010
|
+
description: "使用组织统一身份认证连接企业模型。密码不会进入 DSH;模型请求使用登录授权。",
|
|
6011
6011
|
connecting: "正在连接…",
|
|
6012
6012
|
login: "使用企业账号登录",
|
|
6013
|
-
provisioning: "正在创建…",
|
|
6014
|
-
provision: "确认创建模型凭据",
|
|
6015
6013
|
checking: "正在检查…",
|
|
6016
6014
|
check: "检查连接",
|
|
6017
6015
|
logout: "退出登录",
|
|
@@ -6021,7 +6019,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6021
6019
|
other: "使用其他模型",
|
|
6022
6020
|
enabled: "完成后将启用",
|
|
6023
6021
|
footerConnected: "企业模型已连接",
|
|
6024
|
-
footerAuthenticated: "身份认证完成,等待创建模型凭据",
|
|
6025
6022
|
footerSetup: "点击设置完成登录"
|
|
6026
6023
|
} : {
|
|
6027
6024
|
waiting: "Complete sign-in in your browser…",
|
|
@@ -6030,13 +6027,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6030
6027
|
identityConnected: "Organization identity connected",
|
|
6031
6028
|
unavailable: "Enterprise model service is temporarily unavailable",
|
|
6032
6029
|
connected: "Connected",
|
|
6033
|
-
authenticated: "Identity verified; model credential is not provisioned",
|
|
6034
6030
|
disconnected: "Not connected",
|
|
6035
|
-
description: "Connect with your organization account. Your password never enters DSH; model requests use
|
|
6031
|
+
description: "Connect with your organization account. Your password never enters DSH; model requests use your sign-in authorization.",
|
|
6036
6032
|
connecting: "Connecting…",
|
|
6037
6033
|
login: "Sign in with organization",
|
|
6038
|
-
provisioning: "Provisioning…",
|
|
6039
|
-
provision: "Provision model credential",
|
|
6040
6034
|
checking: "Checking…",
|
|
6041
6035
|
check: "Check connection",
|
|
6042
6036
|
logout: "Sign out",
|
|
@@ -6046,7 +6040,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6046
6040
|
other: "Use another model",
|
|
6047
6041
|
enabled: "This enables",
|
|
6048
6042
|
footerConnected: "Enterprise models connected",
|
|
6049
|
-
footerAuthenticated: "Identity verified; provision a model credential",
|
|
6050
6043
|
footerSetup: "Open settings to connect"
|
|
6051
6044
|
};
|
|
6052
6045
|
async function unwrap(operation) {
|
|
@@ -6176,7 +6169,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6176
6169
|
};
|
|
6177
6170
|
const login = useSignIn(service);
|
|
6178
6171
|
const begin = () => account.run("login", () => login.begin(profile.id));
|
|
6179
|
-
const stateLabel = account.status?.state === "connected" ? messages.connected :
|
|
6172
|
+
const stateLabel = account.status?.state === "connected" ? messages.connected : messages.disconnected;
|
|
6180
6173
|
const userName = accountUserName(account.status);
|
|
6181
6174
|
return h("section", { style: {
|
|
6182
6175
|
padding: "16px 0",
|
|
@@ -6216,21 +6209,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6216
6209
|
display: "flex",
|
|
6217
6210
|
gap: 8,
|
|
6218
6211
|
flexWrap: "wrap"
|
|
6219
|
-
} }, account.status?.state !== "connected" &&
|
|
6212
|
+
} }, account.status?.state !== "connected" && h("button", {
|
|
6220
6213
|
type: "button",
|
|
6221
6214
|
disabled: Boolean(account.busy),
|
|
6222
6215
|
style: primary,
|
|
6223
6216
|
onClick: begin
|
|
6224
|
-
}, account.busy ? messages.connecting : messages.login),
|
|
6225
|
-
type: "button",
|
|
6226
|
-
disabled: Boolean(account.busy),
|
|
6227
|
-
style: primary,
|
|
6228
|
-
onClick: () => account.run("provision", () => service.reconcile(profile.id, { allowProvision: true }))
|
|
6229
|
-
}, account.busy ? messages.provisioning : messages.provision), account.status?.state === "connected" && h("button", {
|
|
6217
|
+
}, account.busy ? messages.connecting : messages.login), account.status?.state === "connected" && h("button", {
|
|
6230
6218
|
type: "button",
|
|
6231
6219
|
disabled: Boolean(account.busy),
|
|
6232
6220
|
style: button,
|
|
6233
|
-
onClick: () => account.run("reconcile", () => service.reconcile(profile.id, {
|
|
6221
|
+
onClick: () => account.run("reconcile", () => service.reconcile(profile.id, {}))
|
|
6234
6222
|
}, account.busy ? messages.checking : messages.check), account.status?.state !== "signed_out" && h("button", {
|
|
6235
6223
|
type: "button",
|
|
6236
6224
|
disabled: Boolean(account.busy),
|
|
@@ -6332,17 +6320,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6332
6320
|
display: "flex",
|
|
6333
6321
|
gap: 9,
|
|
6334
6322
|
flexWrap: "wrap"
|
|
6335
|
-
} },
|
|
6336
|
-
type: "button",
|
|
6337
|
-
disabled: Boolean(account.busy),
|
|
6338
|
-
style: primary,
|
|
6339
|
-
onClick: account.status?.state === "connected" ? () => account.run("provision", () => service.reconcile(profile.id, { allowProvision: true })) : begin
|
|
6340
|
-
}, account.busy ? messages.connecting : account.status?.state === "connected" ? isChinese ? "使用企业模型" : "Use organization model" : messages.login), account.status?.state === "authenticated" && h("button", {
|
|
6323
|
+
} }, h("button", {
|
|
6341
6324
|
type: "button",
|
|
6342
6325
|
disabled: Boolean(account.busy),
|
|
6343
6326
|
style: primary,
|
|
6344
|
-
onClick: () => account.run("
|
|
6345
|
-
}, account.busy ? messages.
|
|
6327
|
+
onClick: account.status?.state === "connected" ? () => account.run("select", () => service.useModels(profile.id)) : begin
|
|
6328
|
+
}, account.busy ? messages.connecting : account.status?.state === "connected" ? isChinese ? "使用企业模型" : "Use organization model" : messages.login), h("button", {
|
|
6346
6329
|
type: "button",
|
|
6347
6330
|
disabled: Boolean(account.busy),
|
|
6348
6331
|
style: button,
|
|
@@ -6369,7 +6352,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6369
6352
|
const profile = configuration.profiles[0];
|
|
6370
6353
|
const status = useAccountStatus(service, profile.id);
|
|
6371
6354
|
const userName = accountUserName(status);
|
|
6372
|
-
const statusLabel = status?.state === "connected" ? profile.provider ? messages.footerConnected : messages.identityConnected :
|
|
6355
|
+
const statusLabel = status?.state === "connected" ? profile.provider ? messages.footerConnected : messages.identityConnected : messages.footerSetup;
|
|
6373
6356
|
return h(AccountMenu, {
|
|
6374
6357
|
service,
|
|
6375
6358
|
profile,
|
package/lib/desktop-oidc.js
CHANGED
|
@@ -6,7 +6,7 @@ import { callbackLanguage, callbackPage } from './callback-page.js'
|
|
|
6
6
|
const MAX_FLOWS = 32
|
|
7
7
|
const MAX_HISTORY = 128
|
|
8
8
|
const error = (code, message) => Object.assign(new Error(message), { code })
|
|
9
|
-
const safeCodes = new Set(['oidc_callback_invalid', 'oidc_authorization_rejected', 'oidc_token_invalid', 'oidc_id_token_invalid', 'oidc_userinfo_invalid'])
|
|
9
|
+
const safeCodes = new Set(['oidc_callback_invalid', 'oidc_authorization_rejected', 'oidc_token_invalid', 'oidc_id_token_invalid', 'oidc_userinfo_invalid', 'gateway_token_invalid', 'gateway_scope_changed', 'gateway_identity_changed', 'gateway_callback_issuer_missing', 'gateway_callback_issuer_invalid'])
|
|
10
10
|
|
|
11
11
|
function reply(response, status, profile, outcome, language) {
|
|
12
12
|
const page = callbackPage(profile, outcome, language)
|
|
@@ -60,7 +60,8 @@ export class DesktopOidcBackend extends WebOidcBackend {
|
|
|
60
60
|
throw error('oidc_host_unavailable', 'OIDC host has stopped')
|
|
61
61
|
}
|
|
62
62
|
attempt.origin = `http://127.0.0.1:${server.address().port}`
|
|
63
|
-
const authorization = this.createAuthorization(profile, discovery, `${attempt.origin}${OIDC_CALLBACK_PATH}`)
|
|
63
|
+
const authorization = await this.createAuthorization(profile, discovery, `${attempt.origin}${OIDC_CALLBACK_PATH}`)
|
|
64
|
+
if (this.disposed || attempt.state !== 'pending') throw error('oidc_login_cancelled', 'Sign-in was cancelled')
|
|
64
65
|
Object.assign(attempt, { oauthState: authorization.state, flow: authorization.flow })
|
|
65
66
|
attempt.timer = setTimeout(() => { void this.cancelLogin(loginID, 'expired').catch(() => {}) }, this.ttl)
|
|
66
67
|
attempt.timer.unref()
|
|
@@ -137,8 +138,9 @@ export class DesktopOidcBackend extends WebOidcBackend {
|
|
|
137
138
|
this.ctx.logger.warn('Desktop OIDC host credentials are unavailable')
|
|
138
139
|
})
|
|
139
140
|
await attempt.processing
|
|
141
|
+
const failedOutcome = ['gateway_callback_issuer_missing', 'gateway_callback_issuer_invalid'].includes(attempt.errorCode) ? 'issuer-invalid' : 'failed'
|
|
140
142
|
if (!response.destroyed) reply(response, attempt.state === 'completed' ? 200 : 400, profile,
|
|
141
|
-
attempt.state === 'completed' ?
|
|
143
|
+
attempt.state === 'completed' ? 'completed' : failedOutcome, language)
|
|
142
144
|
await this.closeCallback(attempt)
|
|
143
145
|
}
|
|
144
146
|
|
|
@@ -149,7 +151,7 @@ export class DesktopOidcBackend extends WebOidcBackend {
|
|
|
149
151
|
try {
|
|
150
152
|
const session = await this.exchangeAuthorization(requested, attempt.flow)
|
|
151
153
|
if (attempt.state !== 'pending') return
|
|
152
|
-
const refs = [sessionRef(profile)
|
|
154
|
+
const refs = [sessionRef(profile)]
|
|
153
155
|
for (const ref of refs) previous.set(ref, await this.ctx.credentials.resolve(ref))
|
|
154
156
|
if (attempt.state !== 'pending') return
|
|
155
157
|
wrote = true
|
|
@@ -157,7 +159,7 @@ export class DesktopOidcBackend extends WebOidcBackend {
|
|
|
157
159
|
if (attempt.state !== 'pending') return
|
|
158
160
|
// A resource outage must not turn a successful identity login into a failed login.
|
|
159
161
|
let status
|
|
160
|
-
try { status = await this.reconcile(profile.id
|
|
162
|
+
try { status = await this.reconcile(profile.id) }
|
|
161
163
|
catch { status = await this.status(profile.id) }
|
|
162
164
|
if (attempt.state !== 'pending') return
|
|
163
165
|
attempt.status = status
|
|
@@ -173,13 +175,7 @@ export class DesktopOidcBackend extends WebOidcBackend {
|
|
|
173
175
|
// Cancellation/disposal while the host vault writes must not resurrect a login.
|
|
174
176
|
if (wrote && attempt.state !== 'completed') {
|
|
175
177
|
await this.writeCredentials(profile, attempt.flow.epoch, async () => {
|
|
176
|
-
const currentRecord = await this.ctx.credentials.resolve(sessionRef(profile))
|
|
177
|
-
let currentSession
|
|
178
|
-
try { currentSession = JSON.parse(currentRecord?.value) } catch { /* No proven runtime-key ownership. */ }
|
|
179
|
-
const ownsCurrentKey = await this.boundCredential(profile, currentSession)
|
|
180
178
|
for (const [ref, record] of previous) {
|
|
181
|
-
// A different profile may have rebound the shared key during login.
|
|
182
|
-
if (ref !== sessionRef(profile) && !ownsCurrentKey) continue
|
|
183
179
|
if (record?.value !== undefined) await this.ctx.credentials.set(ref, record.value)
|
|
184
180
|
else await this.ctx.credentials.unset(ref)
|
|
185
181
|
}
|