Skip to content

Commit

Permalink
Add ability to customise verification token (#92)
Browse files Browse the repository at this point in the history
Co-authored-by: Thomas Heymann <[email protected]>
  • Loading branch information
thomheymann and Thomas Heymann authored May 19, 2020
1 parent d9d4152 commit 2e734cb
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 18 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ fastify.listen(3000, err => {
* `clockTolerance`: number of seconds to tolerate when checking the `nbf` and `exp` claims, to deal with small clock differences among different servers
* `maxAge`: the maximum allowed age for tokens to still be valid. It is expressed in seconds or a string describing a time span [zeit/ms](https://github.com/zeit/ms). Eg: `1000`, `"2 days"`, `"10h"`, `"7d"`. A numeric value is interpreted as a seconds count. If you use a string be sure you provide the time units (days, hours, etc), otherwise milliseconds unit is used by default (`"120"` is equal to `"120ms"`).
* `clockTimestamp`: the time in seconds that should be used as the current time for all necessary comparisons.
* `extractToken(request): token`: Callback function allowing to use custom logic to extract the JWT token from the request.

#### messages options

Expand Down
8 changes: 4 additions & 4 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ declare module 'fastify' {
type VerifyPayloadType = object | string;
type DecodePayloadType = object | string;

interface SignCallback extends jwt.SignCallback {}
interface SignCallback extends jwt.SignCallback { }

interface VerifyCallback<Decoded extends VerifyPayloadType> extends jwt.VerifyCallback {
(err: jwt.VerifyErrors, decoded: Decoded): void;
Expand Down Expand Up @@ -63,8 +63,8 @@ declare interface FastifyJWTOptions {
secret: jwt.Secret | { public: jwt.Secret; private: jwt.Secret };
decode?: jwt.DecodeOptions;
sign?: jwt.SignOptions;
verify?: jwt.VerifyOptions;
cookie?: {
verify?: jwt.VerifyOptions & { extractToken?: (request: fastify.FastifyRequest) => string | void; };
cookie?: {
cookieName: string;
};
messages?: {
Expand All @@ -74,7 +74,7 @@ declare interface FastifyJWTOptions {
authorizationTokenInvalid?: ((err: Error) => string) | string;
authorizationTokenUntrusted?: string;
}
trusted?: (request: fastify.FastifyRequest, decodedToken: {[k: string]: any}) => boolean | Promise<boolean>
trusted?: (request: fastify.FastifyRequest, decodedToken: { [k: string]: any }) => boolean | Promise<boolean>
}

declare const fastifyJWT: fastify.Plugin<http.Server, http.IncomingMessage, http.ServerResponse, FastifyJWTOptions>;
Expand Down
12 changes: 9 additions & 3 deletions jwt.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function fastifyJwt (fastify, options, next) {
signOptions.algorithm &&
signOptions.algorithm.includes('RS') &&
(typeof secret === 'string' ||
secret instanceof Buffer)
secret instanceof Buffer)
) {
return next(new Error('RSA Signatures set as Algorithm in the options require a private and public key to be set as the secret'))
}
Expand All @@ -75,7 +75,7 @@ function fastifyJwt (fastify, options, next) {
signOptions.algorithm &&
signOptions.algorithm.includes('ES') &&
(typeof secret === 'string' ||
secret instanceof Buffer)
secret instanceof Buffer)
) {
return next(new Error('ECDSA Signatures set as Algorithm in the options require a private and public key to be set as the secret'))
}
Expand Down Expand Up @@ -202,7 +202,13 @@ function fastifyJwt (fastify, options, next) {
}

let token
if (request.headers && request.headers.authorization) {
const extractToken = options.extractToken
if (extractToken) {
token = extractToken(request)
if (!token) {
return next(new BadRequest(messagesOptions.badRequestErrorMessage))
}
} else if (request.headers && request.headers.authorization) {
const parts = request.headers.authorization.split(' ')
if (parts.length === 2) {
const scheme = parts[0]
Expand Down
62 changes: 62 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1919,3 +1919,65 @@ test('custom response messages', function (t) {
})
})
})

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

const fastify = Fastify()
fastify.register(jwt, { secret: 'test', verify: { extractToken: (request) => request.headers.customauthheader } })

fastify.post('/sign', function (request, reply) {
return reply.jwtSign(request.body)
.then(function (token) {
return { token }
})
})

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

t.test('token can be extracted correctly', function (t) {
t.plan(2)
fastify.inject({
method: 'post',
url: '/sign',
payload: { foo: 'bar' }
}).then(function (signResponse) {
const token = JSON.parse(signResponse.payload).token
t.ok(token)

return fastify.inject({
method: 'get',
url: '/verify',
headers: {
customauthheader: token
}
}).then(function (verifyResponse) {
t.is(verifyResponse.statusCode, 200)
})
})
})

t.test('token can not be extracted', function (t) {
t.plan(2)
fastify.inject({
method: 'post',
url: '/sign',
payload: { foo: 'bar' }
}).then(function (signResponse) {
const token = JSON.parse(signResponse.payload).token
t.ok(token)

return fastify.inject({
method: 'get',
url: '/verify'
}).then(function (verifyResponse) {
t.is(verifyResponse.statusCode, 400)
})
})
})
})
19 changes: 8 additions & 11 deletions type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ app.register(fastifyJwt, {
cookieName: 'jwt'
},
verify: {
maxAge: '1h'
maxAge: '1h',
extractToken: (request) => 'token'
},
decode: {
complete: true
Expand All @@ -23,24 +24,20 @@ app.register(fastifyJwt, {
authorizationTokenExpiredMessage: 'Token Expired',
authorizationTokenInvalid: (err) => `${err.message}`,
authorizationTokenUntrusted: 'Token untrusted'
},
trusted: () => true,
},
trusted: () => true,
});

app.addHook("preHandler", async (request, reply) =>
{
try
{
app.addHook("preHandler", async (request, reply) => {
try {
await request.jwtVerify();
}
catch (err)
{
catch (err) {
reply.send(err);
}
});

app.post('/signup', async (req, reply) =>
{
app.post('/signup', async (req, reply) => {
const token = app.jwt.sign({ user: "userName" });
let data = await app.jwt.verify(token);
const user = req.user;
Expand Down

0 comments on commit 2e734cb

Please sign in to comment.