diff --git a/.changeset/decodejwt-malformed-token.md b/.changeset/decodejwt-malformed-token.md new file mode 100644 index 00000000000..513ffedfd80 --- /dev/null +++ b/.changeset/decodejwt-malformed-token.md @@ -0,0 +1,5 @@ +--- +'@clerk/backend': patch +--- + +Return a `TokenVerificationError` from `decodeJwt` and `verifyToken` for tokens whose header, payload, or signature cannot be decoded. diff --git a/packages/backend/src/jwt/__tests__/verifyJwt.test.ts b/packages/backend/src/jwt/__tests__/verifyJwt.test.ts index d14e536d2e7..8811231aa73 100644 --- a/packages/backend/src/jwt/__tests__/verifyJwt.test.ts +++ b/packages/backend/src/jwt/__tests__/verifyJwt.test.ts @@ -1,5 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { TokenVerificationError } from '../../errors'; import { createJwt, mockJwks, @@ -85,6 +86,18 @@ describe('decodeJwt(jwt)', () => { const { errors: [error] = [] } = decodeJwt('42'); expect(error).toMatchObject(invalidTokenError); }); + + it('returns an error if the token segments are not valid base64url', () => { + const { errors: [error] = [] } = decodeJwt('aaa.bbb.ccc'); + expect(error).toBeInstanceOf(TokenVerificationError); + expect(error).toMatchObject({ reason: 'token-invalid' }); + }); + + it('returns an error if the token segments do not decode to JSON', () => { + const { errors: [error] = [] } = decodeJwt('YWJj.YWJj.YWJj'); + expect(error).toBeInstanceOf(TokenVerificationError); + expect(error).toMatchObject({ reason: 'token-invalid' }); + }); }); describe('verifyJwt(jwt, options)', () => { diff --git a/packages/backend/src/jwt/verifyJwt.ts b/packages/backend/src/jwt/verifyJwt.ts index 6e5d89593c4..4ef5eab47b2 100644 --- a/packages/backend/src/jwt/verifyJwt.ts +++ b/packages/backend/src/jwt/verifyJwt.ts @@ -79,10 +79,21 @@ export function decodeJwt(token: string): JwtReturnType