@m1a0rz/agent-identity 0.1.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.
- package/README-cn.md +223 -0
- package/README.md +223 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +306 -0
- package/dist/src/actions/identity-actions.d.ts +142 -0
- package/dist/src/actions/identity-actions.d.ts.map +1 -0
- package/dist/src/actions/identity-actions.js +429 -0
- package/dist/src/commands/identity-commands.d.ts +33 -0
- package/dist/src/commands/identity-commands.d.ts.map +1 -0
- package/dist/src/commands/identity-commands.js +572 -0
- package/dist/src/hooks/after-tool-call.d.ts +22 -0
- package/dist/src/hooks/after-tool-call.d.ts.map +1 -0
- package/dist/src/hooks/after-tool-call.js +35 -0
- package/dist/src/hooks/before-agent-start.d.ts +30 -0
- package/dist/src/hooks/before-agent-start.d.ts.map +1 -0
- package/dist/src/hooks/before-agent-start.js +93 -0
- package/dist/src/hooks/before-tool-call.d.ts +38 -0
- package/dist/src/hooks/before-tool-call.d.ts.map +1 -0
- package/dist/src/hooks/before-tool-call.js +138 -0
- package/dist/src/risk/classify-risk.d.ts +24 -0
- package/dist/src/risk/classify-risk.d.ts.map +1 -0
- package/dist/src/risk/classify-risk.js +61 -0
- package/dist/src/risk/diagnose-risk.d.ts +21 -0
- package/dist/src/risk/diagnose-risk.d.ts.map +1 -0
- package/dist/src/risk/diagnose-risk.js +37 -0
- package/dist/src/risk/llm-risk-check.d.ts +27 -0
- package/dist/src/risk/llm-risk-check.d.ts.map +1 -0
- package/dist/src/risk/llm-risk-check.js +274 -0
- package/dist/src/risk/low-risk-tools.d.ts +5 -0
- package/dist/src/risk/low-risk-tools.d.ts.map +1 -0
- package/dist/src/risk/low-risk-tools.js +29 -0
- package/dist/src/routes/oidc-login.d.ts +51 -0
- package/dist/src/routes/oidc-login.d.ts.map +1 -0
- package/dist/src/routes/oidc-login.js +153 -0
- package/dist/src/services/identity-client.d.ts +366 -0
- package/dist/src/services/identity-client.d.ts.map +1 -0
- package/dist/src/services/identity-client.js +578 -0
- package/dist/src/services/identity-credentials.d.ts +28 -0
- package/dist/src/services/identity-credentials.d.ts.map +1 -0
- package/dist/src/services/identity-credentials.js +170 -0
- package/dist/src/services/identity-service.d.ts +33 -0
- package/dist/src/services/identity-service.d.ts.map +1 -0
- package/dist/src/services/identity-service.js +53 -0
- package/dist/src/services/oidc-client.d.ts +57 -0
- package/dist/src/services/oidc-client.d.ts.map +1 -0
- package/dist/src/services/oidc-client.js +127 -0
- package/dist/src/services/send-notification-feishu.d.ts +27 -0
- package/dist/src/services/send-notification-feishu.d.ts.map +1 -0
- package/dist/src/services/send-notification-feishu.js +148 -0
- package/dist/src/services/session-refresh.d.ts +16 -0
- package/dist/src/services/session-refresh.d.ts.map +1 -0
- package/dist/src/services/session-refresh.js +38 -0
- package/dist/src/store/credential-env-bindings.d.ts +16 -0
- package/dist/src/store/credential-env-bindings.d.ts.map +1 -0
- package/dist/src/store/credential-env-bindings.js +61 -0
- package/dist/src/store/credential-store.d.ts +31 -0
- package/dist/src/store/credential-store.d.ts.map +1 -0
- package/dist/src/store/credential-store.js +57 -0
- package/dist/src/store/oidc-state-store.d.ts +15 -0
- package/dist/src/store/oidc-state-store.d.ts.map +1 -0
- package/dist/src/store/oidc-state-store.js +32 -0
- package/dist/src/store/session-store.d.ts +21 -0
- package/dist/src/store/session-store.d.ts.map +1 -0
- package/dist/src/store/session-store.js +69 -0
- package/dist/src/store/tip-store.d.ts +21 -0
- package/dist/src/store/tip-store.d.ts.map +1 -0
- package/dist/src/store/tip-store.js +60 -0
- package/dist/src/store/tool-approval-store.d.ts +44 -0
- package/dist/src/store/tool-approval-store.d.ts.map +1 -0
- package/dist/src/store/tool-approval-store.js +147 -0
- package/dist/src/tools/identity-approve-tool.d.ts +24 -0
- package/dist/src/tools/identity-approve-tool.d.ts.map +1 -0
- package/dist/src/tools/identity-approve-tool.js +36 -0
- package/dist/src/tools/identity-config.d.ts +13 -0
- package/dist/src/tools/identity-config.d.ts.map +1 -0
- package/dist/src/tools/identity-config.js +18 -0
- package/dist/src/tools/identity-fetch.d.ts +21 -0
- package/dist/src/tools/identity-fetch.d.ts.map +1 -0
- package/dist/src/tools/identity-fetch.js +63 -0
- package/dist/src/tools/identity-list-credentials.d.ts +15 -0
- package/dist/src/tools/identity-list-credentials.d.ts.map +1 -0
- package/dist/src/tools/identity-list-credentials.js +30 -0
- package/dist/src/tools/identity-list-risk-patterns.d.ts +13 -0
- package/dist/src/tools/identity-list-risk-patterns.d.ts.map +1 -0
- package/dist/src/tools/identity-list-risk-patterns.js +23 -0
- package/dist/src/tools/identity-list-tips.d.ts +13 -0
- package/dist/src/tools/identity-list-tips.d.ts.map +1 -0
- package/dist/src/tools/identity-list-tips.js +21 -0
- package/dist/src/tools/identity-login.d.ts +14 -0
- package/dist/src/tools/identity-login.d.ts.map +1 -0
- package/dist/src/tools/identity-login.js +40 -0
- package/dist/src/tools/identity-logout.d.ts +13 -0
- package/dist/src/tools/identity-logout.d.ts.map +1 -0
- package/dist/src/tools/identity-logout.js +24 -0
- package/dist/src/tools/identity-risk-check.d.ts +29 -0
- package/dist/src/tools/identity-risk-check.d.ts.map +1 -0
- package/dist/src/tools/identity-risk-check.js +54 -0
- package/dist/src/tools/identity-set-binding.d.ts +16 -0
- package/dist/src/tools/identity-set-binding.d.ts.map +1 -0
- package/dist/src/tools/identity-set-binding.js +31 -0
- package/dist/src/tools/identity-status.d.ts +13 -0
- package/dist/src/tools/identity-status.d.ts.map +1 -0
- package/dist/src/tools/identity-status.js +41 -0
- package/dist/src/tools/identity-unset-binding.d.ts +15 -0
- package/dist/src/tools/identity-unset-binding.d.ts.map +1 -0
- package/dist/src/tools/identity-unset-binding.js +25 -0
- package/dist/src/tools/identity-whoami.d.ts +13 -0
- package/dist/src/tools/identity-whoami.d.ts.map +1 -0
- package/dist/src/tools/identity-whoami.js +38 -0
- package/dist/src/types.d.ts +93 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +5 -0
- package/dist/src/utils/approval-channel.d.ts +11 -0
- package/dist/src/utils/approval-channel.d.ts.map +1 -0
- package/dist/src/utils/approval-channel.js +13 -0
- package/dist/src/utils/auth.d.ts +24 -0
- package/dist/src/utils/auth.d.ts.map +1 -0
- package/dist/src/utils/auth.js +44 -0
- package/dist/src/utils/derive-session-key.d.ts +78 -0
- package/dist/src/utils/derive-session-key.d.ts.map +1 -0
- package/dist/src/utils/derive-session-key.js +198 -0
- package/openclaw.plugin.json +162 -0
- package/package.json +33 -0
- package/skills/SKILL.md +230 -0
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Identity API client.
|
|
3
|
+
* Service name: identity, service code: id.
|
|
4
|
+
* Data plane: GetWorkloadAccessTokenForJWT (Version 2025-10-30).
|
|
5
|
+
* Control plane (TOP): GetResourceOauth2Token, Oauth2Callback, GetResourceApiKey,
|
|
6
|
+
* CheckPermission, UserPool/UserPoolClient APIs (Version 2025-10-30).
|
|
7
|
+
*
|
|
8
|
+
* Credentials: explicit config, or env (VOLCENGINE_ACCESS_KEY/SECRET_KEY/SESSION_TOKEN),
|
|
9
|
+
* or file (VOLCENGINE_CREDENTIALS_FILE), with optional STS AssumeRole.
|
|
10
|
+
*/
|
|
11
|
+
import { loadIdentityCredentials } from "./identity-credentials.js";
|
|
12
|
+
export { loadIdentityCredentials } from "./identity-credentials.js";
|
|
13
|
+
function isWorkloadNotFoundError(err) {
|
|
14
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
15
|
+
return /404|NotFound/i.test(msg);
|
|
16
|
+
}
|
|
17
|
+
function toUserPoolResult(r) {
|
|
18
|
+
const uid = r.Uid;
|
|
19
|
+
if (!uid)
|
|
20
|
+
throw new Error("Identity GetUserPool: missing Uid");
|
|
21
|
+
const brand = r.Brand;
|
|
22
|
+
const tags = r.Tags;
|
|
23
|
+
return {
|
|
24
|
+
uid,
|
|
25
|
+
name: r.Name ?? "",
|
|
26
|
+
description: r.Description,
|
|
27
|
+
domain: r.Domain ?? "",
|
|
28
|
+
trn: r.Trn,
|
|
29
|
+
createTime: r.CreateTime,
|
|
30
|
+
updateTime: r.UpdateTime,
|
|
31
|
+
enabled: r.Enabled,
|
|
32
|
+
brand: brand ? { name: brand.Name, logoUri: brand.LogoUri } : undefined,
|
|
33
|
+
discoveryUrl: r.DiscoveryUrl,
|
|
34
|
+
issuerUrl: r.IssuerUrl,
|
|
35
|
+
tokenUrl: r.TokenUrl,
|
|
36
|
+
tokenSigningKeyUrl: r.TokenSigningKeyUrl,
|
|
37
|
+
totalUsers: r.TotalUsers,
|
|
38
|
+
totalClients: r.TotalClients,
|
|
39
|
+
totalConnections: r.TotalConnections,
|
|
40
|
+
passwordSignInEnabled: r.PasswordSignInEnabled,
|
|
41
|
+
smsPasswordlessSignInEnabled: r.SmsPasswordlessSignInEnabled,
|
|
42
|
+
emailPasswordlessSignInEnabled: r.EmailPasswordlessSignInEnabled,
|
|
43
|
+
selfSignUpEnabled: r.SelfSignUpEnabled,
|
|
44
|
+
signUpAutoVerificationEnabled: r.SignUpAutoVerificationEnabled,
|
|
45
|
+
selfAccountRecoveryEnabled: r.SelfAccountRecoveryEnabled,
|
|
46
|
+
unconfirmedUserSignInEnabled: r.UnconfirmedUserSignInEnabled,
|
|
47
|
+
smsAnonymousSignUpEnabled: r.SmsAnonymousSignUpEnabled,
|
|
48
|
+
signInAttributes: r.SignInAttributes,
|
|
49
|
+
requiredSignUpAttributes: r.RequiredSignUpAttributes,
|
|
50
|
+
tags: tags?.map((t) => ({ key: t.Key ?? "", value: t.Value ?? "" })),
|
|
51
|
+
projectName: r.ProjectName,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function toUserPoolClientResult(r) {
|
|
55
|
+
const uid = r.Uid;
|
|
56
|
+
if (!uid)
|
|
57
|
+
throw new Error("Identity GetUserPoolClient: missing Uid");
|
|
58
|
+
const idToken = r.IdToken;
|
|
59
|
+
const refreshToken = r.RefreshToken;
|
|
60
|
+
return {
|
|
61
|
+
uid,
|
|
62
|
+
name: r.Name ?? "",
|
|
63
|
+
description: r.Description,
|
|
64
|
+
clientType: r.ClientType,
|
|
65
|
+
clientSecret: r.ClientSecret,
|
|
66
|
+
logoUri: r.LogoUri,
|
|
67
|
+
createTime: r.CreateTime,
|
|
68
|
+
updateTime: r.UpdateTime,
|
|
69
|
+
allowedCallbackUrls: r.AllowedCallbackUrls,
|
|
70
|
+
allowedLogoutUrls: r.AllowedLogoutUrls,
|
|
71
|
+
allowedWebOrigins: r.AllowedWebOrigins,
|
|
72
|
+
allowedCors: r.AllowedCors,
|
|
73
|
+
idToken: idToken ? { lifetimeSeconds: idToken.LifetimeSeconds } : undefined,
|
|
74
|
+
refreshToken: refreshToken
|
|
75
|
+
? {
|
|
76
|
+
hasIdleLifetime: refreshToken.HasIdleLifetime,
|
|
77
|
+
idleLifetimeSeconds: refreshToken.IdleLifetimeSeconds,
|
|
78
|
+
hasCombinedLifetime: refreshToken.HasCombinedLifetime,
|
|
79
|
+
combinedLifetimeSeconds: refreshToken.CombinedLifetimeSeconds,
|
|
80
|
+
rotation: refreshToken.Rotation,
|
|
81
|
+
}
|
|
82
|
+
: undefined,
|
|
83
|
+
loginPageUrl: r.LoginPageUrl,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Identity client using fetch. Credentials from config, env, or file (veadk-style).
|
|
88
|
+
* Uses HMAC-SHA256 signing per Volcengine OpenAPI conventions. Supports STS.
|
|
89
|
+
*/
|
|
90
|
+
export class IdentityClient {
|
|
91
|
+
config;
|
|
92
|
+
constructor(config) {
|
|
93
|
+
this.config = config;
|
|
94
|
+
}
|
|
95
|
+
async resolveCredentials() {
|
|
96
|
+
if (this.config.credentialsGetter) {
|
|
97
|
+
return this.config.credentialsGetter();
|
|
98
|
+
}
|
|
99
|
+
if (this.config.accessKeyId && this.config.secretAccessKey) {
|
|
100
|
+
return {
|
|
101
|
+
accessKeyId: this.config.accessKeyId,
|
|
102
|
+
secretAccessKey: this.config.secretAccessKey,
|
|
103
|
+
sessionToken: this.config.sessionToken,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return loadIdentityCredentials({
|
|
107
|
+
accessKeyId: this.config.accessKeyId,
|
|
108
|
+
secretAccessKey: this.config.secretAccessKey,
|
|
109
|
+
sessionToken: this.config.sessionToken,
|
|
110
|
+
credentialsFile: this.config.credentialsFile,
|
|
111
|
+
roleTrn: this.config.roleTrn,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
async getWorkloadAccessTokenForJWT(params) {
|
|
115
|
+
const doFetch = async () => {
|
|
116
|
+
const body = {
|
|
117
|
+
UserToken: params.userToken,
|
|
118
|
+
WorkloadPoolName: params.workloadPoolName ?? "default",
|
|
119
|
+
DurationSeconds: params.durationSeconds ?? 3600,
|
|
120
|
+
};
|
|
121
|
+
if (params.name)
|
|
122
|
+
body.Name = params.name;
|
|
123
|
+
if (params.audience?.length)
|
|
124
|
+
body.Audience = params.audience;
|
|
125
|
+
const result = (await this.signedPost(this.config.endpoint, "GetWorkloadAccessTokenForJWT", body, "2025-10-30"));
|
|
126
|
+
if (!result?.WorkloadAccessToken) {
|
|
127
|
+
throw new Error("Identity API: missing WorkloadAccessToken in response");
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
workloadAccessToken: result.WorkloadAccessToken,
|
|
131
|
+
expiresAt: result.ExpiresAt ?? new Date(Date.now() + 3600 * 1000).toISOString(),
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
try {
|
|
135
|
+
return await doFetch();
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
// When workload (agent) does not exist (404), create it and retry. Only when Name was passed.
|
|
139
|
+
if (params.name && isWorkloadNotFoundError(err)) {
|
|
140
|
+
await this.createWorkloadIdentity({
|
|
141
|
+
workloadPoolName: params.workloadPoolName ?? "default",
|
|
142
|
+
name: params.name,
|
|
143
|
+
category: "Agent",
|
|
144
|
+
});
|
|
145
|
+
return doFetch();
|
|
146
|
+
}
|
|
147
|
+
throw err;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async createWorkloadIdentity(params) {
|
|
151
|
+
const body = {
|
|
152
|
+
WorkloadPoolName: params.workloadPoolName,
|
|
153
|
+
Name: params.name,
|
|
154
|
+
};
|
|
155
|
+
if (params.category)
|
|
156
|
+
body.Category = params.category;
|
|
157
|
+
if (params.description)
|
|
158
|
+
body.Description = params.description;
|
|
159
|
+
try {
|
|
160
|
+
await this.signedPost(this.config.endpoint, "CreateWorkloadIdentity", body, "2025-10-30");
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
// Duplicated (409): workload already exists, e.g. race with another process. Retry will succeed.
|
|
164
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
165
|
+
if (/409|Duplicated/i.test(msg))
|
|
166
|
+
return;
|
|
167
|
+
throw err;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async sha256Hex(data) {
|
|
171
|
+
const { createHash } = await import("node:crypto");
|
|
172
|
+
return createHash("sha256").update(data, "utf-8").digest("hex");
|
|
173
|
+
}
|
|
174
|
+
async hmacSha256(key, data) {
|
|
175
|
+
const { createHmac } = await import("node:crypto");
|
|
176
|
+
return createHmac("sha256", key).update(data, "utf-8").digest();
|
|
177
|
+
}
|
|
178
|
+
async hmacSha256Hex(key, data) {
|
|
179
|
+
const { createHmac } = await import("node:crypto");
|
|
180
|
+
return createHmac("sha256", key).update(data, "utf-8").digest("hex");
|
|
181
|
+
}
|
|
182
|
+
/** Build canonical query string (sorted, URI-encoded) per volc-sdk-nodejs. */
|
|
183
|
+
queryParamsToString(params) {
|
|
184
|
+
return Object.keys(params)
|
|
185
|
+
.sort()
|
|
186
|
+
.map((key) => {
|
|
187
|
+
const val = params[key];
|
|
188
|
+
if (val == null)
|
|
189
|
+
return "";
|
|
190
|
+
const ek = encodeURIComponent(key).replace(/[^A-Za-z0-9_.~-]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
191
|
+
const ev = encodeURIComponent(val).replace(/[^A-Za-z0-9_.~-]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
192
|
+
return `${ek}=${ev}`;
|
|
193
|
+
})
|
|
194
|
+
.filter(Boolean)
|
|
195
|
+
.join("&");
|
|
196
|
+
}
|
|
197
|
+
/** DateTime for signing: ISO8601 without ms, then strip separators (volc-sdk-nodejs). */
|
|
198
|
+
getDateTime() {
|
|
199
|
+
return new Date()
|
|
200
|
+
.toISOString()
|
|
201
|
+
.replace(/\.\d{3}Z$/, "Z")
|
|
202
|
+
.replace(/[:\-]/g, "");
|
|
203
|
+
}
|
|
204
|
+
async signedPost(baseUrl, action, body, versionOverride) {
|
|
205
|
+
const creds = await this.resolveCredentials();
|
|
206
|
+
const { accessKeyId, secretAccessKey, sessionToken } = creds;
|
|
207
|
+
const serviceCode = this.config.serviceCode ?? "id";
|
|
208
|
+
const version = versionOverride ?? this.config.version ?? "2025-10-30";
|
|
209
|
+
const region = this.config.region ?? "cn-beijing";
|
|
210
|
+
const url = new URL(baseUrl);
|
|
211
|
+
url.pathname = url.pathname || "/";
|
|
212
|
+
const queryParams = { Action: action, Version: version };
|
|
213
|
+
const canonicalQuery = this.queryParamsToString(queryParams);
|
|
214
|
+
url.search = canonicalQuery ? `?${canonicalQuery}` : "";
|
|
215
|
+
const bodyStr = JSON.stringify(body);
|
|
216
|
+
const xDate = this.getDateTime();
|
|
217
|
+
const xContentSha256 = await this.sha256Hex(bodyStr);
|
|
218
|
+
const host = url.host;
|
|
219
|
+
// Headers to sign (volc-sdk-nodejs addHeaders): host, x-content-sha256, x-date, x-security-token (if present)
|
|
220
|
+
const headerEntries = [
|
|
221
|
+
["host", host],
|
|
222
|
+
["x-content-sha256", xContentSha256],
|
|
223
|
+
["x-date", xDate],
|
|
224
|
+
];
|
|
225
|
+
if (sessionToken) {
|
|
226
|
+
headerEntries.push(["x-security-token", sessionToken.replace(/\s+/g, " ").trim()]);
|
|
227
|
+
}
|
|
228
|
+
headerEntries.sort((a, b) => (a[0] < b[0] ? -1 : 1));
|
|
229
|
+
const canonicalHeaders = headerEntries.map(([k, v]) => `${k}:${v}`).join("\n");
|
|
230
|
+
const signedHeaders = headerEntries.map(([k]) => k).join(";");
|
|
231
|
+
const canonicalRequest = [
|
|
232
|
+
"POST",
|
|
233
|
+
url.pathname,
|
|
234
|
+
canonicalQuery,
|
|
235
|
+
canonicalHeaders + "\n",
|
|
236
|
+
signedHeaders,
|
|
237
|
+
xContentSha256,
|
|
238
|
+
].join("\n");
|
|
239
|
+
const algorithm = "HMAC-SHA256";
|
|
240
|
+
const credentialScope = `${xDate.slice(0, 8)}/${region}/${serviceCode}/request`;
|
|
241
|
+
const stringToSign = [
|
|
242
|
+
algorithm,
|
|
243
|
+
xDate,
|
|
244
|
+
credentialScope,
|
|
245
|
+
await this.sha256Hex(canonicalRequest),
|
|
246
|
+
].join("\n");
|
|
247
|
+
const kDate = await this.hmacSha256(secretAccessKey, xDate.slice(0, 8));
|
|
248
|
+
const kRegion = await this.hmacSha256(kDate, region);
|
|
249
|
+
const kService = await this.hmacSha256(kRegion, serviceCode);
|
|
250
|
+
const kSigning = await this.hmacSha256(kService, "request");
|
|
251
|
+
const signature = await this.hmacSha256Hex(kSigning, stringToSign);
|
|
252
|
+
const authorization = `${algorithm} Credential=${accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
|
253
|
+
const headers = {
|
|
254
|
+
"Content-Type": "application/json; charset=UTF-8",
|
|
255
|
+
"X-Date": xDate,
|
|
256
|
+
"X-Content-Sha256": xContentSha256,
|
|
257
|
+
Authorization: authorization,
|
|
258
|
+
};
|
|
259
|
+
if (sessionToken) {
|
|
260
|
+
headers["X-Security-Token"] = sessionToken;
|
|
261
|
+
}
|
|
262
|
+
const res = await fetch(url.toString(), {
|
|
263
|
+
method: "POST",
|
|
264
|
+
headers,
|
|
265
|
+
body: bodyStr,
|
|
266
|
+
});
|
|
267
|
+
if (!res.ok) {
|
|
268
|
+
const text = await res.text();
|
|
269
|
+
throw new Error(`Identity API error ${res.status}: ${text}`);
|
|
270
|
+
}
|
|
271
|
+
const json = (await res.json());
|
|
272
|
+
if (json.Error) {
|
|
273
|
+
throw new Error(`Identity API error: ${json.Error.Code ?? "Unknown"} - ${json.Error.Message ?? ""}`);
|
|
274
|
+
}
|
|
275
|
+
return json.Result ?? {};
|
|
276
|
+
}
|
|
277
|
+
async getResourceOauth2Token(params) {
|
|
278
|
+
const body = {
|
|
279
|
+
ProviderName: params.providerName,
|
|
280
|
+
IdentityToken: params.identityToken,
|
|
281
|
+
};
|
|
282
|
+
if (params.flow)
|
|
283
|
+
body.Flow = params.flow;
|
|
284
|
+
if (params.scopes?.length)
|
|
285
|
+
body.Scopes = params.scopes;
|
|
286
|
+
if (params.redirectUrl)
|
|
287
|
+
body.RedirectUrl = params.redirectUrl;
|
|
288
|
+
if (params.forceAuthentication !== undefined)
|
|
289
|
+
body.ForceAuthentication = params.forceAuthentication ? 1 : 0;
|
|
290
|
+
if (params.poolName)
|
|
291
|
+
body.PoolName = params.poolName;
|
|
292
|
+
const result = (await this.signedPost(this.config.endpoint, "GetResourceOauth2Token", body));
|
|
293
|
+
return {
|
|
294
|
+
accessToken: result.AccessToken,
|
|
295
|
+
authorizationUrl: result.AuthorizationUrl,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
async oauth2Callback(params) {
|
|
299
|
+
const body = {
|
|
300
|
+
Code: params.code,
|
|
301
|
+
State: params.state,
|
|
302
|
+
IdentityToken: params.identityToken,
|
|
303
|
+
};
|
|
304
|
+
if (params.error)
|
|
305
|
+
body.Error = params.error;
|
|
306
|
+
const result = (await this.signedPost(this.config.endpoint, "Oauth2Callback", body));
|
|
307
|
+
if (!result.AccessToken) {
|
|
308
|
+
throw new Error("Identity Oauth2Callback: missing AccessToken");
|
|
309
|
+
}
|
|
310
|
+
return { accessToken: result.AccessToken };
|
|
311
|
+
}
|
|
312
|
+
async getResourceApiKey(params) {
|
|
313
|
+
const body = {
|
|
314
|
+
ProviderName: params.providerName,
|
|
315
|
+
IdentityToken: params.identityToken,
|
|
316
|
+
};
|
|
317
|
+
const result = (await this.signedPost(this.config.endpoint, "GetResourceApiKey", body));
|
|
318
|
+
if (!result.ApiKey) {
|
|
319
|
+
throw new Error("Identity GetResourceApiKey: missing ApiKey");
|
|
320
|
+
}
|
|
321
|
+
return {
|
|
322
|
+
apiKey: result.ApiKey,
|
|
323
|
+
metadata: result.Metadata,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
async checkPermission(params) {
|
|
327
|
+
const body = {
|
|
328
|
+
NamespaceName: params.namespaceName,
|
|
329
|
+
Principal: params.principal,
|
|
330
|
+
Operation: params.action,
|
|
331
|
+
Resource: params.resource,
|
|
332
|
+
...(params.originalCallers?.length ? { OriginalCallers: params.originalCallers } : {}),
|
|
333
|
+
};
|
|
334
|
+
const result = (await this.signedPost(this.config.endpoint, "CheckPermission", body));
|
|
335
|
+
return {
|
|
336
|
+
allowed: result.Allowed ?? false,
|
|
337
|
+
message: result.Message,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
async listCredentialProviders(params) {
|
|
341
|
+
const body = {
|
|
342
|
+
PageNumber: params.PageNumber ?? 1,
|
|
343
|
+
PageSize: params.PageSize ?? 20,
|
|
344
|
+
};
|
|
345
|
+
if (params.PoolName)
|
|
346
|
+
body.PoolName = params.PoolName;
|
|
347
|
+
if (params.Filter && Object.keys(params.Filter).length > 0) {
|
|
348
|
+
body.Filter = params.Filter;
|
|
349
|
+
}
|
|
350
|
+
const r = (await this.signedPost(this.config.endpoint, "ListCredentialProviders", body));
|
|
351
|
+
const providers = r.CredentialProviders ?? r.Data ?? [];
|
|
352
|
+
return {
|
|
353
|
+
CredentialProviders: providers,
|
|
354
|
+
Data: providers,
|
|
355
|
+
TotalCount: r.TotalCount ?? providers.length,
|
|
356
|
+
PageNumber: r.PageNumber ?? 1,
|
|
357
|
+
PageSize: r.PageSize ?? 20,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
async getUserPool(params) {
|
|
361
|
+
const body = { UserPoolUid: params.userPoolUid };
|
|
362
|
+
const r = (await this.signedPost(this.config.endpoint, "GetUserPool", body));
|
|
363
|
+
return toUserPoolResult(r);
|
|
364
|
+
}
|
|
365
|
+
async listUserPools(params) {
|
|
366
|
+
const body = {};
|
|
367
|
+
body.PageNumber = params.pageNumber ?? 1;
|
|
368
|
+
body.PageSize = params.pageSize ?? 10;
|
|
369
|
+
if (params.projectName)
|
|
370
|
+
body.ProjectName = params.projectName;
|
|
371
|
+
if (params.sortField)
|
|
372
|
+
body.SortField = params.sortField;
|
|
373
|
+
if (params.sortDirection)
|
|
374
|
+
body.SortDirection = params.sortDirection;
|
|
375
|
+
if (params.filter) {
|
|
376
|
+
const f = params.filter;
|
|
377
|
+
body.Filter = {
|
|
378
|
+
...(f.name && { Name: f.name }),
|
|
379
|
+
...(f.trn && { Trn: f.trn }),
|
|
380
|
+
...(f.description && { Description: f.description }),
|
|
381
|
+
...(f.uid && { Uid: f.uid }),
|
|
382
|
+
...(f.tagFilters && {
|
|
383
|
+
TagFilters: f.tagFilters.map((t) => ({
|
|
384
|
+
Key: t.key,
|
|
385
|
+
Values: t.values,
|
|
386
|
+
})),
|
|
387
|
+
}),
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
const r = (await this.signedPost(this.config.endpoint, "ListUserPools", body));
|
|
391
|
+
const data = (r.Data ?? []).map((d) => {
|
|
392
|
+
const tags = d.Tags;
|
|
393
|
+
return {
|
|
394
|
+
uid: d.Uid ?? "",
|
|
395
|
+
name: d.Name ?? "",
|
|
396
|
+
description: d.Description,
|
|
397
|
+
domain: d.Domain ?? "",
|
|
398
|
+
trn: d.Trn,
|
|
399
|
+
createTime: d.CreateTime,
|
|
400
|
+
updateTime: d.UpdateTime,
|
|
401
|
+
tags: tags?.map((t) => ({ key: t.Key ?? "", value: t.Value ?? "" })),
|
|
402
|
+
projectName: d.ProjectName,
|
|
403
|
+
};
|
|
404
|
+
});
|
|
405
|
+
return {
|
|
406
|
+
pageNumber: r.PageNumber ?? 1,
|
|
407
|
+
pageSize: r.PageSize ?? 10,
|
|
408
|
+
totalCount: r.TotalCount ?? 0,
|
|
409
|
+
data,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
async createUserPool(params) {
|
|
413
|
+
const body = {
|
|
414
|
+
Name: params.name,
|
|
415
|
+
Description: params.description,
|
|
416
|
+
ProjectName: params.projectName ?? "default",
|
|
417
|
+
PasswordSignInEnabled: params.passwordSignInEnabled ?? true,
|
|
418
|
+
EmailPasswordlessSignInEnabled: params.emailPasswordlessSignInEnabled ?? false,
|
|
419
|
+
SelfSignUpEnabled: params.selfSignUpEnabled ?? true,
|
|
420
|
+
SignInAttributes: params.signInAttributes ?? ["email"],
|
|
421
|
+
RequiredSignUpAttributes: params.requiredSignUpAttributes ?? ["email"],
|
|
422
|
+
SignUpAutoVerificationEnabled: params.signUpAutoVerificationEnabled ?? true,
|
|
423
|
+
SmsPasswordlessSignInEnabled: params.smsPasswordlessSignInEnabled ?? false,
|
|
424
|
+
SelfAccountRecoveryEnabled: params.selfAccountRecoveryEnabled ?? true,
|
|
425
|
+
UnconfirmedUserSignInEnabled: params.unconfirmedUserSignInEnabled ?? true,
|
|
426
|
+
SmsAnonymousSignUpEnabled: params.smsAnonymousSignUpEnabled ?? false,
|
|
427
|
+
Tags: params.tags?.map((t) => ({ Key: t.key, Value: t.value })),
|
|
428
|
+
Brand: params.brand ? { Name: params.brand.name, LogoUri: params.brand.logoUri } : undefined,
|
|
429
|
+
};
|
|
430
|
+
const r = (await this.signedPost(this.config.endpoint, "CreateUserPool", body));
|
|
431
|
+
return toUserPoolResult(r);
|
|
432
|
+
}
|
|
433
|
+
async getUserPoolClient(params) {
|
|
434
|
+
const body = {
|
|
435
|
+
UserPoolUid: params.userPoolUid,
|
|
436
|
+
ClientUid: params.clientUid,
|
|
437
|
+
};
|
|
438
|
+
const r = (await this.signedPost(this.config.endpoint, "GetUserPoolClient", body));
|
|
439
|
+
return toUserPoolClientResult(r);
|
|
440
|
+
}
|
|
441
|
+
async listUserPoolClients(params) {
|
|
442
|
+
const body = { UserPoolUid: params.userPoolUid };
|
|
443
|
+
body.PageNumber = params.pageNumber ?? 1;
|
|
444
|
+
body.PageSize = params.pageSize ?? 10;
|
|
445
|
+
if (params.sortField)
|
|
446
|
+
body.SortField = params.sortField;
|
|
447
|
+
if (params.sortDirection)
|
|
448
|
+
body.SortDirection = params.sortDirection;
|
|
449
|
+
if (params.filter) {
|
|
450
|
+
const f = params.filter;
|
|
451
|
+
body.Filter = {
|
|
452
|
+
...(f.name && { Name: f.name }),
|
|
453
|
+
...(f.description && { Description: f.description }),
|
|
454
|
+
...(f.clientTypes && { ClientTypes: f.clientTypes }),
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
const r = (await this.signedPost(this.config.endpoint, "ListUserPoolClients", body));
|
|
458
|
+
const data = (r.Data ?? []).map((d) => ({
|
|
459
|
+
uid: d.Uid ?? "",
|
|
460
|
+
name: d.Name ?? "",
|
|
461
|
+
description: d.Description,
|
|
462
|
+
clientType: d.ClientType,
|
|
463
|
+
createTime: d.CreateTime,
|
|
464
|
+
updateTime: d.UpdateTime,
|
|
465
|
+
}));
|
|
466
|
+
return {
|
|
467
|
+
pageNumber: r.PageNumber ?? 1,
|
|
468
|
+
pageSize: r.PageSize ?? 10,
|
|
469
|
+
totalCount: r.TotalCount ?? 0,
|
|
470
|
+
data,
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
async createUserPoolClient(params) {
|
|
474
|
+
const body = {
|
|
475
|
+
UserPoolUid: params.userPoolUid,
|
|
476
|
+
Name: params.name,
|
|
477
|
+
Description: params.description,
|
|
478
|
+
LogoUri: params.logoUri,
|
|
479
|
+
ClientType: params.clientType ?? "WEB_APPLICATION",
|
|
480
|
+
AllowedCallbackUrls: params.allowedCallbackUrls ?? [],
|
|
481
|
+
AllowedLogoutUrls: params.allowedLogoutUrls,
|
|
482
|
+
AllowedWebOrigins: params.allowedWebOrigins,
|
|
483
|
+
AllowedCors: params.allowedCors,
|
|
484
|
+
};
|
|
485
|
+
const r = (await this.signedPost(this.config.endpoint, "CreateUserPoolClient", body));
|
|
486
|
+
return toUserPoolClientResult(r);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Resolve OIDC config from UserPool and Client names (from_veidentity style).
|
|
491
|
+
* If pool/client not found and autoCreate=true, creates them.
|
|
492
|
+
* Returns discoveryUrl, clientId, clientSecret, callbackUrl for OIDC flow.
|
|
493
|
+
*/
|
|
494
|
+
export async function resolveOIDCConfig(client, params) {
|
|
495
|
+
const { userPoolName, userPoolUid, clientName, clientUid, redirectUri, scope = "openid profile email", autoCreate = true, clientType = "WEB_APPLICATION", } = params;
|
|
496
|
+
if (!userPoolName && !userPoolUid) {
|
|
497
|
+
throw new Error("Either userPoolName or userPoolUid must be provided");
|
|
498
|
+
}
|
|
499
|
+
if (!clientName && !clientUid) {
|
|
500
|
+
throw new Error("Either clientName or clientUid must be provided");
|
|
501
|
+
}
|
|
502
|
+
let poolUid;
|
|
503
|
+
let poolDomain;
|
|
504
|
+
if (userPoolUid) {
|
|
505
|
+
const pool = await client.getUserPool({ userPoolUid });
|
|
506
|
+
poolUid = pool.uid;
|
|
507
|
+
poolDomain = pool.domain;
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
const list = await client.listUserPools({
|
|
511
|
+
pageSize: 50,
|
|
512
|
+
filter: { name: userPoolName },
|
|
513
|
+
});
|
|
514
|
+
const found = list.data.find((p) => p.name === userPoolName);
|
|
515
|
+
if (found) {
|
|
516
|
+
poolUid = found.uid;
|
|
517
|
+
poolDomain = found.domain;
|
|
518
|
+
}
|
|
519
|
+
else if (autoCreate && userPoolName) {
|
|
520
|
+
const created = await client.createUserPool({ name: userPoolName });
|
|
521
|
+
poolUid = created.uid;
|
|
522
|
+
poolDomain = created.domain;
|
|
523
|
+
}
|
|
524
|
+
else {
|
|
525
|
+
throw new Error(`User pool '${userPoolName}' not found (autoCreate=false or only UID provided)`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
let clientId;
|
|
529
|
+
let clientSecret;
|
|
530
|
+
if (clientUid) {
|
|
531
|
+
const c = await client.getUserPoolClient({
|
|
532
|
+
userPoolUid: poolUid,
|
|
533
|
+
clientUid,
|
|
534
|
+
});
|
|
535
|
+
clientId = c.uid;
|
|
536
|
+
clientSecret = c.clientSecret;
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
const list = await client.listUserPoolClients({
|
|
540
|
+
userPoolUid: poolUid,
|
|
541
|
+
pageSize: 50,
|
|
542
|
+
filter: { name: clientName },
|
|
543
|
+
});
|
|
544
|
+
const found = list.data.find((c) => c.name === clientName);
|
|
545
|
+
if (found) {
|
|
546
|
+
const full = await client.getUserPoolClient({
|
|
547
|
+
userPoolUid: poolUid,
|
|
548
|
+
clientUid: found.uid,
|
|
549
|
+
});
|
|
550
|
+
clientId = full.uid;
|
|
551
|
+
clientSecret = full.clientSecret;
|
|
552
|
+
}
|
|
553
|
+
else if (autoCreate && clientName) {
|
|
554
|
+
const created = await client.createUserPoolClient({
|
|
555
|
+
userPoolUid: poolUid,
|
|
556
|
+
name: clientName,
|
|
557
|
+
clientType,
|
|
558
|
+
allowedCallbackUrls: [redirectUri],
|
|
559
|
+
});
|
|
560
|
+
clientId = created.uid;
|
|
561
|
+
clientSecret = created.clientSecret;
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
throw new Error(`Client '${clientName}' not found (autoCreate=false or only UID provided)`);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
const discoveryBase = poolDomain.startsWith("http") ? poolDomain : `https://${poolDomain}`;
|
|
568
|
+
const discoveryUrl = discoveryBase.endsWith("/")
|
|
569
|
+
? `${discoveryBase}.well-known/openid-configuration`
|
|
570
|
+
: `${discoveryBase}/.well-known/openid-configuration`;
|
|
571
|
+
return {
|
|
572
|
+
discoveryUrl,
|
|
573
|
+
clientId,
|
|
574
|
+
clientSecret,
|
|
575
|
+
scope,
|
|
576
|
+
callbackUrl: redirectUri,
|
|
577
|
+
};
|
|
578
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identity credentials loader (veadk-style).
|
|
3
|
+
* Loads AK/SK from:
|
|
4
|
+
* 1. Explicit config (accessKeyId, secretAccessKey, sessionToken)
|
|
5
|
+
* 2. Environment variables (VOLCENGINE_ACCESS_KEY, VOLCENGINE_SECRET_KEY, VOLCENGINE_SESSION_TOKEN)
|
|
6
|
+
* 3. Credential file (VOLCENGINE_CREDENTIALS_FILE or /var/run/secrets/iam/credential)
|
|
7
|
+
* Supports STS session token. Optional AssumeRole via roleTrn.
|
|
8
|
+
*/
|
|
9
|
+
export type IdentityCredentials = {
|
|
10
|
+
accessKeyId: string;
|
|
11
|
+
secretAccessKey: string;
|
|
12
|
+
sessionToken?: string;
|
|
13
|
+
};
|
|
14
|
+
export type LoadCredentialsOptions = {
|
|
15
|
+
accessKeyId?: string;
|
|
16
|
+
secretAccessKey?: string;
|
|
17
|
+
sessionToken?: string;
|
|
18
|
+
credentialsFile?: string;
|
|
19
|
+
roleTrn?: string;
|
|
20
|
+
resolvePath?: (p: string) => string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Load credentials from config, env, or file (veadk-style).
|
|
24
|
+
* Order: explicit > env > file.
|
|
25
|
+
* Returns resolved credentials or throws if none found.
|
|
26
|
+
*/
|
|
27
|
+
export declare function loadIdentityCredentials(opts?: LoadCredentialsOptions): Promise<IdentityCredentials>;
|
|
28
|
+
//# sourceMappingURL=identity-credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity-credentials.d.ts","sourceRoot":"","sources":["../../../src/services/identity-credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AASF,MAAM,MAAM,sBAAsB,GAAG;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;CACrC,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,GAAE,sBAA2B,GAChC,OAAO,CAAC,mBAAmB,CAAC,CAkD9B"}
|