@m1a0rz/agent-identity 0.5.1 → 0.5.2-cs
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/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +177 -24
- package/dist/src/actions/identity-actions.d.ts.map +1 -1
- package/dist/src/actions/identity-actions.js +172 -23
- package/dist/src/local-server/handlers.d.ts.map +1 -1
- package/dist/src/local-server/handlers.js +6 -1
- package/dist/src/preflight/plugin-preflight.d.ts +9 -0
- package/dist/src/preflight/plugin-preflight.d.ts.map +1 -1
- package/dist/src/preflight/plugin-preflight.js +22 -8
- package/dist/src/preflight/plugin-state.d.ts.map +1 -1
- package/dist/src/preflight/plugin-state.js +10 -4
- package/dist/src/services/identity-client.d.ts +59 -1
- package/dist/src/services/identity-client.d.ts.map +1 -1
- package/dist/src/services/identity-client.js +720 -93
- package/dist/src/services/identity-credentials.d.ts +1 -0
- package/dist/src/services/identity-credentials.d.ts.map +1 -1
- package/dist/src/services/identity-credentials.js +1 -23
- package/dist/src/services/identity-service.d.ts +3 -0
- package/dist/src/services/identity-service.d.ts.map +1 -1
- package/dist/src/services/identity-service.js +1 -0
- package/dist/src/store/credential-env-snapshot.d.ts.map +1 -1
- package/dist/src/store/credential-env-snapshot.js +2 -0
- package/dist/src/store/encryption.d.ts.map +1 -1
- package/dist/src/store/encryption.js +15 -11
- package/dist/src/store/tool-approval-store.d.ts +40 -0
- package/dist/src/store/tool-approval-store.d.ts.map +1 -0
- package/dist/src/store/tool-approval-store.js +162 -0
- package/dist/src/tools/identity-approve-tool.d.ts +15 -0
- package/dist/src/tools/identity-approve-tool.d.ts.map +1 -0
- package/dist/src/tools/identity-approve-tool.js +51 -0
- package/dist/src/types.d.ts +18 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/approval-channel.d.ts +7 -0
- package/dist/src/utils/approval-channel.d.ts.map +1 -0
- package/dist/src/utils/approval-channel.js +28 -0
- package/dist/src/utils/trust-anchor.d.ts +2 -0
- package/dist/src/utils/trust-anchor.d.ts.map +1 -0
- package/dist/src/utils/trust-anchor.js +48 -0
- package/dist/src/variant.d.ts +16 -0
- package/dist/src/variant.d.ts.map +1 -0
- package/dist/src/variant.js +18 -0
- package/package.json +1 -1
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
import { loadIdentityCredentials } from "./identity-credentials.js";
|
|
17
17
|
import { canonicalQueryString, signRequest } from "../utils/sts-signer.js";
|
|
18
18
|
import { resolveIdentityApiEndpoint, signingRegionFromIdentityEndpoint, } from "../utils/resolve-identity-endpoint.js";
|
|
19
|
+
import { randomUUID } from "node:crypto";
|
|
20
|
+
import { getfreshTrustAnchorToken } from "../utils/trust-anchor.js";
|
|
19
21
|
export { loadIdentityCredentials } from "./identity-credentials.js";
|
|
20
22
|
function isWorkloadNotFoundError(err) {
|
|
21
23
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -152,7 +154,42 @@ export class IdentityClient {
|
|
|
152
154
|
}
|
|
153
155
|
return {
|
|
154
156
|
workloadAccessToken: result.WorkloadAccessToken,
|
|
155
|
-
expiresAt: result.ExpiresAt ??
|
|
157
|
+
expiresAt: Number.isFinite(Date.parse(result.ExpiresAt ?? "")) ? Date.parse(result.ExpiresAt) : (Date.now() + 3600 * 1000),
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
try {
|
|
161
|
+
return await doFetch();
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
// When workload (agent) does not exist (404), create it and retry. Only when Name was passed.
|
|
165
|
+
if (params.name && isWorkloadNotFoundError(err)) {
|
|
166
|
+
await this.createWorkloadIdentity({
|
|
167
|
+
workloadPoolName: params.workloadPoolName ?? "default",
|
|
168
|
+
name: params.name,
|
|
169
|
+
category: "Agent",
|
|
170
|
+
});
|
|
171
|
+
return doFetch();
|
|
172
|
+
}
|
|
173
|
+
throw err;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async getWorkloadAccessToken(params) {
|
|
177
|
+
const doFetch = async () => {
|
|
178
|
+
const body = {
|
|
179
|
+
WorkloadPoolName: params.workloadPoolName ?? "default",
|
|
180
|
+
DurationSeconds: params.durationSeconds ?? 3600,
|
|
181
|
+
};
|
|
182
|
+
if (params.name)
|
|
183
|
+
body.Name = params.name;
|
|
184
|
+
if (params.audience?.length)
|
|
185
|
+
body.Audience = params.audience;
|
|
186
|
+
const result = (await this.signedPost("GetWorkloadAccessToken", body, "2025-10-30"));
|
|
187
|
+
if (!result?.WorkloadAccessToken) {
|
|
188
|
+
throw new Error("Identity API: missing WorkloadAccessToken in response");
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
workloadAccessToken: result.WorkloadAccessToken,
|
|
192
|
+
expiresAt: Number.isFinite(Date.parse(result.ExpiresAt ?? "")) ? Date.parse(result.ExpiresAt) : (Date.now() + 3600 * 1000),
|
|
156
193
|
};
|
|
157
194
|
};
|
|
158
195
|
try {
|
|
@@ -573,110 +610,700 @@ export class IdentityClient {
|
|
|
573
610
|
return toUserPoolClientResult(r);
|
|
574
611
|
}
|
|
575
612
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
export async function resolveOIDCConfig(client, params) {
|
|
583
|
-
const { userPoolName, userPoolUid, clientName, clientUid, redirectUri, scope = "openid profile email offline_access", autoCreate = true, clientType = "WEB_APPLICATION", } = params;
|
|
584
|
-
if (!userPoolName && !userPoolUid) {
|
|
585
|
-
throw new Error("Either userPoolName or userPoolUid must be provided");
|
|
586
|
-
}
|
|
587
|
-
if (!clientName && !clientUid) {
|
|
588
|
-
throw new Error("Either clientName or clientUid must be provided");
|
|
613
|
+
export class IdentityDataPlaneClient {
|
|
614
|
+
config;
|
|
615
|
+
lazyResolvedEndpoint = null;
|
|
616
|
+
cachedWorkloadToken;
|
|
617
|
+
constructor(config) {
|
|
618
|
+
this.config = config;
|
|
589
619
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
620
|
+
getJwtExpMs(token) {
|
|
621
|
+
try {
|
|
622
|
+
const parts = token.split(".");
|
|
623
|
+
if (parts.length < 2)
|
|
624
|
+
return null;
|
|
625
|
+
const b64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
626
|
+
const payloadStr = Buffer.from(b64, "base64").toString("utf8");
|
|
627
|
+
const payload = JSON.parse(payloadStr);
|
|
628
|
+
if (typeof payload.exp === "number")
|
|
629
|
+
return payload.exp * 1000;
|
|
630
|
+
return null;
|
|
631
|
+
}
|
|
632
|
+
catch {
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
596
635
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
636
|
+
async getRefreshWorkloadToken() {
|
|
637
|
+
try {
|
|
638
|
+
if (this.cachedWorkloadToken?.token && this.cachedWorkloadToken?.expiresAt) {
|
|
639
|
+
const remainingMs = this.cachedWorkloadToken.expiresAt - Date.now();
|
|
640
|
+
if (remainingMs > 10 * 60 * 1000) {
|
|
641
|
+
return {
|
|
642
|
+
workloadAccessToken: this.cachedWorkloadToken.token,
|
|
643
|
+
expiresAt: this.cachedWorkloadToken.expiresAt,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
catch {
|
|
649
|
+
// ignore cache parse errors and fall through to refresh
|
|
650
|
+
}
|
|
651
|
+
const res = await this.getWorkloadAccessToken({
|
|
652
|
+
trustAnchorName: this.config.trustAnchorName,
|
|
653
|
+
name: this.config.workloadName,
|
|
654
|
+
durationSeconds: 3600,
|
|
655
|
+
audience: this.config.audience,
|
|
601
656
|
});
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
657
|
+
this.cachedWorkloadToken = {
|
|
658
|
+
token: res.workloadAccessToken,
|
|
659
|
+
expiresAt: res.expiresAt,
|
|
660
|
+
};
|
|
661
|
+
return res;
|
|
662
|
+
}
|
|
663
|
+
getResolvedEndpoint() {
|
|
664
|
+
if (!this.lazyResolvedEndpoint) {
|
|
665
|
+
this.lazyResolvedEndpoint = resolveIdentityApiEndpoint({
|
|
666
|
+
endpoint: this.config.endpoint,
|
|
667
|
+
regionMetadataUrl: this.config.regionMetadataUrl,
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
return this.lazyResolvedEndpoint;
|
|
671
|
+
}
|
|
672
|
+
async resolveSigningRegion(baseUrl) {
|
|
673
|
+
const explicit = this.config.region?.trim();
|
|
674
|
+
if (explicit)
|
|
675
|
+
return explicit;
|
|
676
|
+
return signingRegionFromIdentityEndpoint(baseUrl) ?? "cn-beijing";
|
|
677
|
+
}
|
|
678
|
+
async resolveCredentials() {
|
|
679
|
+
if (this.config.credentialsGetter) {
|
|
680
|
+
return this.config.credentialsGetter();
|
|
606
681
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
682
|
+
return {
|
|
683
|
+
workloadToken: await this.getRefreshWorkloadToken().then((r) => r.workloadAccessToken),
|
|
684
|
+
accessKeyId: this.config.accessKeyId ?? "",
|
|
685
|
+
secretAccessKey: this.config.secretAccessKey ?? "",
|
|
686
|
+
sessionToken: this.config.sessionToken ?? "",
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
async getWorkloadAccessTokenForJWT(params) {
|
|
690
|
+
const doFetch = async () => {
|
|
691
|
+
const body = {
|
|
692
|
+
UserToken: params.userToken,
|
|
693
|
+
WorkloadPoolName: params.workloadPoolName ?? "default",
|
|
694
|
+
DurationSeconds: params.durationSeconds ?? 3600,
|
|
695
|
+
TrustAnchorName: params.trustAnchorName ?? "",
|
|
696
|
+
};
|
|
697
|
+
body.Credentials = (this.config.storeDir ? getfreshTrustAnchorToken(this.config.storeDir) : undefined) ?? "";
|
|
698
|
+
if (params.name)
|
|
699
|
+
body.Name = params.name;
|
|
700
|
+
if (params.audience?.length)
|
|
701
|
+
body.Audience = params.audience;
|
|
702
|
+
const result = (await this.workloadPoolSignedPost("getworkloadaccesstokenforjwt", body, "2025-10-30", this.config.workloadEndpoint));
|
|
703
|
+
if (!result?.WorkloadAccessToken) {
|
|
704
|
+
throw new Error("Identity API: missing WorkloadAccessToken in response");
|
|
705
|
+
}
|
|
706
|
+
return {
|
|
707
|
+
workloadAccessToken: result.WorkloadAccessToken,
|
|
708
|
+
expiresAt: (() => {
|
|
709
|
+
const fromJwt = this.getJwtExpMs(result.WorkloadAccessToken);
|
|
710
|
+
if (fromJwt != null)
|
|
711
|
+
return fromJwt;
|
|
712
|
+
const parsed = Date.parse(result.ExpiresAt ?? "");
|
|
713
|
+
return Number.isFinite(parsed) ? parsed : (Date.now() + 3600 * 1000);
|
|
714
|
+
})(),
|
|
715
|
+
};
|
|
716
|
+
};
|
|
717
|
+
try {
|
|
718
|
+
return await doFetch();
|
|
611
719
|
}
|
|
612
|
-
|
|
613
|
-
|
|
720
|
+
catch (err) {
|
|
721
|
+
// When workload (agent) does not exist (404), create it and retry. Only when Name was passed.
|
|
722
|
+
if (params.name && isWorkloadNotFoundError(err)) {
|
|
723
|
+
await this.createWorkloadIdentity({
|
|
724
|
+
workloadPoolName: params.workloadPoolName ?? "default",
|
|
725
|
+
name: params.name,
|
|
726
|
+
category: "Agent",
|
|
727
|
+
});
|
|
728
|
+
return doFetch();
|
|
729
|
+
}
|
|
730
|
+
throw err;
|
|
614
731
|
}
|
|
615
732
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
733
|
+
async getWorkloadAccessToken(params) {
|
|
734
|
+
const doFetch = async () => {
|
|
735
|
+
const body = {
|
|
736
|
+
WorkloadPoolName: params.workloadPoolName ?? "default",
|
|
737
|
+
TrustAnchorName: params.trustAnchorName ?? "",
|
|
738
|
+
DurationSeconds: params.durationSeconds ?? 3600,
|
|
739
|
+
};
|
|
740
|
+
body.Credentials = (this.config.storeDir ? getfreshTrustAnchorToken(this.config.storeDir) : undefined) ?? "";
|
|
741
|
+
if (params.audience?.length)
|
|
742
|
+
body.Audience = params.audience;
|
|
743
|
+
if (params.name)
|
|
744
|
+
body.Name = params.name;
|
|
745
|
+
const result = (await this.workloadPoolSignedPost("getworkloadaccesstoken", body, "2025-10-30", this.config.workloadEndpoint));
|
|
746
|
+
if (!result?.WorkloadAccessToken) {
|
|
747
|
+
throw new Error("Identity API: missing WorkloadAccessToken in response");
|
|
748
|
+
}
|
|
749
|
+
return {
|
|
750
|
+
workloadAccessToken: result.WorkloadAccessToken,
|
|
751
|
+
expiresAt: (() => {
|
|
752
|
+
const fromJwt = this.getJwtExpMs(result.WorkloadAccessToken);
|
|
753
|
+
if (fromJwt != null)
|
|
754
|
+
return fromJwt;
|
|
755
|
+
const parsed = Date.parse(result.ExpiresAt ?? "");
|
|
756
|
+
return Number.isFinite(parsed) ? parsed : (Date.now() + 3600 * 1000);
|
|
757
|
+
})(),
|
|
758
|
+
};
|
|
759
|
+
};
|
|
760
|
+
try {
|
|
761
|
+
return await doFetch();
|
|
762
|
+
}
|
|
763
|
+
catch (err) {
|
|
764
|
+
// When workload (agent) does not exist (404), create it and retry. Only when Name was passed.
|
|
765
|
+
if (params.name && isWorkloadNotFoundError(err)) {
|
|
766
|
+
await this.createWorkloadIdentity({
|
|
767
|
+
workloadPoolName: params.workloadPoolName ?? "default",
|
|
768
|
+
name: params.name,
|
|
769
|
+
category: "Agent",
|
|
770
|
+
});
|
|
771
|
+
return doFetch();
|
|
772
|
+
}
|
|
773
|
+
throw err;
|
|
774
|
+
}
|
|
625
775
|
}
|
|
626
|
-
|
|
627
|
-
const
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
776
|
+
async createWorkloadIdentity(params) {
|
|
777
|
+
const body = {
|
|
778
|
+
WorkloadPoolName: params.workloadPoolName,
|
|
779
|
+
Name: params.name,
|
|
780
|
+
};
|
|
781
|
+
if (params.category)
|
|
782
|
+
body.Category = params.category;
|
|
783
|
+
if (params.description)
|
|
784
|
+
body.Description = params.description;
|
|
785
|
+
try {
|
|
786
|
+
await this.signedPost("CreateWorkloadIdentity", body, "2025-10-30");
|
|
787
|
+
}
|
|
788
|
+
catch (err) {
|
|
789
|
+
// Duplicated (409): workload already exists, e.g. race with another process. Retry will succeed.
|
|
790
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
791
|
+
if (/409|Duplicated/i.test(msg))
|
|
792
|
+
return;
|
|
793
|
+
throw err;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Signed POST using sts-signer (same encoding as volcengine-nodejs-sdk).
|
|
798
|
+
* Service: id (Identity API), method: POST.
|
|
799
|
+
*/
|
|
800
|
+
async signedPost(action, body, versionOverride) {
|
|
801
|
+
const baseUrl = await this.getResolvedEndpoint() + "/" + action;
|
|
802
|
+
;
|
|
803
|
+
const creds = await this.resolveCredentials();
|
|
804
|
+
const { accessKeyId, secretAccessKey, sessionToken, workloadToken } = creds;
|
|
805
|
+
const version = versionOverride ?? this.config.version ?? "2025-10-30";
|
|
806
|
+
const url = new URL(baseUrl);
|
|
807
|
+
const query = { Action: action, Version: version };
|
|
808
|
+
const bodyStr = JSON.stringify(body);
|
|
809
|
+
const qs = canonicalQueryString(query);
|
|
810
|
+
url.search = qs ? `?${qs}` : "";
|
|
811
|
+
const headers = {
|
|
812
|
+
"Content-Type": "application/json; charset=UTF-8",
|
|
813
|
+
"X-Asi-Token": workloadToken ?? "",
|
|
814
|
+
};
|
|
815
|
+
const res = await fetch(url.toString(), {
|
|
816
|
+
method: "POST",
|
|
817
|
+
headers,
|
|
818
|
+
body: bodyStr,
|
|
631
819
|
});
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
userPoolUid: poolUid,
|
|
636
|
-
clientUid: found.uid,
|
|
637
|
-
});
|
|
638
|
-
clientId = full.uid;
|
|
639
|
-
clientSecret = full.clientSecret;
|
|
820
|
+
if (!res.ok) {
|
|
821
|
+
const text = await res.text();
|
|
822
|
+
throw new Error(`Identity API error ${res.status}: ${text}`);
|
|
640
823
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
name: clientName,
|
|
645
|
-
clientType,
|
|
646
|
-
allowedCallbackUrls: [redirectUri],
|
|
647
|
-
});
|
|
648
|
-
clientId = created.uid;
|
|
649
|
-
clientSecret = created.clientSecret;
|
|
824
|
+
const json = (await res.json());
|
|
825
|
+
if (json.Error) {
|
|
826
|
+
throw new Error(`Identity API error: ${json.Error.Code ?? "Unknown"} - ${json.Error.Message ?? ""}`);
|
|
650
827
|
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
828
|
+
return json.Result ?? {};
|
|
829
|
+
}
|
|
830
|
+
async workloadPoolSignedPost(action, body, versionOverride, endpoint) {
|
|
831
|
+
const baseUrl = endpoint + "/" + action;
|
|
832
|
+
const version = versionOverride ?? this.config.version ?? "2025-10-30";
|
|
833
|
+
const url = new URL(baseUrl);
|
|
834
|
+
const query = { Action: action, Version: version };
|
|
835
|
+
const bodyStr = JSON.stringify(body);
|
|
836
|
+
const qs = canonicalQueryString(query);
|
|
837
|
+
url.search = qs ? `?${qs}` : "";
|
|
838
|
+
const headers = {
|
|
839
|
+
"Content-Type": "application/json; charset=UTF-8",
|
|
840
|
+
"X-Asi-Request-Id": randomUUID(),
|
|
841
|
+
};
|
|
842
|
+
const res = await fetch(url.toString(), {
|
|
843
|
+
method: "POST",
|
|
844
|
+
headers,
|
|
845
|
+
body: bodyStr,
|
|
667
846
|
});
|
|
668
|
-
|
|
847
|
+
if (!res.ok) {
|
|
848
|
+
const text = await res.text();
|
|
849
|
+
throw new Error(`Identity API error ${res.status}: ${text}`);
|
|
850
|
+
}
|
|
851
|
+
const json = (await res.json());
|
|
852
|
+
if (json.Error) {
|
|
853
|
+
throw new Error(`Identity API error: ${json.Error.Code ?? "Unknown"} - ${json.Error.Message ?? ""}`);
|
|
854
|
+
}
|
|
855
|
+
return json.Result ?? {};
|
|
856
|
+
}
|
|
857
|
+
async getResourceOauth2Token(params) {
|
|
858
|
+
const body = {
|
|
859
|
+
ProviderName: params.providerName,
|
|
860
|
+
IdentityToken: params.identityToken,
|
|
861
|
+
};
|
|
862
|
+
if (params.flow)
|
|
863
|
+
body.Flow = params.flow;
|
|
864
|
+
if (params.scopes?.length)
|
|
865
|
+
body.Scopes = params.scopes;
|
|
866
|
+
if (params.redirectUrl)
|
|
867
|
+
body.RedirectUrl = params.redirectUrl;
|
|
868
|
+
if (params.forceAuthentication !== undefined)
|
|
869
|
+
body.ForceAuthentication = params.forceAuthentication ? 1 : 0;
|
|
870
|
+
if (params.poolName) {
|
|
871
|
+
body.PoolName = params.poolName;
|
|
872
|
+
}
|
|
873
|
+
const result = (await this.signedPost("getResourceOauth2Token", body));
|
|
874
|
+
return {
|
|
875
|
+
accessToken: result.AccessToken,
|
|
876
|
+
authorizationUrl: result.AuthorizationUrl,
|
|
877
|
+
};
|
|
669
878
|
}
|
|
670
|
-
|
|
671
|
-
|
|
879
|
+
async oauth2Callback(params) {
|
|
880
|
+
const body = {
|
|
881
|
+
Code: params.code,
|
|
882
|
+
State: params.state,
|
|
883
|
+
IdentityToken: params.identityToken,
|
|
884
|
+
};
|
|
885
|
+
if (params.error)
|
|
886
|
+
body.Error = params.error;
|
|
887
|
+
const result = (await this.signedPost("Oauth2Callback", body));
|
|
888
|
+
if (!result.AccessToken) {
|
|
889
|
+
throw new Error("Identity Oauth2Callback: missing AccessToken");
|
|
890
|
+
}
|
|
891
|
+
return { accessToken: result.AccessToken };
|
|
892
|
+
}
|
|
893
|
+
async getResourceApiKey(params) {
|
|
894
|
+
const body = {
|
|
895
|
+
ProviderName: params.providerName,
|
|
896
|
+
IdentityToken: params.identityToken,
|
|
897
|
+
};
|
|
898
|
+
if (params.poolName)
|
|
899
|
+
body.PoolName = params.poolName;
|
|
900
|
+
const result = (await this.signedPost("getresourceapikey", body));
|
|
901
|
+
if (!result.ApiKey) {
|
|
902
|
+
throw new Error("Identity GetResourceApiKey: missing ApiKey");
|
|
903
|
+
}
|
|
904
|
+
return {
|
|
905
|
+
apiKey: result.ApiKey,
|
|
906
|
+
metadata: result.Metadata,
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
async getUserCredential(params) {
|
|
910
|
+
const body = {
|
|
911
|
+
CredentialId: params.credentialId,
|
|
912
|
+
IdentityToken: params.identityToken,
|
|
913
|
+
};
|
|
914
|
+
if (params.poolName)
|
|
915
|
+
body.PoolName = params.poolName;
|
|
916
|
+
const result = (await this.signedPost("GetUserCredential", body));
|
|
917
|
+
if (!result.CredentialId) {
|
|
918
|
+
throw new Error("Identity GetUserCredential: missing CredentialId");
|
|
919
|
+
}
|
|
920
|
+
if (!result.CredentialData) {
|
|
921
|
+
throw new Error("Identity GetUserCredential: missing CredentialId");
|
|
922
|
+
}
|
|
923
|
+
return {
|
|
924
|
+
credentialId: result.CredentialId,
|
|
925
|
+
credentialData: result.CredentialData,
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
async checkPermission(params) {
|
|
929
|
+
const body = {
|
|
930
|
+
NamespaceName: params.namespaceName,
|
|
931
|
+
Principal: params.principal,
|
|
932
|
+
Operation: params.action,
|
|
933
|
+
Resource: params.resource,
|
|
934
|
+
...(params.originalCallers?.length ? { OriginalCallers: params.originalCallers } : {}),
|
|
935
|
+
};
|
|
936
|
+
const result = (await this.signedPost("checkpermission", body));
|
|
937
|
+
return {
|
|
938
|
+
allowed: result.Allowed ?? false,
|
|
939
|
+
message: result.Message,
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
async listCredentialProviders(params) {
|
|
943
|
+
const body = {
|
|
944
|
+
PageNumber: params.PageNumber ?? 1,
|
|
945
|
+
PageSize: params.PageSize ?? 20,
|
|
946
|
+
};
|
|
947
|
+
const mergedFilter = params.Filter ? { ...params.Filter } : {};
|
|
948
|
+
if (params.PoolName) {
|
|
949
|
+
body.PoolName = params.PoolName;
|
|
950
|
+
mergedFilter.PoolName = params.PoolName;
|
|
951
|
+
}
|
|
952
|
+
if (Object.keys(mergedFilter).length > 0) {
|
|
953
|
+
body.Filter = mergedFilter;
|
|
954
|
+
}
|
|
955
|
+
const r = (await this.signedPost("listcredentialproviders", body));
|
|
956
|
+
const providers = r.CredentialProviders ?? r.Data ?? [];
|
|
957
|
+
return {
|
|
958
|
+
CredentialProviders: providers,
|
|
959
|
+
Data: providers,
|
|
960
|
+
TotalCount: r.TotalCount ?? providers.length,
|
|
961
|
+
PageNumber: r.PageNumber ?? 1,
|
|
962
|
+
PageSize: r.PageSize ?? 20,
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
async listRoleCredentialProviders(params) {
|
|
966
|
+
const body = {
|
|
967
|
+
PageNumber: params.PageNumber ?? 1,
|
|
968
|
+
PageSize: params.PageSize ?? 20,
|
|
969
|
+
};
|
|
970
|
+
const mergedFilter = params.Filter ? { ...params.Filter } : {};
|
|
971
|
+
if (params.PoolName) {
|
|
972
|
+
body.PoolName = params.PoolName;
|
|
973
|
+
mergedFilter.PoolName = params.PoolName;
|
|
974
|
+
}
|
|
975
|
+
if (Object.keys(mergedFilter).length > 0) {
|
|
976
|
+
body.Filter = mergedFilter;
|
|
977
|
+
}
|
|
978
|
+
const raw = (await this.signedPost("ListRoleCredentialProviders", body));
|
|
979
|
+
const r = raw.Result ?? raw;
|
|
980
|
+
let providers = r.RoleCredentialProviders ?? [];
|
|
981
|
+
// Client-side filter by user pool when specified
|
|
982
|
+
if (params.UserPoolId || params.UserPoolName) {
|
|
983
|
+
providers = providers.filter((p) => {
|
|
984
|
+
const pool = p.Config?.IdentityPool;
|
|
985
|
+
if (!pool)
|
|
986
|
+
return false;
|
|
987
|
+
if (params.UserPoolId && pool.UserPool?.PoolId === params.UserPoolId)
|
|
988
|
+
return true;
|
|
989
|
+
if (params.UserPoolName && pool.UserPool?.PoolName === params.UserPoolName)
|
|
990
|
+
return true;
|
|
991
|
+
return false;
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
return {
|
|
995
|
+
RoleCredentialProviders: providers,
|
|
996
|
+
TotalCount: params.UserPoolId || params.UserPoolName ? providers.length : (r.TotalCount ?? providers.length),
|
|
997
|
+
PageNumber: r.PageNumber ?? 1,
|
|
998
|
+
PageSize: r.PageSize ?? 20,
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
async getRoleCredentials(params) {
|
|
1002
|
+
const body = {
|
|
1003
|
+
IdentityToken: params.IdentityToken,
|
|
1004
|
+
ProviderName: params.ProviderName,
|
|
1005
|
+
};
|
|
1006
|
+
if (params.PoolName) {
|
|
1007
|
+
body.PoolName = params.PoolName;
|
|
1008
|
+
}
|
|
1009
|
+
if (params.RoleSessionName)
|
|
1010
|
+
body.RoleSessionName = params.RoleSessionName;
|
|
1011
|
+
if (params.RequestedRoleTrn)
|
|
1012
|
+
body.RequestedRoleTrn = params.RequestedRoleTrn;
|
|
1013
|
+
if (params.WithOIDC !== undefined)
|
|
1014
|
+
body.WithOIDC = params.WithOIDC;
|
|
1015
|
+
const r = (await this.signedPost("GetRoleCredentials", body));
|
|
1016
|
+
const creds = r.VolcEngResponse?.Credentials ?? r.Credentials;
|
|
1017
|
+
if (!creds?.AccessKeyId || !creds?.SecretAccessKey || !creds?.SessionToken) {
|
|
1018
|
+
throw new Error("Identity GetRoleCredentials: missing Credentials in response");
|
|
1019
|
+
}
|
|
1020
|
+
return {
|
|
1021
|
+
Credentials: {
|
|
1022
|
+
AccessKeyId: creds.AccessKeyId,
|
|
1023
|
+
SecretAccessKey: creds.SecretAccessKey,
|
|
1024
|
+
SessionToken: creds.SessionToken,
|
|
1025
|
+
CurrentTime: creds.CurrentTime,
|
|
1026
|
+
Expiration: creds.Expiration,
|
|
1027
|
+
},
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
async getUserPool(params) {
|
|
1031
|
+
const body = { UserPoolUid: params.userPoolUid };
|
|
1032
|
+
const r = (await this.signedPost("GetUserPool", body));
|
|
1033
|
+
return toUserPoolResult(r);
|
|
1034
|
+
}
|
|
1035
|
+
async listIdentityProviders(params) {
|
|
1036
|
+
const body = {
|
|
1037
|
+
UserPoolUID: params.userPoolUid,
|
|
1038
|
+
PageNumber: params.pageNumber ?? 1,
|
|
1039
|
+
PageSize: params.pageSize ?? 10,
|
|
1040
|
+
};
|
|
1041
|
+
if (params.filter) {
|
|
1042
|
+
const f = params.filter;
|
|
1043
|
+
body.Filter = {
|
|
1044
|
+
...(f.name && { Name: f.name }),
|
|
1045
|
+
...(f.connectionType && { ConnectionType: f.connectionType }),
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
const r = (await this.signedPost("ListIdentityProviders", body));
|
|
1049
|
+
const data = (r.Data ?? []).map((d) => ({
|
|
1050
|
+
uid: d.Uid ?? "",
|
|
1051
|
+
name: d.Name ?? "",
|
|
1052
|
+
connectionType: d.ConnectionType,
|
|
1053
|
+
provider: d.Provider,
|
|
1054
|
+
createTime: d.CreateTime,
|
|
1055
|
+
updateTime: d.UpdateTime,
|
|
1056
|
+
}));
|
|
1057
|
+
return {
|
|
1058
|
+
pageNumber: r.PageNumber ?? 1,
|
|
1059
|
+
pageSize: r.PageSize ?? 10,
|
|
1060
|
+
totalCount: r.TotalCount ?? 0,
|
|
1061
|
+
data,
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
async listUserPools(params) {
|
|
1065
|
+
const body = {};
|
|
1066
|
+
body.PageNumber = params.pageNumber ?? 1;
|
|
1067
|
+
body.PageSize = params.pageSize ?? 10;
|
|
1068
|
+
if (params.projectName)
|
|
1069
|
+
body.ProjectName = params.projectName;
|
|
1070
|
+
if (params.sortField)
|
|
1071
|
+
body.SortField = params.sortField;
|
|
1072
|
+
if (params.sortDirection)
|
|
1073
|
+
body.SortDirection = params.sortDirection;
|
|
1074
|
+
if (params.filter) {
|
|
1075
|
+
const f = params.filter;
|
|
1076
|
+
body.Filter = {
|
|
1077
|
+
...(f.name && { Name: f.name }),
|
|
1078
|
+
...(f.trn && { Trn: f.trn }),
|
|
1079
|
+
...(f.description && { Description: f.description }),
|
|
1080
|
+
...(f.uid && { Uid: f.uid }),
|
|
1081
|
+
...(f.tagFilters && {
|
|
1082
|
+
TagFilters: f.tagFilters.map((t) => ({
|
|
1083
|
+
Key: t.key,
|
|
1084
|
+
Values: t.values,
|
|
1085
|
+
})),
|
|
1086
|
+
}),
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
const r = (await this.signedPost("ListUserPools", body));
|
|
1090
|
+
const data = (r.Data ?? []).map((d) => {
|
|
1091
|
+
const tags = d.Tags;
|
|
1092
|
+
return {
|
|
1093
|
+
uid: d.Uid ?? "",
|
|
1094
|
+
name: d.Name ?? "",
|
|
1095
|
+
description: d.Description,
|
|
1096
|
+
domain: d.Domain ?? "",
|
|
1097
|
+
trn: d.Trn,
|
|
1098
|
+
createTime: d.CreateTime,
|
|
1099
|
+
updateTime: d.UpdateTime,
|
|
1100
|
+
tags: tags?.map((t) => ({ key: t.Key ?? "", value: t.Value ?? "" })),
|
|
1101
|
+
projectName: d.ProjectName,
|
|
1102
|
+
};
|
|
1103
|
+
});
|
|
1104
|
+
return {
|
|
1105
|
+
pageNumber: r.PageNumber ?? 1,
|
|
1106
|
+
pageSize: r.PageSize ?? 10,
|
|
1107
|
+
totalCount: r.TotalCount ?? 0,
|
|
1108
|
+
data,
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
async createUserPool(params) {
|
|
1112
|
+
const body = {
|
|
1113
|
+
Name: params.name,
|
|
1114
|
+
Description: params.description,
|
|
1115
|
+
ProjectName: params.projectName ?? "default",
|
|
1116
|
+
PasswordSignInEnabled: params.passwordSignInEnabled ?? true,
|
|
1117
|
+
EmailPasswordlessSignInEnabled: params.emailPasswordlessSignInEnabled ?? false,
|
|
1118
|
+
SelfSignUpEnabled: params.selfSignUpEnabled ?? true,
|
|
1119
|
+
SignInAttributes: params.signInAttributes ?? ["email"],
|
|
1120
|
+
RequiredSignUpAttributes: params.requiredSignUpAttributes ?? ["email"],
|
|
1121
|
+
SignUpAutoVerificationEnabled: params.signUpAutoVerificationEnabled ?? true,
|
|
1122
|
+
SmsPasswordlessSignInEnabled: params.smsPasswordlessSignInEnabled ?? false,
|
|
1123
|
+
SelfAccountRecoveryEnabled: params.selfAccountRecoveryEnabled ?? true,
|
|
1124
|
+
UnconfirmedUserSignInEnabled: params.unconfirmedUserSignInEnabled ?? true,
|
|
1125
|
+
SmsAnonymousSignUpEnabled: params.smsAnonymousSignUpEnabled ?? false,
|
|
1126
|
+
Tags: params.tags?.map((t) => ({ Key: t.key, Value: t.value })),
|
|
1127
|
+
Brand: params.brand ? { Name: params.brand.name, LogoUri: params.brand.logoUri } : undefined,
|
|
1128
|
+
};
|
|
1129
|
+
const r = (await this.signedPost("CreateUserPool", body));
|
|
1130
|
+
return toUserPoolResult(r);
|
|
1131
|
+
}
|
|
1132
|
+
async getUserPoolClient(params) {
|
|
1133
|
+
const body = {
|
|
1134
|
+
UserPoolUid: params.userPoolUid,
|
|
1135
|
+
ClientUid: params.clientUid,
|
|
1136
|
+
};
|
|
1137
|
+
const r = (await this.signedPost("GetUserPoolClient", body));
|
|
1138
|
+
return toUserPoolClientResult(r);
|
|
1139
|
+
}
|
|
1140
|
+
async listUserPoolClients(params) {
|
|
1141
|
+
const body = { UserPoolUid: params.userPoolUid };
|
|
1142
|
+
body.PageNumber = params.pageNumber ?? 1;
|
|
1143
|
+
body.PageSize = params.pageSize ?? 10;
|
|
1144
|
+
if (params.sortField)
|
|
1145
|
+
body.SortField = params.sortField;
|
|
1146
|
+
if (params.sortDirection)
|
|
1147
|
+
body.SortDirection = params.sortDirection;
|
|
1148
|
+
if (params.filter) {
|
|
1149
|
+
const f = params.filter;
|
|
1150
|
+
body.Filter = {
|
|
1151
|
+
...(f.name && { Name: f.name }),
|
|
1152
|
+
...(f.description && { Description: f.description }),
|
|
1153
|
+
...(f.clientTypes && { ClientTypes: f.clientTypes }),
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
const r = (await this.signedPost("ListUserPoolClients", body));
|
|
1157
|
+
const data = (r.Data ?? []).map((d) => ({
|
|
1158
|
+
uid: d.Uid ?? "",
|
|
1159
|
+
name: d.Name ?? "",
|
|
1160
|
+
description: d.Description,
|
|
1161
|
+
clientType: d.ClientType,
|
|
1162
|
+
createTime: d.CreateTime,
|
|
1163
|
+
updateTime: d.UpdateTime,
|
|
1164
|
+
}));
|
|
1165
|
+
return {
|
|
1166
|
+
pageNumber: r.PageNumber ?? 1,
|
|
1167
|
+
pageSize: r.PageSize ?? 10,
|
|
1168
|
+
totalCount: r.TotalCount ?? 0,
|
|
1169
|
+
data,
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
async createUserPoolClient(params) {
|
|
1173
|
+
const body = {
|
|
1174
|
+
UserPoolUid: params.userPoolUid,
|
|
1175
|
+
Name: params.name,
|
|
1176
|
+
Description: params.description,
|
|
1177
|
+
LogoUri: params.logoUri,
|
|
1178
|
+
ClientType: params.clientType ?? "WEB_APPLICATION",
|
|
1179
|
+
AllowedCallbackUrls: params.allowedCallbackUrls ?? [],
|
|
1180
|
+
AllowedLogoutUrls: params.allowedLogoutUrls,
|
|
1181
|
+
AllowedWebOrigins: params.allowedWebOrigins,
|
|
1182
|
+
AllowedCors: params.allowedCors,
|
|
1183
|
+
SkipConsentEnabled: params.skipConsentEnabled ?? true,
|
|
1184
|
+
};
|
|
1185
|
+
const r = (await this.signedPost("CreateUserPoolClient", body));
|
|
1186
|
+
return toUserPoolClientResult(r);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* Resolve OIDC config from UserPool and Client names (from_veidentity style).
|
|
1191
|
+
* If pool/client not found and autoCreate=true, creates them.
|
|
1192
|
+
* Also fetches the first identity provider from ListIdentityProviders and caches poolUid —
|
|
1193
|
+
* callers should cache the result to avoid repeated API calls.
|
|
1194
|
+
*/
|
|
1195
|
+
export async function resolveOIDCConfig(client, params) {
|
|
1196
|
+
const { userPoolName, userPoolUid, clientName, clientUid, redirectUri, scope = "openid profile email offline_access", autoCreate = true, clientType = "WEB_APPLICATION", userPoolConfig, pluginType, } = params;
|
|
1197
|
+
if (pluginType == "clawsentry") {
|
|
1198
|
+
const discoveryUrl = `${userPoolConfig?.userPoolEndpoint}/.well-known/openid-configuration`;
|
|
1199
|
+
return {
|
|
1200
|
+
discoveryUrl: discoveryUrl,
|
|
1201
|
+
clientId: userPoolConfig?.clientId ?? "",
|
|
1202
|
+
clientSecret: userPoolConfig?.clientSecret ?? "",
|
|
1203
|
+
scope: scope,
|
|
1204
|
+
callbackUrl: redirectUri,
|
|
1205
|
+
poolUid: userPoolConfig?.userPoolId ?? "",
|
|
1206
|
+
identityProvider: userPoolConfig?.identityProvider ?? "",
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
else {
|
|
1210
|
+
if (!userPoolName && !userPoolUid) {
|
|
1211
|
+
throw new Error("Either userPoolName or userPoolUid must be provided");
|
|
1212
|
+
}
|
|
1213
|
+
if (!clientName && !clientUid) {
|
|
1214
|
+
throw new Error("Either clientName or clientUid must be provided");
|
|
1215
|
+
}
|
|
1216
|
+
let poolUid;
|
|
1217
|
+
let poolDomain;
|
|
1218
|
+
if (userPoolUid) {
|
|
1219
|
+
const pool = await client.getUserPool({ userPoolUid });
|
|
1220
|
+
poolUid = pool.uid;
|
|
1221
|
+
poolDomain = pool.domain;
|
|
1222
|
+
}
|
|
1223
|
+
else {
|
|
1224
|
+
const list = await client.listUserPools({
|
|
1225
|
+
pageSize: 50,
|
|
1226
|
+
filter: { name: userPoolName },
|
|
1227
|
+
});
|
|
1228
|
+
const found = list.data.find((p) => p.name === userPoolName);
|
|
1229
|
+
if (found) {
|
|
1230
|
+
poolUid = found.uid;
|
|
1231
|
+
poolDomain = found.domain;
|
|
1232
|
+
}
|
|
1233
|
+
else if (autoCreate && userPoolName) {
|
|
1234
|
+
const created = await client.createUserPool({ name: userPoolName });
|
|
1235
|
+
poolUid = created.uid;
|
|
1236
|
+
poolDomain = created.domain;
|
|
1237
|
+
}
|
|
1238
|
+
else {
|
|
1239
|
+
throw new Error(`User pool '${userPoolName}' not found (autoCreate=false or only UID provided)`);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
let clientId;
|
|
1243
|
+
let clientSecret;
|
|
1244
|
+
if (clientUid) {
|
|
1245
|
+
const c = await client.getUserPoolClient({
|
|
1246
|
+
userPoolUid: poolUid,
|
|
1247
|
+
clientUid,
|
|
1248
|
+
});
|
|
1249
|
+
clientId = c.uid;
|
|
1250
|
+
clientSecret = c.clientSecret;
|
|
1251
|
+
}
|
|
1252
|
+
else {
|
|
1253
|
+
const list = await client.listUserPoolClients({
|
|
1254
|
+
userPoolUid: poolUid,
|
|
1255
|
+
pageSize: 50,
|
|
1256
|
+
filter: { name: clientName },
|
|
1257
|
+
});
|
|
1258
|
+
const found = list.data.find((c) => c.name === clientName);
|
|
1259
|
+
if (found) {
|
|
1260
|
+
const full = await client.getUserPoolClient({
|
|
1261
|
+
userPoolUid: poolUid,
|
|
1262
|
+
clientUid: found.uid,
|
|
1263
|
+
});
|
|
1264
|
+
clientId = full.uid;
|
|
1265
|
+
clientSecret = full.clientSecret;
|
|
1266
|
+
}
|
|
1267
|
+
else if (autoCreate && clientName) {
|
|
1268
|
+
const created = await client.createUserPoolClient({
|
|
1269
|
+
userPoolUid: poolUid,
|
|
1270
|
+
name: clientName,
|
|
1271
|
+
clientType,
|
|
1272
|
+
allowedCallbackUrls: [redirectUri],
|
|
1273
|
+
});
|
|
1274
|
+
clientId = created.uid;
|
|
1275
|
+
clientSecret = created.clientSecret;
|
|
1276
|
+
}
|
|
1277
|
+
else {
|
|
1278
|
+
throw new Error(`Client '${clientName}' not found (autoCreate=false or only UID provided)`);
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
const discoveryBase = poolDomain.startsWith("http") ? poolDomain : `https://${poolDomain}`;
|
|
1282
|
+
const discoveryUrl = discoveryBase.endsWith("/")
|
|
1283
|
+
? `${discoveryBase}.well-known/openid-configuration`
|
|
1284
|
+
: `${discoveryBase}/.well-known/openid-configuration`;
|
|
1285
|
+
// Fetch first identity provider at resolve time so callers don't need a
|
|
1286
|
+
// separate ListIdentityProviders call. Failure is non-fatal.
|
|
1287
|
+
let identityProvider;
|
|
1288
|
+
try {
|
|
1289
|
+
const providers = await client.listIdentityProviders({
|
|
1290
|
+
userPoolUid: poolUid,
|
|
1291
|
+
pageNumber: 1,
|
|
1292
|
+
pageSize: 1,
|
|
1293
|
+
});
|
|
1294
|
+
identityProvider = providers.data[0]?.name;
|
|
1295
|
+
}
|
|
1296
|
+
catch {
|
|
1297
|
+
// best-effort; identityProvider stays undefined
|
|
1298
|
+
}
|
|
1299
|
+
return {
|
|
1300
|
+
discoveryUrl,
|
|
1301
|
+
clientId,
|
|
1302
|
+
clientSecret,
|
|
1303
|
+
scope,
|
|
1304
|
+
callbackUrl: redirectUri,
|
|
1305
|
+
poolUid,
|
|
1306
|
+
identityProvider,
|
|
1307
|
+
};
|
|
672
1308
|
}
|
|
673
|
-
return {
|
|
674
|
-
discoveryUrl,
|
|
675
|
-
clientId,
|
|
676
|
-
clientSecret,
|
|
677
|
-
scope,
|
|
678
|
-
callbackUrl: redirectUri,
|
|
679
|
-
poolUid,
|
|
680
|
-
identityProvider,
|
|
681
|
-
};
|
|
682
1309
|
}
|