Skip to content

Secret function is called with different signature depending on context #388

@paolochiodi

Description

@paolochiodi

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

5.x.x

Plugin version

10.x.x

Node.js version

24.x

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

26.3

Description

fastify-jwt allows the secret plugin option to be a function instead of a static string.
The documentation states that the function will receive request, token, and callback as input.

This works correctly when called from the request or reply context, i.e.

request.jwtVerify()

However, verify can be called on the fastify instance outside of a request context too:

fastify.jwt.verify('example token')

When this is done, and a function was passed as secret option, i.e.

const fastify = require('fastify')()
fastify.register(require('@fastify/jwt'), {
  secret: function randomSecret(request, token, callback) {
    console.log(request)
    callback(null, 'random token')
  }
})

Then the secret function will be called with the decoded token as first parameter, instead of the fastify request object.

This is happening because in the verify implementation on the fastify instance, the secret option is never treated as a function, and it's passed as is to the the fast-jwt verifier as key.
The verifier itself does support key to be function, but indeed with its own signature.

Whilst this is not a bug per se, I'd say it's an inconsistent or surprising behavior that might cause bugs in consumer code - and might be worth clarifying and uniform it.

PS: this was found whilst investigating the following issue on nearform/fastify-jwt-jwks#54

Link to code that reproduces the bug

https://github.com/paolochiodi/fastify-jwt-secret-as-function-repro

Expected Behavior

The documentation seems to be saying that the function version of the secret is only supported in requests context, but it's easy to miss:

Function based secret is supported by the request.jwtVerify() and reply.jwtSign() methods and is called with request, token, and callback parameters.

I'd suggest a few possible resolutions (from more desirable to less desirable, imo):

  1. Calls to secret function are uniformed and happen with a consistent, documented signature
  2. Differences are documented clearly, so consumers are made aware of the scenario
  3. secret option as function is prohibited and guarded against when using verify on the fastify instance (i.e. throwing an error).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions