@fastify/auth

repository·main·Indexed 18 days ago

https://github.com/fastify/fastify-auth

A lightweight utility for Fastify version 5.0.4 that allows developers to compose multiple authentication strategies using logical 'and' or 'or' relations. It manages the execution flow of provided validation functions (callbacks, Promises, or async functions) rather than providing the strategies themselves. It supports nested arrays for complex composite authentication, a 'run: all' option to execute every strategy regardless of success, and can be applied via route-level or plugin-level preHandler hooks.

Tokens
2.5K
Snippets
10
Records
11
Agent score
14%

What's inside @fastify/auth

  1. How @fastify/auth works with authentication strategies

    main

    Note that @fastify/auth does not provide authentication strategies itself. You must provide your own validation logic, typically by decorating the Fastify instance or using another plugin. @fastify/auth acts as a utility to compose these strategies into logical groups (using and or or relations) and apply them to routes or hooks.

    Strategies can be implemented using:

    • Callbacks: (request, reply, done) => { ... }
    • Promises: Functions that return a Promise.
    • Async functions: async (request, reply) => { ... } (Note: if using async, do not call the done parameter to avoid multiple handler calls).
    fastify
      .decorate('verifyJWT', function (request, reply, done) {
        // your validation logic
        done() // pass an error if authentication fails
      })
      .register(require('@fastify/auth'))
      .after(() => {
        fastify.route({
          method: 'POST',
          url: '/secure',
          preHandler: fastify.auth([fastify.verifyJWT]),
          handler: (req, reply) => reply.send({ hello: 'world' })
        })
      })
  2. Compose authentication strategies with logical relations

    main

    You can combine multiple authentication functions using logical and or or relations. By default, the relation is or.

    Basic Relations

    • OR (Default): The request succeeds if at least one strategy succeeds.
    • AND: The request succeeds only if all strategies succeed.

    Composite Authentication (Nested Arrays)

    For complex logic, use nested arrays. The relation of a sub-array is always the opposite of the main relation:

    • If the main relation is or, sub-arrays use and.
    • If the main relation is and, sub-arrays use or.
    Auth CodeLogical Expression
    fastify.auth([f1, f2, [f3, f4]], { relation: 'or' })f1 OR f2 OR (f3 AND f4)
    fastify.auth([f1, f2, [f3, f4]], { relation: 'and' })f1 AND f2 AND (f3 OR f4)

    Run All Strategies

    By default, any successful authentication stops the rest of the chain. To execute every strategy regardless of success/failure (useful for attaching business data to the request), use the run: 'all' option.

    // Example: (verifyUserPassword AND verifyLevel) OR (verifyVIP)
    fastify.route({
      method: 'POST',
      url: '/auth-multiple',
      preHandler: fastify.auth([
        [fastify.verifyUserPassword, fastify.verifyLevel],
        fastify.verifyVIP
      ], {
        relation: 'or' // default
      }),
      handler: (req, reply) => reply.send({ hello: 'world' })
    })
  3. Apply authentication to routes or hooks

    main

    You can apply authentication in two ways:

    1. Route-level preHandler: Applies authentication only to the specific route. This is the most common usage.
    2. Plugin-level preHandler hook: Applies authentication to all routes within the current plugin and its descendants.

    Note: Route definitions must be done as a plugin or within an .after() callback to ensure the plugin is registered before the routes are defined.

    // Route-level
    fastify.route({
      method: 'POST',
      url: '/auth',
      preHandler: fastify.auth([fastify.verifyJWT]),
      handler: (req, reply) => reply.send({ ok: true })
    })
    
    // Hook-level (applies to all routes in this scope)
    fastify.addHook('preHandler', fastify.auth([fastify.verifyJWT]))
  4. Configure @fastify/auth options

    main

    You can configure the default behavior of the plugin when registering it.

    OptionTypeDefaultDescription
    defaultRelation'and' | 'or''or'The default relation between the functions in the array.

    To change the default relation for the entire plugin instance, pass it during registration:

    fastify.register(require('@fastify/auth'), { defaultRelation: 'and' })
  5. Configure authentication relations and execution logic

    main

    The @fastify/auth plugin uses logical relations to determine if a request is authenticated based on an array of functions.

    Relation Logic

    Parent RelationSub-array (Nested) RelationDescription
    'or''and'The request is authenticated if any of the top-level functions succeed. Within a sub-array, all functions must succeed.
    'and''or'The request is authenticated if all top-level functions succeed. Within a sub-array, any function succeeding is enough.

    The run option

    By default, the plugin stops processing as soon as the relation requirement is met (e.g., the first successful function in an 'or' relation).

    If you set run: 'all' in the options object, the plugin will continue to execute all provided functions regardless of whether the authentication requirement has already been satisfied or failed. This is useful if you need side effects from all authentication attempts.

    // Example: Requiring ALL strategies to pass, but running all even if one fails
    fastify.get('/strict', {
      auth: fastify.auth([auth1, auth2], { relation: 'and', run: 'all' })
    }, handler)
  6. Register the @fastify/auth plugin

    main

    To use the authentication utility, register the @fastify/auth plugin with your Fastify instance. You can optionally provide a defaultRelation in the plugin options to set the global logic for how multiple authentication functions are evaluated.

    Supported values for defaultRelation are 'or' (default) and 'and'.

    const fastify = require('fastify')()
    const fastifyAuth = require('@fastify/auth')
    
    fastify.register(fastifyAuth, { defaultRelation: 'and' })
  7. Security: Choosing the right hook (onRequest vs preHandler)

    main

    Choosing the correct lifecycle hook is critical for security and performance:

    • Use onRequest or preParsing: For authentication methods that do not require the request body (e.g., checking a JWT in a header). This prevents Denial of Service (DoS) attacks by avoiding unnecessary body parsing for unauthorized requests.
    • Use preHandler: Only for authentication methods that require the request body (e.g., a token sent inside a JSON payload).

    Using preHandler for header-based auth can lead to unnecessary memory allocation if a malicious user sends a large payload that is ultimately rejected by the auth check.

  8. Use the auth decorator in routes

    main

    Once registered, the plugin decorates your Fastify instance with an auth method. You use this method within your route handlers to apply one or more authentication functions (strategies).

    Arguments

    fastify.auth(functions, opts)

    • functions: An array of authentication functions. These functions can be single functions or arrays of functions (sub-arrays).
    • opts: An optional configuration object:
      • relation: 'or' or 'and'. Determines how the functions in the array are evaluated. Note that sub-arrays use the inverse logic of the parent array.
      • run: If set to 'all', the plugin will attempt to run all authentication functions even if one fails. Otherwise, it stops at the first failure (for 'and') or first success (for 'or'). Must be set to 'all' if used.

    Authentication Function Signature

    Each authentication function must follow the standard Fastify hook signature: function(request, reply, done) { ... }.

    They can be synchronous (calling done()) or return a Promise.

    // Example: Applying multiple auth strategies to a route
    fastify.get('/protected', { auth: fastify.auth([strategyOne, [strategyTwo, strategyThree]]) }, (request, reply) => {
      reply.send({ authenticated: true })
    })
  9. Reference: auth() method options

    main

    When calling fastify.auth(functions, opts) in a route definition, the opts object supports:

    KeyTypeDefaultDescription
    relation'or' | 'and'pluginOptions.defaultRelationThe logical relation for the provided functions.
    run'all'nullIf set to 'all', all functions in the array will be executed.
    // Route-specific auth options
    {
      relation: 'or',
      run: 'all'
    }
  10. Reference: fastifyAuth plugin options

    main

    When registering the @fastify/auth plugin, you can provide the following configuration:

    KeyTypeDefaultDescription
    defaultRelation'or' | 'and''or'The default logical relation used by fastify.auth() if no relation is specified in the route options.
    // Plugin registration options
    {
      defaultRelation: 'and'
    }