@go-to-k/cdkd 0.118.0 → 0.119.0

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/cli.js CHANGED
@@ -40448,14 +40448,23 @@ function resolveRestV1Authorizer(authorizerLogicalId, template, stackName, decla
40448
40448
  if (type === "COGNITO_USER_POOLS") {
40449
40449
  const arns = props["ProviderARNs"];
40450
40450
  if (!Array.isArray(arns) || arns.length === 0) throw new RouteDiscoveryError(`${stackName}/${authorizerLogicalId}: COGNITO_USER_POOLS authorizer is missing ProviderARNs.`);
40451
- const arn = pickStringFromArn(arns[0], `${stackName}/${authorizerLogicalId}.ProviderARNs[0]`);
40452
- const parsed = parseCognitoUserPoolArn(arn, `${stackName}/${authorizerLogicalId}`);
40451
+ const pools = arns.map((entry, idx) => {
40452
+ const arn = pickStringFromArn(entry, `${stackName}/${authorizerLogicalId}.ProviderARNs[${idx}]`);
40453
+ const parsed = parseCognitoUserPoolArn(arn, `${stackName}/${authorizerLogicalId}.ProviderARNs[${idx}]`);
40454
+ return {
40455
+ userPoolArn: arn,
40456
+ region: parsed.region,
40457
+ userPoolId: parsed.userPoolId
40458
+ };
40459
+ });
40460
+ const first = pools[0];
40453
40461
  return {
40454
40462
  kind: "cognito",
40455
40463
  logicalId: authorizerLogicalId,
40456
- userPoolArn: arn,
40457
- region: parsed.region,
40458
- userPoolId: parsed.userPoolId,
40464
+ pools,
40465
+ userPoolArn: first.userPoolArn,
40466
+ region: first.region,
40467
+ userPoolId: first.userPoolId,
40459
40468
  declaredAt
40460
40469
  };
40461
40470
  }
@@ -40677,7 +40686,9 @@ function parseCognitoIssuer(issuer) {
40677
40686
  /**
40678
40687
  * Pull a string out of a {Ref} / literal entry under `ProviderARNs`.
40679
40688
  * CDK's CognitoUserPoolsAuthorizer emits a literal array of `Fn::GetAtt:
40680
- * [<UserPool>, 'Arn']` entries — we accept both.
40689
+ * [<UserPool>, 'Arn']` entries — we accept both. The `location` argument
40690
+ * carries the full `<stack>/<authorizer>.ProviderARNs[<idx>]` path so the
40691
+ * error names the offending entry exactly.
40681
40692
  */
40682
40693
  function pickStringFromArn(value, location) {
40683
40694
  if (typeof value === "string") return value;
@@ -40685,10 +40696,10 @@ function pickStringFromArn(value, location) {
40685
40696
  const obj = value;
40686
40697
  if ("Fn::GetAtt" in obj) {
40687
40698
  const arg = obj["Fn::GetAtt"];
40688
- if (Array.isArray(arg) && arg.length === 2 && typeof arg[0] === "string" && arg[1] === "Arn") throw new RouteDiscoveryError(`${location}: ProviderARNs[0] uses Fn::GetAtt against logical ID '${arg[0]}'. cdkd local start-api needs the literal ARN string to derive the JWKS URL — set the user pool ARN explicitly via 'authorizer.providerArns' on the CDK construct, or upgrade to JWT (HTTP v2) which encodes the pool in the Issuer URL.`);
40699
+ if (Array.isArray(arg) && arg.length === 2 && typeof arg[0] === "string" && arg[1] === "Arn") throw new RouteDiscoveryError(`${location}: uses Fn::GetAtt against logical ID '${arg[0]}'. cdkd local start-api needs the literal ARN string to derive the JWKS URL — set the user pool ARN explicitly via 'authorizer.providerArns' on the CDK construct, or upgrade to JWT (HTTP v2) which encodes the pool in the Issuer URL.`);
40689
40700
  }
40690
40701
  }
40691
- throw new RouteDiscoveryError(`${location}: ProviderARNs[0] must be a literal string (got ${shortJson(value)}).`);
40702
+ throw new RouteDiscoveryError(`${location}: must be a literal string (got ${shortJson(value)}).`);
40692
40703
  }
40693
40704
  /**
40694
40705
  * Parse and clamp a TTL value to `[0, max]` with a default fallback.
@@ -41206,21 +41217,36 @@ function buildJwksUrlFromIssuer(issuer) {
41206
41217
  return `${issuer.replace(/\/+$/, "")}/.well-known/jwks.json`;
41207
41218
  }
41208
41219
  /**
41209
- * Verify a Bearer JWT against the Cognito user pool referenced by the
41210
- * authorizer. Returns a {@link CachedAuthorizerResult} the http-server
41211
- * can both cache (briefly — JWT exp itself is the cache deadline) and
41212
- * propagate into the route event.
41220
+ * Build the expected `iss` claim URL for a Cognito user pool. Matches the
41221
+ * issuer Cognito embeds in every minted JWT.
41222
+ */
41223
+ function buildCognitoIssuer(region, userPoolId) {
41224
+ return `https://cognito-idp.${region}.amazonaws.com/${userPoolId}`;
41225
+ }
41226
+ /**
41227
+ * Verify a Bearer JWT against the Cognito user pool(s) referenced by the
41228
+ * authorizer. With a multi-pool authorizer (`ProviderARNs[]` of length
41229
+ * 1+) the request-time pool selection is driven by the JWT's unverified
41230
+ * `iss` claim — only the matching pool's JWKS is used for signature
41231
+ * verification, so a token issued by pool A cannot be verified against
41232
+ * pool B's keys. Issuer mismatch against EVERY configured pool rejects
41233
+ * with 401.
41213
41234
  *
41214
41235
  * Returns `{ allow: false }` on:
41215
41236
  * - missing / malformed Authorization header (caller surfaces 401);
41216
41237
  * - signature verification failure;
41217
41238
  * - expired token (`exp` in the past);
41218
- * - issuer mismatch (token's `iss` doesn't match the pool's URL);
41239
+ * - issuer mismatch (token's `iss` doesn't match any configured pool);
41219
41240
  * - audience mismatch (token's `aud` not in the configured allowlist).
41220
41241
  *
41221
41242
  * Returns `{ allow: true, principalId, context }` on:
41222
- * - successful verification;
41223
- * - JWKS-unreachable pass-through mode (with a warn line on first hit).
41243
+ * - successful verification against the matching pool;
41244
+ * - JWKS-unreachable pass-through mode for the matching pool (with a
41245
+ * warn line on first hit; per-pool TTL handled by the cache).
41246
+ *
41247
+ * Backward compat: a single-element `ProviderARNs[]` (the historical
41248
+ * single-pool case) behaves identically to pre-PR — `pools[0]` is the
41249
+ * only candidate and the `iss` check matches it.
41224
41250
  */
41225
41251
  async function verifyCognitoJwt(authorizer, authorizationHeader, jwksCache, opts = {}) {
41226
41252
  const now = opts.now ?? (() => Date.now());
@@ -41230,7 +41256,33 @@ async function verifyCognitoJwt(authorizer, authorizationHeader, jwksCache, opts
41230
41256
  identityHash: void 0,
41231
41257
  ttlSeconds: 0
41232
41258
  };
41233
- return verifyAndShape(token, buildCognitoJwksUrl(authorizer.region, authorizer.userPoolId), `https://cognito-idp.${authorizer.region}.amazonaws.com/${authorizer.userPoolId}`, void 0, jwksCache, opts.warned, now);
41259
+ const pools = authorizer.pools && authorizer.pools.length > 0 ? authorizer.pools : [{
41260
+ userPoolArn: authorizer.userPoolArn,
41261
+ region: authorizer.region,
41262
+ userPoolId: authorizer.userPoolId
41263
+ }];
41264
+ const parsed = parseJwt(token);
41265
+ const identityHash = buildIdentityHash([token]);
41266
+ let selectedPool = pools[0];
41267
+ let issMatched = false;
41268
+ if (parsed && typeof parsed.payload["iss"] === "string") {
41269
+ const tokenIss = parsed.payload["iss"].replace(/\/+$/, "");
41270
+ for (const pool of pools) if (buildCognitoIssuer(pool.region, pool.userPoolId) === tokenIss) {
41271
+ selectedPool = pool;
41272
+ issMatched = true;
41273
+ break;
41274
+ }
41275
+ if (!issMatched) return {
41276
+ allow: false,
41277
+ identityHash,
41278
+ ttlSeconds: 0
41279
+ };
41280
+ } else if (pools.length > 1) return {
41281
+ allow: false,
41282
+ identityHash,
41283
+ ttlSeconds: 0
41284
+ };
41285
+ return verifyAndShape(token, buildCognitoJwksUrl(selectedPool.region, selectedPool.userPoolId), buildCognitoIssuer(selectedPool.region, selectedPool.userPoolId), void 0, jwksCache, opts.warned, now);
41234
41286
  }
41235
41287
  /**
41236
41288
  * Verify a Bearer JWT against an HTTP v2 JWT authorizer's `JwtConfiguration`.
@@ -42712,7 +42764,7 @@ async function prewarmJwks(routesWithAuth, jwksCache) {
42712
42764
  for (const entry of routesWithAuth) {
42713
42765
  const auth = entry.authorizer;
42714
42766
  if (!auth) continue;
42715
- if (auth.kind === "cognito") urls.add(buildCognitoJwksUrl(auth.region, auth.userPoolId));
42767
+ if (auth.kind === "cognito") for (const pool of auth.pools) urls.add(buildCognitoJwksUrl(pool.region, pool.userPoolId));
42716
42768
  else if (auth.kind === "jwt") {
42717
42769
  const url = auth.region && auth.userPoolId ? buildCognitoJwksUrl(auth.region, auth.userPoolId) : buildJwksUrlFromIssuer(auth.issuer);
42718
42770
  urls.add(url);
@@ -46172,7 +46224,7 @@ function reorderArgs(argv) {
46172
46224
  */
46173
46225
  async function main() {
46174
46226
  const program = new Command();
46175
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.118.0");
46227
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.119.0");
46176
46228
  program.addCommand(createBootstrapCommand());
46177
46229
  program.addCommand(createSynthCommand());
46178
46230
  program.addCommand(createListCommand());