diff --git a/packages/backend/src/jwt/__tests__/assertions.test.ts b/packages/backend/src/jwt/__tests__/assertions.test.ts index 0dea86341d6..f1ccef09fe8 100644 --- a/packages/backend/src/jwt/__tests__/assertions.test.ts +++ b/packages/backend/src/jwt/__tests__/assertions.test.ts @@ -107,10 +107,15 @@ describe('assertAudienceClaim(audience?, aud?)', () => { }); describe('assertHeaderType(typ?, allowedTypes?)', () => { - it('does not throw error if type is missing', () => { + it('does not throw error if type is missing and allowed types are not configured', () => { expect(() => assertHeaderType(undefined)).not.toThrow(); - expect(() => assertHeaderType(undefined, 'JWT')).not.toThrow(); - expect(() => assertHeaderType(undefined, ['JWT', 'at+jwt'])).not.toThrow(); + }); + + it('throws error if type is missing and allowed types are configured', () => { + expect(() => assertHeaderType(undefined, 'JWT')).toThrow(`Invalid JWT type undefined. Expected "JWT".`); + expect(() => assertHeaderType(undefined, ['JWT', 'at+jwt'])).toThrow( + `Invalid JWT type undefined. Expected "JWT, at+jwt".`, + ); }); it('does not throw error if type matches default allowed type (JWT)', () => { diff --git a/packages/backend/src/jwt/__tests__/verifyJwt.test.ts b/packages/backend/src/jwt/__tests__/verifyJwt.test.ts index d2f529251d8..c1a6ab4cbe6 100644 --- a/packages/backend/src/jwt/__tests__/verifyJwt.test.ts +++ b/packages/backend/src/jwt/__tests__/verifyJwt.test.ts @@ -189,6 +189,22 @@ describe('verifyJwt(jwt, options)', () => { expect(error?.message).toContain('Expected "at+jwt"'); }); + it('rejects JWT with missing type when headerType is configured', async () => { + const jwtWithoutTyp = createJwt({ + header: { typ: undefined }, + }); + const inputVerifyJwtOptions = { + key: mockJwks.keys[0], + issuer: mockJwtPayload.iss, + authorizedParties: ['https://accounts.inspired.puma-74.lcl.dev'], + headerType: 'at+jwt', + }; + const { errors: [error] = [] } = await verifyJwt(jwtWithoutTyp, inputVerifyJwtOptions); + expect(error).toBeDefined(); + expect(error?.message).toContain('Invalid JWT type undefined'); + expect(error?.message).toContain('Expected "at+jwt"'); + }); + it('rejects OAuth JWT when headerType does not match', async () => { const inputVerifyJwtOptions = { key: mockJwks.keys[0], diff --git a/packages/backend/src/jwt/assertions.ts b/packages/backend/src/jwt/assertions.ts index 5cc4325f98e..074d3f14c30 100644 --- a/packages/backend/src/jwt/assertions.ts +++ b/packages/backend/src/jwt/assertions.ts @@ -47,12 +47,13 @@ export const assertAudienceClaim = (aud?: unknown, audience?: unknown) => { } }; -export const assertHeaderType = (typ?: unknown, allowedTypes: string | string[] = 'JWT') => { - if (typeof typ === 'undefined') { +export const assertHeaderType = (typ?: unknown, allowedTypes?: string | string[]) => { + if (typeof typ === 'undefined' && typeof allowedTypes === 'undefined') { return; } - const allowed = Array.isArray(allowedTypes) ? allowedTypes : [allowedTypes]; + const expectedTypes = allowedTypes ?? 'JWT'; + const allowed = Array.isArray(expectedTypes) ? expectedTypes : [expectedTypes]; if (!allowed.includes(typ as string)) { throw new TokenVerificationError({ action: TokenVerificationErrorAction.EnsureClerkJWT,