Skip to content

Commit

Permalink
fix: handle fast-jwt verify error properly (#282)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tarrask authored Mar 16, 2023
1 parent 7245d14 commit 18fbf03
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 4 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -454,8 +454,12 @@ const fastify = require('fastify')

const myCustomMessages = {
badRequestErrorMessage: 'Format is Authorization: Bearer [token]',
badCookieRequestErrorMessage: 'Cookie could not be parsed in request',
noAuthorizationInHeaderMessage: 'Autorization header is missing!',
noAuthorizationInCookieMessage: 'No Authorization was found in request.cookies',
authorizationTokenExpiredMessage: 'Authorization token expired',
authorizationTokenUntrusted: 'Untrusted authorization token',
authorizationTokenUnsigned: 'Unsigned authorization token
// for the below message you can pass a sync function that must return a string as shown or a string
authorizationTokenInvalid: (err) => {
return `Authorization token is invalid: ${err.message}`
Expand Down
1 change: 1 addition & 0 deletions jwt.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ declare namespace fastifyJwt {
authorizationTokenExpiredMessage?: string
authorizationTokenInvalid?: ((err: Error) => string) | string
authorizationTokenUntrusted?: string
authorizationTokenUnsigned?: string
}
trusted?: (request: FastifyRequest, decodedToken: { [k: string]: any }) => boolean | Promise<boolean> | SignPayloadType | Promise<SignPayloadType>
formatUser?: (payload: SignPayloadType) => UserType,
Expand Down
8 changes: 7 additions & 1 deletion jwt.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const messages = {
noAuthorizationInCookieMessage: 'No Authorization was found in request.cookies',
authorizationTokenExpiredMessage: 'Authorization token expired',
authorizationTokenInvalid: (err) => `Authorization token is invalid: ${err.message}`,
authorizationTokenUntrusted: 'Untrusted authorization token'
authorizationTokenUntrusted: 'Untrusted authorization token',
authorizationTokenUnsigned: 'Unsigned authorization token'
}

function wrapStaticSecretInCallback (secret) {
Expand Down Expand Up @@ -109,6 +110,7 @@ function fastifyJwt (fastify, options, next) {
const NoAuthorizationInCookieError = createError('FST_JWT_NO_AUTHORIZATION_IN_COOKIE', messagesOptions.noAuthorizationInCookieMessage, 401)
const AuthorizationTokenExpiredError = createError('FST_JWT_AUTHORIZATION_TOKEN_EXPIRED', messagesOptions.authorizationTokenExpiredMessage, 401)
const AuthorizationTokenUntrustedError = createError('FST_JWT_AUTHORIZATION_TOKEN_UNTRUSTED', messagesOptions.authorizationTokenUntrusted, 401)
const AuthorizationTokenUnsignedError = createError('FAST_JWT_MISSING_SIGNATURE', messagesOptions.authorizationTokenUnsigned, 401)
const NoAuthorizationInHeaderError = createError('FST_JWT_NO_AUTHORIZATION_IN_HEADER', messagesOptions.noAuthorizationInHeaderMessage, 401)
const AuthorizationTokenInvalidError = createError('FST_JWT_AUTHORIZATION_TOKEN_INVALID', typeof messagesOptions.authorizationTokenInvalid === 'function'
? messagesOptions.authorizationTokenInvalid({ message: '%s' })
Expand Down Expand Up @@ -498,6 +500,10 @@ function fastifyJwt (fastify, options, next) {
: new AuthorizationTokenInvalidError())
}

if (error.code === TokenError.codes.missingSignature) {
return callback(new AuthorizationTokenUnsignedError())
}

return callback(error)
}
},
Expand Down
54 changes: 51 additions & 3 deletions test/jwt.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1497,7 +1497,7 @@ test('decode', function (t) {
})

test('errors', function (t) {
t.plan(14)
t.plan(15)

const fastify = Fastify()
fastify.register(jwt, {
Expand Down Expand Up @@ -1576,6 +1576,16 @@ test('errors', function (t) {
})
})

fastify.get('/verifyFailUnsignedToken', function (request, reply) {
request.jwtVerify()
.then(function (decodedToken) {
return reply.send(decodedToken)
})
.catch(function (error) {
return reply.send(error)
})
})

fastify
.ready()
.then(function () {
Expand Down Expand Up @@ -1763,6 +1773,25 @@ test('errors', function (t) {
})
})

t.test('Unsigned token error', function (t) {
t.plan(2)

const signer = createSigner({ algorithm: 'none' })
const unsignedToken = signer({ foo: 'bar' })

fastify.inject({
method: 'get',
url: '/verifyFailUnsignedToken',
headers: {
authorization: `Bearer ${unsignedToken}`
}
}).then(function (response) {
const error = JSON.parse(response.payload)
t.equal(error.message, 'Unsigned authorization token')
t.equal(response.statusCode, 401)
})
})

t.test('requestVerify function: steed.waterfall error function loop test', function (t) {
t.plan(3)

Expand Down Expand Up @@ -2322,10 +2351,10 @@ test('token and refreshToken in a signed cookie, with @fastify/cookie parsing, d
})

test('custom response messages', function (t) {
t.plan(5)
t.plan(6)

const fastify = Fastify()
fastify.register(jwt, { secret: 'test', messages: { noAuthorizationInHeaderMessage: 'auth header missing', authorizationTokenExpiredMessage: 'token expired', authorizationTokenInvalid: 'invalid token', authorizationTokenUntrusted: 'untrusted token' }, trusted: (request, { jti }) => jti !== 'untrusted' })
fastify.register(jwt, { secret: 'test', messages: { noAuthorizationInHeaderMessage: 'auth header missing', authorizationTokenExpiredMessage: 'token expired', authorizationTokenInvalid: 'invalid token', authorizationTokenUntrusted: 'untrusted token', authorizationTokenUnsigned: 'unsigned token' }, trusted: (request, { jti }) => jti !== 'untrusted' })

fastify.get('/verify', function (request, reply) {
request.jwtVerify()
Expand Down Expand Up @@ -2408,6 +2437,25 @@ test('custom response messages', function (t) {
})
})

t.test('custom unsigned token error', function (t) {
t.plan(2)

const signer = createSigner({ algorithm: 'none' })
const unsignedToken = signer({ foo: 'bar' })

fastify.inject({
method: 'get',
url: '/verify',
headers: {
authorization: `Bearer ${unsignedToken}`
}
}).then(function (response) {
const error = JSON.parse(response.payload)
t.equal(error.message, 'unsigned token')
t.equal(response.statusCode, 401)
})
})

t.test('custom untrusted token error', function (t) {
t.plan(2)

Expand Down

0 comments on commit 18fbf03

Please sign in to comment.