diff --git a/packages/backend/src/services/oidc.service.ts b/packages/backend/src/services/oidc.service.ts index 250d3d4..dcc05c7 100644 --- a/packages/backend/src/services/oidc.service.ts +++ b/packages/backend/src/services/oidc.service.ts @@ -120,7 +120,10 @@ export async function validateIdToken(idToken: string, nonce: string): Promise): Promise { +async function signIdToken(claims: Record, tokenIssuer = issuer): Promise { return new SignJWT(claims) .setProtectedHeader({ alg: "ES256", kid: "test-kid" }) - .setIssuer(issuer) + .setIssuer(tokenIssuer) .setAudience(CLIENT_ID) .setSubject("sub-123") .setExpirationTime("5m") @@ -241,6 +241,22 @@ describe("token exchange + id_token validation", () => { await expect(validateIdToken(idToken, "nonce-1")).rejects.toThrow(); }); + test("accepts an id_token whose issuer has a trailing slash", async () => { + resetOidcServiceForTesting(); + trailingSlashIssuer = true; + try { + const idToken = await signIdToken( + { email: "alice@example.com", email_verified: true, nonce: "nonce-1" }, + `${issuer}/`, + ); + const info = await validateIdToken(idToken, "nonce-1"); + expect(info.email).toBe("alice@example.com"); + } finally { + trailingSlashIssuer = false; + resetOidcServiceForTesting(); + } + }); + test("id_token with wrong audience is rejected", async () => { const idToken = await new SignJWT({ email_verified: true }) .setProtectedHeader({ alg: "ES256", kid: "test-kid" })