diff --git a/index.js b/index.js index 486c3bb..dddff40 100644 --- a/index.js +++ b/index.js @@ -121,11 +121,13 @@ function fastifyJwt (fastify, options, next) { secretOrPrivateKey = secretOrPublicKey = secret } + let hasStaticPrivateKey = false let hasStaticPublicKey = false let secretCallbackSign = secretOrPrivateKey let secretCallbackVerify = secretOrPublicKey if (typeof secretCallbackSign !== 'function') { secretCallbackSign = wrapStaticSecretInCallback(secretCallbackSign) + hasStaticPrivateKey = true } if (typeof secretCallbackVerify !== 'function') { secretCallbackVerify = wrapStaticSecretInCallback(secretCallbackVerify) @@ -289,9 +291,11 @@ function fastifyJwt (fastify, options, next) { return token } - function mergeOptionsWithKey (options, useProvidedPrivateKey) { + function mergeOptionsWithKey (options, useProvidedPrivateKey, preferOptionsKey) { if (useProvidedPrivateKey && (typeof useProvidedPrivateKey !== 'boolean')) { - return Object.assign({}, options, { key: options.key ?? useProvidedPrivateKey }) + return preferOptionsKey && options.key + ? Object.assign({ key: useProvidedPrivateKey }, options) + : Object.assign({}, options, { key: useProvidedPrivateKey }) } else { const key = useProvidedPrivateKey ? secretOrPrivateKey : secretOrPublicKey return Object.assign(!options.key ? { key } : {}, options) @@ -409,7 +413,8 @@ function fastifyJwt (fastify, options, next) { }, function sign (secretOrPrivateKey, callback) { if (useLocalSigner) { - const signerOptions = mergeOptionsWithKey(options.sign || options, secretOrPrivateKey) + const localSignOptions = options.sign || options + const signerOptions = mergeOptionsWithKey(localSignOptions, secretOrPrivateKey, hasStaticPrivateKey) const localSigner = createSigner(signerOptions) const token = localSigner(payload) callback(null, token) @@ -467,7 +472,6 @@ function fastifyJwt (fastify, options, next) { } const useGlobalOptions = !options - if (typeof options === 'function') { next = options options = {} @@ -508,7 +512,8 @@ function fastifyJwt (fastify, options, next) { }, function verify (secretOrPublicKey, callback) { try { - const verifierOptions = mergeOptionsWithKey(options.verify || options, secretOrPublicKey) + const localVerifyOptions = options.verify || options + const verifierOptions = mergeOptionsWithKey(localVerifyOptions, secretOrPublicKey, hasStaticPublicKey) const localVerifier = getVerifier(verifierOptions, useGlobalOptions) const verifyResult = localVerifier(token) if (verifyResult && typeof verifyResult.then === 'function') { diff --git a/test/jwt.test.js b/test/jwt.test.js index fa79532..b8229f2 100644 --- a/test/jwt.test.js +++ b/test/jwt.test.js @@ -2,7 +2,7 @@ const { test } = require('node:test') const Fastify = require('fastify') -const { createSigner } = require('fast-jwt') +const { createSigner, createVerifier } = require('fast-jwt') const jwt = require('..') const defaultExport = require('..').default const { fastifyJwt: namedExport } = require('..') @@ -3133,3 +3133,52 @@ test('local sign options should not overwrite global sign options', async functi t.assert.strictEqual(fastify.jwt.options.sign.expiresIn, '15m') }) + +test('reply.jwtSign should honor a per-request sign.key override', async function (t) { + t.plan(2) + + const fastify = Fastify() + fastify.register(jwt, { secret: 'hunter2' }) + + fastify.post('/sign', async function (request, reply) { + return reply.jwtSign(request.body, { sign: { key: 'override' } }) + }) + + await fastify.ready() + + const response = await fastify.inject({ + method: 'post', + url: '/sign', + payload: { foo: 'bar' } + }) + + t.assert.strictEqual(response.statusCode, 200) + + const decoded = createVerifier({ key: 'override' })(response.payload) + t.assert.strictEqual(decoded.foo, 'bar') +}) + +test('request.jwtVerify should honor a per-request verify.key override', async function (t) { + t.plan(2) + + const fastify = Fastify() + fastify.register(jwt, { secret: 'hunter2' }) + + fastify.get('/verify', async function (request) { + return request.jwtVerify({ verify: { key: 'override' } }) + }) + + await fastify.ready() + + // Token signed with the override key, not the registration secret. + const token = createSigner({ key: 'override' })({ foo: 'bar' }) + + const response = await fastify.inject({ + method: 'get', + url: '/verify', + headers: { authorization: `Bearer ${token}` } + }) + + t.assert.strictEqual(response.statusCode, 200) + t.assert.strictEqual(JSON.parse(response.payload).foo, 'bar') +})