From 6c02ee84d1697c9aed00c7628f073f6c6bc6f78a Mon Sep 17 00:00:00 2001 From: Konstantin Borisov Date: Fri, 20 Mar 2026 10:56:39 -0500 Subject: [PATCH 1/6] New staging account updates --- tests/api/specs/hasura/app-verification.spec.ts | 2 +- tests/api/specs/hasura/get-image.spec.ts | 2 +- tests/api/specs/hasura/get-unverified-images.spec.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/api/specs/hasura/app-verification.spec.ts b/tests/api/specs/hasura/app-verification.spec.ts index 5ed5fb4ee..5a330fe49 100644 --- a/tests/api/specs/hasura/app-verification.spec.ts +++ b/tests/api/specs/hasura/app-verification.spec.ts @@ -20,7 +20,7 @@ describe.skip("Hasura API - App Verification", () => { beforeAll(async () => { // Set required AWS environment variables for tests - process.env.ASSETS_S3_BUCKET_NAME = "world-id-assets-staging"; + process.env.ASSETS_S3_BUCKET_NAME = process.env.ASSETS_S3_BUCKET_NAME || "world-id-assets-staging"; process.env.ASSETS_S3_REGION = "us-east-1"; // Create test app and metadata diff --git a/tests/api/specs/hasura/get-image.spec.ts b/tests/api/specs/hasura/get-image.spec.ts index 4e2c9e2cf..5b16a4c5b 100644 --- a/tests/api/specs/hasura/get-image.spec.ts +++ b/tests/api/specs/hasura/get-image.spec.ts @@ -36,7 +36,7 @@ describe("Hasura API - Get Image", () => { beforeAll(async () => { // Set required AWS environment variables for tests - process.env.ASSETS_S3_BUCKET_NAME = "world-id-assets-staging"; + process.env.ASSETS_S3_BUCKET_NAME = process.env.ASSETS_S3_BUCKET_NAME || "world-id-assets-staging"; process.env.ASSETS_S3_REGION = "us-east-1"; // Create test team and user diff --git a/tests/api/specs/hasura/get-unverified-images.spec.ts b/tests/api/specs/hasura/get-unverified-images.spec.ts index 0a33a3550..971e51254 100644 --- a/tests/api/specs/hasura/get-unverified-images.spec.ts +++ b/tests/api/specs/hasura/get-unverified-images.spec.ts @@ -32,7 +32,7 @@ describe("Hasura API - Get Unverified Images", () => { beforeAll(async () => { // Set required AWS environment variables for tests - process.env.ASSETS_S3_BUCKET_NAME = "world-id-assets-staging"; + process.env.ASSETS_S3_BUCKET_NAME = process.env.ASSETS_S3_BUCKET_NAME || "world-id-assets-staging"; process.env.ASSETS_S3_REGION = "us-east-1"; // Create test team and user From 768cd9619bc16391aafc4341ea785fcf8e5cdb82 Mon Sep 17 00:00:00 2001 From: Konstantin Borisov Date: Fri, 20 Mar 2026 10:56:55 -0500 Subject: [PATCH 2/6] Skip the host check on staging --- web/lib/utils.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/web/lib/utils.ts b/web/lib/utils.ts index 0914d62aa..61090f9c5 100644 --- a/web/lib/utils.ts +++ b/web/lib/utils.ts @@ -259,8 +259,11 @@ export const isValidHostName = (request: Request) => { return false; } - // Skip check for development - if (process.env.NODE_ENV === "development") { + // Skip check for development and staging (staging may not have custom domains) + if ( + process.env.NODE_ENV === "development" || + process.env.NEXT_PUBLIC_APP_ENV === "staging" + ) { return true; } const cdnHost = process.env.NEXT_PUBLIC_IMAGES_CDN_URL; From a6e8c7b0bd176c3e3cb87b65bb332805d7616f2c Mon Sep 17 00:00:00 2001 From: Konstantin Borisov Date: Tue, 24 Mar 2026 12:28:14 -0500 Subject: [PATCH 3/6] Cross-account KMS key resolution for account migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add resolveKeyId() helper that resolves bare key IDs to full ARNs using KMS_LEGACY_ACCOUNT_ID env var. New keys store ARN instead of bare key ID. Backwards compatible — no-op when env var is unset. --- web/api/helpers/kms-eth.ts | 8 ++++---- web/api/helpers/kms.ts | 33 +++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/web/api/helpers/kms-eth.ts b/web/api/helpers/kms-eth.ts index 0644cf1ca..d4e09d4d2 100644 --- a/web/api/helpers/kms-eth.ts +++ b/web/api/helpers/kms-eth.ts @@ -22,7 +22,7 @@ import { toBeHex, zeroPadValue, } from "ethers"; -import { scheduleKeyDeletion } from "./kms"; +import { resolveKeyId, scheduleKeyDeletion } from "./kms"; // secp256k1 curve order const SECP256K1_N = BigInt( @@ -187,7 +187,7 @@ export async function getEthAddressFromKMS( keyId: string, ): Promise { const { PublicKey } = await client.send( - new GetPublicKeyCommand({ KeyId: keyId }), + new GetPublicKeyCommand({ KeyId: resolveKeyId(keyId) }), ); if (!PublicKey) { @@ -214,7 +214,7 @@ async function signWithKms( ): Promise { const { Signature: derSignature } = await client.send( new SignCommand({ - KeyId: keyId, + KeyId: resolveKeyId(keyId), Message: digest, MessageType: "DIGEST", SigningAlgorithm: "ECDSA_SHA_256", @@ -273,7 +273,7 @@ export async function createManagerKey( }), ); - const keyId = KeyMetadata?.KeyId; + const keyId = KeyMetadata?.Arn ?? KeyMetadata?.KeyId; const createdAt = KeyMetadata?.CreationDate; if (!keyId || !createdAt) { diff --git a/web/api/helpers/kms.ts b/web/api/helpers/kms.ts index 75a1ee7c6..8248676ce 100644 --- a/web/api/helpers/kms.ts +++ b/web/api/helpers/kms.ts @@ -31,6 +31,31 @@ export const getKMSClient = async (region?: string) => { }); }; +/** + * Resolves a KMS key reference to a full ARN for cross-account access. + * + * - Full ARN (`arn:aws:kms:...`): returned as-is (new keys or already resolved) + * - Bare key ID (UUID): if `KMS_LEGACY_ACCOUNT_ID` is set, constructs a full + * ARN pointing to the legacy account; otherwise returns as-is + * + * This enables a seamless migration period where the application runs in a new + * AWS account but still uses KMS keys that remain in the old account. + */ +export function resolveKeyId(keyId: string, region?: string): string { + if (keyId.startsWith("arn:")) { + return keyId; + } + + const legacyAccountId = process.env.KMS_LEGACY_ACCOUNT_ID; + if (!legacyAccountId) { + return keyId; + } + + const resolvedRegion = + region ?? process.env.AWS_REGION_NAME ?? "eu-west-1"; + return `arn:aws:kms:${resolvedRegion}:${legacyAccountId}:key/${keyId}`; +} + export const createKMSKey = async ( client: KMSClient, alg: KeySpec, @@ -45,7 +70,7 @@ export const createKMSKey = async ( }), ); - const keyId = KeyMetadata?.KeyId; + const keyId = KeyMetadata?.Arn ?? KeyMetadata?.KeyId; const createdAt = KeyMetadata?.CreationDate; if (keyId && createdAt) { @@ -70,7 +95,7 @@ export const getKMSKeyStatus = async (client: KMSClient, keyId: string) => { try { const { KeyMetadata } = await client.send( new DescribeKeyCommand({ - KeyId: keyId, + KeyId: resolveKeyId(keyId), }), ); return KeyMetadata?.Enabled; @@ -97,7 +122,7 @@ export const signJWTWithKMSKey = async ( const response = await client.send( new SignCommand({ - KeyId: kms_id, + KeyId: resolveKeyId(kms_id), Message: new Uint8Array(Buffer.from(encodedHeaderPayload)), MessageType: "RAW", SigningAlgorithm: "RSASSA_PKCS1_V1_5_SHA_256", @@ -122,7 +147,7 @@ export const scheduleKeyDeletion = async (client: KMSClient, keyId: string) => { try { await client.send( new ScheduleKeyDeletionCommand({ - KeyId: keyId, + KeyId: resolveKeyId(keyId), PendingWindowInDays: 7, // Note: 7 is the minimum allowed value }), ); From b5c3e3c7fd878c8dae8ad5fe9c33455c4bca6591 Mon Sep 17 00:00:00 2001 From: Konstantin Borisov Date: Wed, 25 Mar 2026 12:56:27 -0500 Subject: [PATCH 4/6] Lint fix --- tests/api/specs/hasura/app-verification.spec.ts | 3 ++- tests/api/specs/hasura/get-image.spec.ts | 3 ++- tests/api/specs/hasura/get-unverified-images.spec.ts | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/api/specs/hasura/app-verification.spec.ts b/tests/api/specs/hasura/app-verification.spec.ts index 5a330fe49..6b7616205 100644 --- a/tests/api/specs/hasura/app-verification.spec.ts +++ b/tests/api/specs/hasura/app-verification.spec.ts @@ -20,7 +20,8 @@ describe.skip("Hasura API - App Verification", () => { beforeAll(async () => { // Set required AWS environment variables for tests - process.env.ASSETS_S3_BUCKET_NAME = process.env.ASSETS_S3_BUCKET_NAME || "world-id-assets-staging"; + process.env.ASSETS_S3_BUCKET_NAME = + process.env.ASSETS_S3_BUCKET_NAME || "world-id-assets-staging"; process.env.ASSETS_S3_REGION = "us-east-1"; // Create test app and metadata diff --git a/tests/api/specs/hasura/get-image.spec.ts b/tests/api/specs/hasura/get-image.spec.ts index 5b16a4c5b..39c933be7 100644 --- a/tests/api/specs/hasura/get-image.spec.ts +++ b/tests/api/specs/hasura/get-image.spec.ts @@ -36,7 +36,8 @@ describe("Hasura API - Get Image", () => { beforeAll(async () => { // Set required AWS environment variables for tests - process.env.ASSETS_S3_BUCKET_NAME = process.env.ASSETS_S3_BUCKET_NAME || "world-id-assets-staging"; + process.env.ASSETS_S3_BUCKET_NAME = + process.env.ASSETS_S3_BUCKET_NAME || "world-id-assets-staging"; process.env.ASSETS_S3_REGION = "us-east-1"; // Create test team and user diff --git a/tests/api/specs/hasura/get-unverified-images.spec.ts b/tests/api/specs/hasura/get-unverified-images.spec.ts index 971e51254..1d692141d 100644 --- a/tests/api/specs/hasura/get-unverified-images.spec.ts +++ b/tests/api/specs/hasura/get-unverified-images.spec.ts @@ -32,7 +32,8 @@ describe("Hasura API - Get Unverified Images", () => { beforeAll(async () => { // Set required AWS environment variables for tests - process.env.ASSETS_S3_BUCKET_NAME = process.env.ASSETS_S3_BUCKET_NAME || "world-id-assets-staging"; + process.env.ASSETS_S3_BUCKET_NAME = + process.env.ASSETS_S3_BUCKET_NAME || "world-id-assets-staging"; process.env.ASSETS_S3_REGION = "us-east-1"; // Create test team and user From 9edf68ff934387b65d3fe1735efde7864a0e35e6 Mon Sep 17 00:00:00 2001 From: Konstantin Borisov Date: Wed, 25 Mar 2026 13:01:34 -0500 Subject: [PATCH 5/6] Formatting --- web/api/helpers/kms.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/api/helpers/kms.ts b/web/api/helpers/kms.ts index 8248676ce..fbe26d8e2 100644 --- a/web/api/helpers/kms.ts +++ b/web/api/helpers/kms.ts @@ -51,8 +51,7 @@ export function resolveKeyId(keyId: string, region?: string): string { return keyId; } - const resolvedRegion = - region ?? process.env.AWS_REGION_NAME ?? "eu-west-1"; + const resolvedRegion = region ?? process.env.AWS_REGION_NAME ?? "eu-west-1"; return `arn:aws:kms:${resolvedRegion}:${legacyAccountId}:key/${keyId}`; } From e5b7d9922a529ec7919bce6f2b4cd04e770b8c56 Mon Sep 17 00:00:00 2001 From: Konstantin Borisov Date: Wed, 25 Mar 2026 14:00:44 -0500 Subject: [PATCH 6/6] Add TODO for temporary staging host validation bypass --- web/lib/utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/web/lib/utils.ts b/web/lib/utils.ts index 61090f9c5..a07b7682b 100644 --- a/web/lib/utils.ts +++ b/web/lib/utils.ts @@ -260,6 +260,7 @@ export const isValidHostName = (request: Request) => { } // Skip check for development and staging (staging may not have custom domains) + // TODO(CORPLAT-689): Remove staging bypass after DNS cutover if ( process.env.NODE_ENV === "development" || process.env.NEXT_PUBLIC_APP_ENV === "staging"