@vizamodo/aws-sts-core 0.3.47 → 0.3.49
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/federation/login.js +2 -31
- package/dist/sts/issue.d.ts +11 -2
- package/dist/sts/issue.js +150 -150
- package/package.json +2 -2
package/dist/federation/login.js
CHANGED
|
@@ -11,29 +11,17 @@ export async function buildFederationLoginUrl(input) {
|
|
|
11
11
|
throw new Error("[federation] invalid or expired credentials");
|
|
12
12
|
}
|
|
13
13
|
const tokenHash = await sha256Hex(input.sessionToken);
|
|
14
|
-
//
|
|
15
|
-
const cacheKey = `aws-signin
|
|
14
|
+
// Use region and tokenHash for cache key
|
|
15
|
+
const cacheKey = `aws-signin:${input.region}:${tokenHash}`;
|
|
16
16
|
const sessionJson = JSON.stringify(session);
|
|
17
17
|
const encoded = encodeURIComponent(sessionJson);
|
|
18
18
|
const SigninToken = await getCachedOrFetch(cacheKey, async () => {
|
|
19
|
-
console.debug("[signin] fetcher start", {
|
|
20
|
-
cacheKey,
|
|
21
|
-
expiration: input.expiration,
|
|
22
|
-
now: Date.now()
|
|
23
|
-
});
|
|
24
19
|
const tokenResp = await fetch(`https://signin.aws.amazon.com/federation?Action=getSigninToken&Session=${encoded}`);
|
|
25
|
-
console.debug("[signin] fetch response", {
|
|
26
|
-
ok: tokenResp.ok,
|
|
27
|
-
status: tokenResp.status
|
|
28
|
-
});
|
|
29
20
|
if (!tokenResp.ok) {
|
|
30
21
|
// best-effort: do not cache failures
|
|
31
22
|
throw new Error("[signin] failed to fetch SigninToken");
|
|
32
23
|
}
|
|
33
24
|
const json = await tokenResp.json();
|
|
34
|
-
console.debug("[signin] json parsed", {
|
|
35
|
-
hasToken: !!json?.SigninToken
|
|
36
|
-
});
|
|
37
25
|
const token = json?.SigninToken;
|
|
38
26
|
if (!token) {
|
|
39
27
|
// do not cache invalid response
|
|
@@ -43,29 +31,12 @@ export async function buildFederationLoginUrl(input) {
|
|
|
43
31
|
const SIGNIN_TOKEN_TTL_SEC = 15 * 60;
|
|
44
32
|
// derive effective TTL = min(tokenTTL, credentialTTL)
|
|
45
33
|
const credRemainingSec = Math.floor((Date.parse(input.expiration) - Date.now()) / 1000);
|
|
46
|
-
console.debug("[signin] TTL compute", {
|
|
47
|
-
credRemainingSec,
|
|
48
|
-
expiration: input.expiration,
|
|
49
|
-
now: Date.now()
|
|
50
|
-
});
|
|
51
34
|
const effectiveTtlSec = Math.min(SIGNIN_TOKEN_TTL_SEC, credRemainingSec);
|
|
52
|
-
console.debug("[signin] effective TTL", {
|
|
53
|
-
effectiveTtlSec,
|
|
54
|
-
SIGNIN_TOKEN_TTL_SEC
|
|
55
|
-
});
|
|
56
35
|
// if credential too close to expiry → skip caching
|
|
57
36
|
if (effectiveTtlSec <= 0) {
|
|
58
|
-
console.debug("[signin] skip cache (ttl<=0)", {
|
|
59
|
-
effectiveTtlSec
|
|
60
|
-
});
|
|
61
37
|
return token;
|
|
62
38
|
}
|
|
63
39
|
const expiryIso = new Date(Date.now() + effectiveTtlSec * 1000).toISOString();
|
|
64
|
-
console.debug("[signin] wrapResult", {
|
|
65
|
-
effectiveTtlSec,
|
|
66
|
-
expiryIso,
|
|
67
|
-
now: Date.now()
|
|
68
|
-
});
|
|
69
40
|
// restore primitive storage (cache layer now handles primitive safely)
|
|
70
41
|
return wrapResult(token, expiryIso);
|
|
71
42
|
}, { ttlSec: 60, ...(input.forceRefresh !== undefined ? { forceRefresh: input.forceRefresh } : {}) } // allow caller-controlled retry
|
package/dist/sts/issue.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AwsCredentialResult } from "../types";
|
|
2
|
-
export
|
|
2
|
+
export interface IssueAwsCredentialsInput {
|
|
3
3
|
roleArn: string;
|
|
4
4
|
profileArn: string;
|
|
5
5
|
trustAnchorArn: string;
|
|
@@ -8,4 +8,13 @@ export declare function issueAwsCredentials(input: {
|
|
|
8
8
|
privateKeyPkcs8Base64: string;
|
|
9
9
|
profile: string;
|
|
10
10
|
forceRefresh?: boolean;
|
|
11
|
-
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Reset isolate-level signing material and cert serial caches.
|
|
14
|
+
* ONLY for use in tests — forces re-import of signing key on next call.
|
|
15
|
+
*
|
|
16
|
+
* L1 cache is bypassed in tests via `forceRefresh: true` on each call —
|
|
17
|
+
* there is no need to expose a cache-clear API from the core library.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resetIsolateCache(): void;
|
|
20
|
+
export declare function issueAwsCredentials(input: IssueAwsCredentialsInput): Promise<AwsCredentialResult>;
|
package/dist/sts/issue.js
CHANGED
|
@@ -5,10 +5,14 @@ import { buildStringToSign } from "../sigv4/string-to-sign";
|
|
|
5
5
|
import { signStringToSign } from "./signer";
|
|
6
6
|
import { InternalError } from "./errors";
|
|
7
7
|
import { sha256Hex } from "../crypto/sha256";
|
|
8
|
-
//
|
|
8
|
+
// ── Constants ──────────────────────────────────────────────────────────────
|
|
9
9
|
const ALGORITHM = "AWS4-X509-ECDSA-SHA256";
|
|
10
10
|
const SERVICE = "rolesanywhere";
|
|
11
11
|
const PATH = "/sessions";
|
|
12
|
+
/**
|
|
13
|
+
* Per-profile AWS session duration (seconds).
|
|
14
|
+
* Clamped to [MIN_SESSION_TTL, MAX_SESSION_TTL] at resolution time.
|
|
15
|
+
*/
|
|
12
16
|
const PROFILE_TTL = {
|
|
13
17
|
Runtime: 45 * 60,
|
|
14
18
|
ConsoleReadOnly: 12 * 60 * 60,
|
|
@@ -17,178 +21,176 @@ const PROFILE_TTL = {
|
|
|
17
21
|
BillingAdmin: 3 * 60 * 60,
|
|
18
22
|
BillingAccountant: 3 * 60 * 60,
|
|
19
23
|
};
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
//
|
|
24
|
-
// Single Promise
|
|
24
|
+
const DEFAULT_SESSION_TTL = 2 * 60 * 60;
|
|
25
|
+
const MIN_SESSION_TTL = 45 * 60; // 45 min
|
|
26
|
+
const MAX_SESSION_TTL = 12 * 60 * 60; // 12 h
|
|
27
|
+
// ── Isolate-level signing-material cache ───────────────────────────────────
|
|
28
|
+
// Single Promise slot prevents concurrent cold-start import races.
|
|
25
29
|
let signingKeyPromise = null;
|
|
26
30
|
let cachedSigningKey = null;
|
|
27
31
|
let cachedCertBase64 = null;
|
|
28
32
|
let cachedPrivateKeyBase64 = null;
|
|
29
|
-
//
|
|
33
|
+
// ── Isolate-level cert-serial cache ───────────────────────────────────────
|
|
34
|
+
// DER walk is CPU-bound; the cert rarely rotates within an isolate lifetime.
|
|
30
35
|
let cachedCertSerialDec = null;
|
|
31
36
|
let cachedCertSerialSource = null;
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
// ── Test utilities ─────────────────────────────────────────────────────────
|
|
38
|
+
/**
|
|
39
|
+
* Reset isolate-level signing material and cert serial caches.
|
|
40
|
+
* ONLY for use in tests — forces re-import of signing key on next call.
|
|
41
|
+
*
|
|
42
|
+
* L1 cache is bypassed in tests via `forceRefresh: true` on each call —
|
|
43
|
+
* there is no need to expose a cache-clear API from the core library.
|
|
44
|
+
*/
|
|
45
|
+
export function resetIsolateCache() {
|
|
46
|
+
signingKeyPromise = null;
|
|
47
|
+
cachedSigningKey = null;
|
|
48
|
+
cachedCertBase64 = null;
|
|
49
|
+
cachedPrivateKeyBase64 = null;
|
|
50
|
+
cachedCertSerialDec = null;
|
|
51
|
+
cachedCertSerialSource = null;
|
|
52
|
+
}
|
|
53
|
+
// ── Signing material ───────────────────────────────────────────────────────
|
|
54
|
+
async function getSigningMaterial(certBase64, privateKeyPkcs8Base64) {
|
|
55
|
+
// Fast path: same material already imported in this isolate.
|
|
37
56
|
if (cachedSigningKey &&
|
|
38
|
-
cachedCertBase64 ===
|
|
39
|
-
cachedPrivateKeyBase64 ===
|
|
40
|
-
return
|
|
41
|
-
}
|
|
42
|
-
// Material rotated — discard the stale resolved key so we re-import.
|
|
43
|
-
if (cachedSigningKey) {
|
|
44
|
-
signingKeyPromise = null;
|
|
45
|
-
cachedSigningKey = null;
|
|
57
|
+
cachedCertBase64 === certBase64 &&
|
|
58
|
+
cachedPrivateKeyBase64 === privateKeyPkcs8Base64) {
|
|
59
|
+
return cachedSigningKey;
|
|
46
60
|
}
|
|
61
|
+
// Material rotated — discard stale state and re-import.
|
|
62
|
+
signingKeyPromise = null;
|
|
63
|
+
cachedSigningKey = null;
|
|
47
64
|
if (!signingKeyPromise) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
catch {
|
|
65
|
+
signingKeyPromise = crypto.subtle
|
|
66
|
+
.importKey("pkcs8", base64ToBytes(privateKeyPkcs8Base64), { name: "ECDSA", namedCurve: "P-256" }, false, ["sign"])
|
|
67
|
+
.catch(() => {
|
|
68
|
+
signingKeyPromise = null; // allow retry
|
|
53
69
|
throw new InternalError("invalid_signing_material");
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
try {
|
|
57
|
-
cachedSigningKey = await signingKeyPromise;
|
|
58
|
-
cachedCertBase64 = input.certBase64;
|
|
59
|
-
cachedPrivateKeyBase64 = input.privateKeyPkcs8Base64;
|
|
60
|
-
return { signingKey: cachedSigningKey, certBase64: cachedCertBase64 };
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
signingKeyPromise = null; // allow retry on next call
|
|
64
|
-
throw new InternalError("invalid_signing_material");
|
|
70
|
+
});
|
|
65
71
|
}
|
|
72
|
+
cachedSigningKey = await signingKeyPromise;
|
|
73
|
+
cachedCertBase64 = certBase64;
|
|
74
|
+
cachedPrivateKeyBase64 = privateKeyPkcs8Base64;
|
|
75
|
+
return cachedSigningKey;
|
|
66
76
|
}
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const ttl = PROFILE_TTL[profile] ?? DEFAULT_TTL;
|
|
72
|
-
return Math.min(Math.max(ttl, MIN_PROFILE_TTL), MAX_PROFILE_TTL);
|
|
77
|
+
// ── Session TTL ────────────────────────────────────────────────────────────
|
|
78
|
+
function resolveSessionTtl(profile) {
|
|
79
|
+
const ttl = PROFILE_TTL[profile] ?? DEFAULT_SESSION_TTL;
|
|
80
|
+
return Math.min(Math.max(ttl, MIN_SESSION_TTL), MAX_SESSION_TTL);
|
|
73
81
|
}
|
|
74
|
-
//
|
|
75
|
-
// Main export
|
|
76
|
-
// ---------------------------------------------------------------------------
|
|
82
|
+
// ── Main export ────────────────────────────────────────────────────────────
|
|
77
83
|
export async function issueAwsCredentials(input) {
|
|
78
84
|
const { roleArn, profileArn, trustAnchorArn, region, certBase64, privateKeyPkcs8Base64, profile, forceRefresh, } = input;
|
|
79
|
-
const sessionTtl =
|
|
85
|
+
const sessionTtl = resolveSessionTtl(profile);
|
|
80
86
|
const normalizedCert = normalizeCert(certBase64);
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
if (cachedCertSerialDec && cachedCertSerialSource === normalizedCert) {
|
|
84
|
-
certSerialDec = cachedCertSerialDec;
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
certSerialDec = parseCertSerialDec(normalizedCert); // throws InternalError on bad DER
|
|
88
|
-
cachedCertSerialDec = certSerialDec;
|
|
89
|
-
cachedCertSerialSource = normalizedCert;
|
|
90
|
-
}
|
|
87
|
+
// Cert serial — use isolate cache to avoid redundant DER walks.
|
|
88
|
+
const certSerialDec = getCertSerialDec(normalizedCert);
|
|
91
89
|
const cacheKey = `${region}|${roleArn}|${profileArn}|${trustAnchorArn}|${certSerialDec}`;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
90
|
+
return getCachedOrFetch(cacheKey, () => fetchCredentials({
|
|
91
|
+
roleArn, profileArn, trustAnchorArn,
|
|
92
|
+
region, normalizedCert, privateKeyPkcs8Base64,
|
|
93
|
+
sessionTtl,
|
|
94
|
+
}), { ttlSec: 60, forceRefresh });
|
|
95
|
+
}
|
|
96
|
+
async function fetchCredentials(input) {
|
|
97
|
+
const { roleArn, profileArn, trustAnchorArn, region, normalizedCert, privateKeyPkcs8Base64, sessionTtl, } = input;
|
|
98
|
+
const signingKey = await getSigningMaterial(normalizedCert, privateKeyPkcs8Base64);
|
|
99
|
+
const host = `${SERVICE}.${region}.amazonaws.com`;
|
|
100
|
+
const iso = new Date().toISOString();
|
|
101
|
+
const amzDate = isoToAmzDate(iso);
|
|
102
|
+
const dateStamp = iso.slice(0, 4) + iso.slice(5, 7) + iso.slice(8, 10);
|
|
103
|
+
const body = JSON.stringify({ trustAnchorArn, profileArn, roleArn, durationSeconds: sessionTtl });
|
|
104
|
+
const payloadHash = await sha256Hex(body);
|
|
105
|
+
const baseHeaders = {
|
|
106
|
+
"content-type": "application/json",
|
|
107
|
+
"host": host,
|
|
108
|
+
"x-amz-date": amzDate,
|
|
109
|
+
"x-amz-x509": normalizedCert,
|
|
110
|
+
};
|
|
111
|
+
const { canonicalHeaders, signedHeaders } = canonicalizeHeaders(baseHeaders);
|
|
112
|
+
const credentialScope = `${dateStamp}/${region}/${SERVICE}/aws4_request`;
|
|
113
|
+
const canonicalRequest = buildCanonicalRequest({
|
|
114
|
+
method: "POST",
|
|
115
|
+
canonicalUri: PATH,
|
|
116
|
+
query: "",
|
|
117
|
+
canonicalHeaders,
|
|
118
|
+
signedHeaders,
|
|
119
|
+
payloadHash,
|
|
120
|
+
});
|
|
121
|
+
const canonicalRequestHash = await sha256Hex(canonicalRequest);
|
|
122
|
+
const stringToSign = buildStringToSign({
|
|
123
|
+
algorithm: ALGORITHM,
|
|
124
|
+
amzDate,
|
|
125
|
+
credentialScope,
|
|
126
|
+
canonicalRequestHash,
|
|
127
|
+
});
|
|
128
|
+
const signatureHex = await signStringToSign(stringToSign, signingKey);
|
|
129
|
+
const certSerialDec = getCertSerialDec(normalizedCert);
|
|
130
|
+
const finalHeaders = new Headers({
|
|
131
|
+
"Content-Type": "application/json",
|
|
132
|
+
"X-Amz-Date": amzDate,
|
|
133
|
+
"X-Amz-X509": normalizedCert,
|
|
134
|
+
"Authorization": `${ALGORITHM} Credential=${certSerialDec}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signatureHex}`,
|
|
135
|
+
});
|
|
136
|
+
let res;
|
|
137
|
+
try {
|
|
138
|
+
res = await fetch(`https://${host}${PATH}`, {
|
|
137
139
|
method: "POST",
|
|
138
140
|
headers: finalHeaders,
|
|
139
141
|
body,
|
|
140
142
|
});
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
console.debug("[issueAwsCredentials] fallback return (no TTL)", {
|
|
174
|
-
accessKeyId: value.accessKeyId
|
|
175
|
-
});
|
|
176
|
-
return value;
|
|
177
|
-
}, {
|
|
178
|
-
ttlSec: 60,
|
|
179
|
-
...(forceRefresh !== undefined ? { forceRefresh } : {})
|
|
180
|
-
});
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
console.warn("[aws-unreachable]", { region, err });
|
|
146
|
+
throw new InternalError("aws_unreachable");
|
|
147
|
+
}
|
|
148
|
+
if (!res.ok) {
|
|
149
|
+
console.warn("[aws-rejected]", { status: res.status, region });
|
|
150
|
+
throw new InternalError("aws_rejected");
|
|
151
|
+
}
|
|
152
|
+
const json = await res.json();
|
|
153
|
+
const creds = json?.credentialSet?.[0]?.credentials;
|
|
154
|
+
if (!creds?.accessKeyId || !creds?.secretAccessKey || !creds?.sessionToken) {
|
|
155
|
+
console.warn("[issueAwsCredentials] malformed AWS credential response");
|
|
156
|
+
throw new InternalError("aws_malformed_credentials");
|
|
157
|
+
}
|
|
158
|
+
const value = {
|
|
159
|
+
accessKeyId: creds.accessKeyId,
|
|
160
|
+
secretAccessKey: creds.secretAccessKey,
|
|
161
|
+
sessionToken: creds.sessionToken,
|
|
162
|
+
expiration: creds.expiration,
|
|
163
|
+
};
|
|
164
|
+
// Cache for 1/3 of the credential lifetime so it's refreshed well before expiry.
|
|
165
|
+
// deriveEdgeTtlSec in cache.ts will further subtract EDGE_BUFFER_SEC (5 min).
|
|
166
|
+
const expiresAtMs = Date.parse(creds.expiration);
|
|
167
|
+
const receivedAt = Date.now();
|
|
168
|
+
const credLifetimeSec = Math.floor((expiresAtMs - receivedAt) / 1000);
|
|
169
|
+
if (Number.isFinite(expiresAtMs) && credLifetimeSec > 0) {
|
|
170
|
+
const cacheTtlSec = Math.floor(credLifetimeSec / 3);
|
|
171
|
+
const cacheExpiry = new Date(receivedAt + cacheTtlSec * 1000).toISOString();
|
|
172
|
+
return wrapResult(value, cacheExpiry);
|
|
173
|
+
}
|
|
174
|
+
return value;
|
|
181
175
|
}
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
// ---------------------------------------------------------------------------
|
|
185
|
-
/** Strip whitespace and reject PEM-wrapped input. */
|
|
176
|
+
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
177
|
+
/** Strip whitespace; reject PEM-wrapped input. */
|
|
186
178
|
function normalizeCert(raw) {
|
|
187
179
|
if (raw.includes("BEGIN CERTIFICATE")) {
|
|
188
180
|
throw new InternalError("pem_not_allowed");
|
|
189
181
|
}
|
|
190
182
|
return raw.replace(/\s+/g, "");
|
|
191
183
|
}
|
|
184
|
+
/** Return cert serial as decimal string, using isolate-level cache. */
|
|
185
|
+
function getCertSerialDec(normalizedCert) {
|
|
186
|
+
if (cachedCertSerialDec && cachedCertSerialSource === normalizedCert) {
|
|
187
|
+
return cachedCertSerialDec;
|
|
188
|
+
}
|
|
189
|
+
const serial = parseCertSerialDec(normalizedCert);
|
|
190
|
+
cachedCertSerialDec = serial;
|
|
191
|
+
cachedCertSerialSource = normalizedCert;
|
|
192
|
+
return serial;
|
|
193
|
+
}
|
|
192
194
|
/**
|
|
193
195
|
* Minimal DER walk to extract the certificate serial number as a decimal string.
|
|
194
196
|
* Throws InternalError("invalid_cert_der") on any parse failure.
|
|
@@ -220,8 +222,7 @@ function parseCertSerialDec(normalizedCertBase64) {
|
|
|
220
222
|
// Skip optional [0] EXPLICIT version field.
|
|
221
223
|
if (der[offset] === 0xa0) {
|
|
222
224
|
offset++;
|
|
223
|
-
|
|
224
|
-
offset += vLen;
|
|
225
|
+
offset += readLen();
|
|
225
226
|
}
|
|
226
227
|
if (der[offset++] !== 0x02)
|
|
227
228
|
throw new Error("bad serial tag");
|
|
@@ -233,7 +234,6 @@ function parseCertSerialDec(normalizedCertBase64) {
|
|
|
233
234
|
if (serial.length > 1 && serial[0] === 0x00) {
|
|
234
235
|
serial = serial.slice(1);
|
|
235
236
|
}
|
|
236
|
-
// Accumulate directly to BigInt — avoids intermediate hex string allocation.
|
|
237
237
|
let serialBig = 0n;
|
|
238
238
|
for (let i = 0; i < serial.length; i++) {
|
|
239
239
|
serialBig = (serialBig << 8n) | BigInt(serial[i]);
|
|
@@ -246,7 +246,7 @@ function parseCertSerialDec(normalizedCertBase64) {
|
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
/**
|
|
249
|
-
* Convert
|
|
249
|
+
* Convert ISO-8601 to compact AMZ date-time format.
|
|
250
250
|
* e.g. "2026-03-07T12:00:00.000Z" → "20260307T120000Z"
|
|
251
251
|
*/
|
|
252
252
|
function isoToAmzDate(iso) {
|
|
@@ -259,7 +259,7 @@ function isoToAmzDate(iso) {
|
|
|
259
259
|
iso.slice(17, 19) +
|
|
260
260
|
"Z");
|
|
261
261
|
}
|
|
262
|
-
/**
|
|
262
|
+
/** base64 → Uint8Array via V8's vectorized atob path. */
|
|
263
263
|
function base64ToBytes(base64) {
|
|
264
264
|
const binary = atob(base64);
|
|
265
265
|
const bytes = new Uint8Array(binary.length);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vizamodo/aws-sts-core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.49",
|
|
4
4
|
"description": "Pure AWS STS + SigV4 (X509 Roles Anywhere) core logic",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -28,6 +28,6 @@
|
|
|
28
28
|
"vitest": "^4.1.0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@vizamodo/edge-cache-core": "^0.3.
|
|
31
|
+
"@vizamodo/edge-cache-core": "^0.3.40"
|
|
32
32
|
}
|
|
33
33
|
}
|