Skip to content

Commit ddc3c8d

Browse files
committed
fix(backend): require configured jwt header type
1 parent 7ea8a0b commit ddc3c8d

3 files changed

Lines changed: 28 additions & 6 deletions

File tree

packages/backend/src/jwt/__tests__/assertions.test.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,15 @@ describe('assertAudienceClaim(audience?, aud?)', () => {
107107
});
108108

109109
describe('assertHeaderType(typ?, allowedTypes?)', () => {
110-
it('does not throw error if type is missing', () => {
110+
it('does not throw error if type is missing and allowed types are not configured', () => {
111111
expect(() => assertHeaderType(undefined)).not.toThrow();
112-
expect(() => assertHeaderType(undefined, 'JWT')).not.toThrow();
113-
expect(() => assertHeaderType(undefined, ['JWT', 'at+jwt'])).not.toThrow();
112+
});
113+
114+
it('throws error if type is missing and allowed types are configured', () => {
115+
expect(() => assertHeaderType(undefined, 'JWT')).toThrow(`Invalid JWT type undefined. Expected "JWT".`);
116+
expect(() => assertHeaderType(undefined, ['JWT', 'at+jwt'])).toThrow(
117+
`Invalid JWT type undefined. Expected "JWT, at+jwt".`,
118+
);
114119
});
115120

116121
it('does not throw error if type matches default allowed type (JWT)', () => {

packages/backend/src/jwt/__tests__/verifyJwt.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,22 @@ describe('verifyJwt(jwt, options)', () => {
189189
expect(error?.message).toContain('Expected "at+jwt"');
190190
});
191191

192+
it('rejects JWT with missing type when headerType is configured', async () => {
193+
const jwtWithoutTyp = createJwt({
194+
header: { typ: undefined },
195+
});
196+
const inputVerifyJwtOptions = {
197+
key: mockJwks.keys[0],
198+
issuer: mockJwtPayload.iss,
199+
authorizedParties: ['https://accounts.inspired.puma-74.lcl.dev'],
200+
headerType: 'at+jwt',
201+
};
202+
const { errors: [error] = [] } = await verifyJwt(jwtWithoutTyp, inputVerifyJwtOptions);
203+
expect(error).toBeDefined();
204+
expect(error?.message).toContain('Invalid JWT type undefined');
205+
expect(error?.message).toContain('Expected "at+jwt"');
206+
});
207+
192208
it('rejects OAuth JWT when headerType does not match', async () => {
193209
const inputVerifyJwtOptions = {
194210
key: mockJwks.keys[0],

packages/backend/src/jwt/assertions.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,13 @@ export const assertAudienceClaim = (aud?: unknown, audience?: unknown) => {
4747
}
4848
};
4949

50-
export const assertHeaderType = (typ?: unknown, allowedTypes: string | string[] = 'JWT') => {
51-
if (typeof typ === 'undefined') {
50+
export const assertHeaderType = (typ?: unknown, allowedTypes?: string | string[]) => {
51+
if (typeof typ === 'undefined' && typeof allowedTypes === 'undefined') {
5252
return;
5353
}
5454

55-
const allowed = Array.isArray(allowedTypes) ? allowedTypes : [allowedTypes];
55+
const expectedTypes = allowedTypes ?? 'JWT';
56+
const allowed = Array.isArray(expectedTypes) ? expectedTypes : [expectedTypes];
5657
if (!allowed.includes(typ as string)) {
5758
throw new TokenVerificationError({
5859
action: TokenVerificationErrorAction.EnsureClerkJWT,

0 commit comments

Comments
 (0)