Giter Site home page Giter Site logo

fastify-auth's Introduction

@fastify/auth

CI NPM version js-standard-style

This module does not provide an authentication strategy, but it provides a very fast utility to handle authentication (and multiple strategies) in your routes, without adding overhead.
Check out a complete example here.

Install

npm i @fastify/auth

Usage

As said above, @fastify/auth does not provide an authentication strategy, so you must provide authentication strategies yourself, with a decorator or another plugin.

In the following example, you will find a very simple implementation that should help you understand how to use this module:

fastify
  .decorate('verifyJWTandLevel', function (request, reply, done) {
    // your validation logic
    done() // pass an error if the authentication fails
  })
  .decorate('verifyUserAndPassword', function (request, reply, done) {
    // your validation logic
    done() // pass an error if the authentication fails
  })
  .register(require('@fastify/auth'))
  .after(() => {
    fastify.route({
      method: 'POST',
      url: '/auth-multiple',
      preHandler: fastify.auth([
        fastify.verifyJWTandLevel,
        fastify.verifyUserAndPassword
      ]),
      handler: (req, reply) => {
        req.log.info('Auth route')
        reply.send({ hello: 'world' })
      }
    })
  })

The default relationship of these customized authentication strategies is or, while we could also use and:

fastify
  .decorate('verifyAdmin', function (request, reply, done) {
    // your validation logic
    done() // pass an error if the authentication fails
  })
  .decorate('verifyReputation', function (request, reply, done) {
    // your validation logic
    done() // pass an error if the authentication fails
  })
  .register(require('@fastify/auth'))
  .after(() => {
    fastify.route({
      method: 'POST',
      url: '/auth-multiple',
      preHandler: fastify.auth([
        fastify.verifyAdmin,
        fastify.verifyReputation
      ], {
        relation: 'and'
      }),
      handler: (req, reply) => {
        req.log.info('Auth route')
        reply.send({ hello: 'world' })
      }
    })
  })

If you need composite authentication, such as verifying user account passwords and levels or meeting VIP eligibility criteria, you can use nested arrays. For example if you need the following logic: [(verifyUserPassword and verifyLevel) or (verifyVIP)], it can be achieved with the code below:

fastify
  .decorate('verifyUserPassword', function (request, reply, done) {
    // your validation logic
    done() // pass an error if the authentication fails
  })
  .decorate('verifyLevel', function (request, reply, done) {
    // your validation logic
    done() // pass an error if the authentication fails
  })
  .decorate('verifyVIP', function (request, reply, done) {
    // your validation logic
    done() // pass an error if the authentication fails
  })
  .register(require('@fastify/auth'))
  .after(() => {
    fastify.route({
      method: 'POST',
      url: '/auth-multiple',
      preHandler: fastify.auth([
        [fastify.verifyUserPassword, fastify.verifyLevel], // The arrays within an array have the opposite relation to the main (default) relation.
        fastify.verifyVIP
      ], {
        relation: 'or' // default relation
      }),
      handler: (req, reply) => {
        req.log.info('Auth route')
        reply.send({ hello: 'world' })
      }
    })
  })

If the relation (defaultRelation) parameter is and, then the relation inside sub-arrays will be or. If the relation (defaultRelation) parameter is or, then the relation inside sub-arrays will be and.

auth code resulting logical 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)

You can use the defaultRelation option while registering the plugin, to change the default relation:

fastify.register(require('@fastify/auth'), { defaultRelation: 'and'} )

For more examples, please check example-composited.js

This plugin support callback and Promise returned by the functions. Note that an async function does not have to call the done parameter, otherwise the route handler to which the auth methods are linked to might be called multiple times:

fastify
  .decorate('asyncVerifyJWTandLevel', async function (request, reply) {
    // your async validation logic
    await validation()
    // throws an error if the authentication fails
  })
  .decorate('asyncVerifyUserAndPassword', function (request, reply) {
    // return a promise that throws an error if the authentication fails
    return myPromiseValidation()
  })
  .register(require('@fastify/auth'))
  .after(() => {
    fastify.route({
      method: 'POST',
      url: '/auth-multiple',
      preHandler: fastify.auth([
        fastify.asyncVerifyJWTandLevel,
        fastify.asyncVerifyUserAndPassword
      ]),
      handler: (req, reply) => {
        req.log.info('Auth route')
        reply.send({ hello: 'world' })
      }
    })
  })

Keep in mind that route definition should either be done as a plugin or within an .after() callback. For a complete example implementation, see example.js.

@fastify/auth will run all your authentication methods and your request will continue if at least one succeeds, otherwise it will return an error to the client. Any successful authentication will automatically stop @fastify/auth from trying the rest, unless you provide the run: 'all' parameter:

fastify.route({
  method: 'GET',
  url: '/run-all',
  preHandler: fastify.auth([
    (request, reply, done) => { console.log('executed 1'); done() },
    (request, reply, done) => { console.log('executed 2'); done() },
    (request, reply, done) => { console.log('executed 3'); done(new Error('you are not authenticated')) },
    (request, reply, done) => { console.log('executed 4'); done() },
    (request, reply, done) => { console.log('executed 5'); done(new Error('you shall not pass')) }
  ], { run: 'all' }),
  handler: (req, reply) => { reply.send({ hello: 'world' }) }
})

This example will show all the console logs and will reply always with 401: you are not authenticated. The run parameter is useful if you are adding to the request business data read from auth-tokens.

You can use this plugin on route level as in the above example or on hook level by using the preHandler hook:

fastify.addHook('preHandler', fastify.auth([
  fastify.verifyJWTandLevel,
  fastify.verifyUserAndPassword
]))

fastify.route({
  method: 'POST',
  url: '/auth-multiple',
  handler: (req, reply) => {
    req.log.info('Auth route')
    reply.send({ hello: 'world' })
  }
})

The difference between the two approaches is that if you use the route level preHandler function the authentication will run just for the selected route. Whereas if you use the preHandler hook the authentication will run for all the routes declared inside the current plugin (and its descendants).

Security Considerations

onRequest vs. preHandler hook

The main difference between the onRequest and preHandler stages of the Fastify Lifecycle is that the body payload is not parsed in the onRequest stage. Parsing the body can be a potential security risk, as it can be used for denial of service (DoS) attacks. Therefore, it is recommended to avoid parsing the body for unauthorized access.

Using the @fastify/auth plugin in the preHandler hook can result in unnecessary memory allocation if a malicious user sends a large payload in the request body and the request is unauthorized. In this case, Fastify will parse the body, even though the request is not authorized, leading to unnecessary memory allocation. To avoid this, it is recommended to use the onRequest hook for authentication, if the authentication method does not require the request body, such as @fastify/jwt, which expects the authentication in the request header.

For authentication methods that do require the request body, such as sending a token in the body, you must use the preHandler hook.

In mixed cases you must use the preHandler hook.

API

Options

@fastify/auth accepts the options object:

{
  defaultRelation: 'and'
}
  • defaultRelation (Default: or): The default relation between the functions. It can be either or or and.

Acknowledgements

This project is kindly sponsored by:

License

Licensed under MIT.

fastify-auth's People

Contributors

cemremengu avatar climba03003 avatar codershiba avatar delvedor avatar dependabot-preview[bot] avatar dependabot[bot] avatar eomm avatar espenmeidell avatar ethan-arrowood avatar fdawgs avatar giacomocerquone avatar github-actions[bot] avatar greenkeeper[bot] avatar iifawzi avatar is2ei avatar jsumners avatar kibertoad avatar mcollina avatar moklick avatar pavelpolyakov avatar priceen avatar puppo avatar sadams avatar salmanm avatar shenxdtw avatar uzlopak avatar yakovenkodenis avatar yamalight avatar yunfan avatar zekth avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fastify-auth's Issues

fastify.auth(functions: fastifyAuth.AuthFunction[]) is missing second parameter in type declaration

🐛 Bug Report

The Typescript type for fastify.auth is missing the optional second parameter that allows us to change the relationship between the authentication functions from or to and (as described in the readme).

To Reproduce

Steps to reproduce the behavior:
Use fastify-auth with Typescript. Attempt to change the relation between two auth functions.

fastify.get(
    '/auth-test',
    {
      schema: authSchema,
      preHandler: fastify.auth([fastify.jwtTokenCheck, fastify.userIsAdmin], {relation: 'and'}),
    },
    async () => {
      return { status: 'ok' };
    },
  );

Expected behavior

We would expect this to work as in the readme, but Typescript expects only one parameter for the function.

Paste the results here:

error TS2554: Expected 1 arguments, but got 2.

Your Environment

  • node version: 10
  • fastify version:^2.10.0
  • fastify-auth version: ^0.6.0
  • os: Mac

Configure default relation

Prerequisites

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

🚀 Feature Proposal

I'm looking to add some CASL authorisation to my routes and I thought it would be less verbose if I could set the default relationship as "and" instead of "or". That way I can run my authentication check first, then the authorisation check, without appending: { relation: 'and' } to every route.

Motivation

Less verbose if I never intend to use "or"

Example

app.register(require('@fastify/auth'), {
  defaultRelation: 'and'
})

How to support composited authenication?

i have some apis needs to be accessed with some higher grants than auth only
for ordinary auth check, i have a JWT based identify solution, but for the higher grants i might needs to add a new and strict solution based on JWT

the current support for multiple auth solution of fastify-auth is the relaction of OR, while my needs might need the relation AND

is it possible for you guys to adds a options to toggle that?

like

fastify.post('/api/ordinary', { preHandler: fastify.auth([fastify.jwtcheck, fastify.userpasscheck], {relation: 'or'}) }, handler_ordinary)

fastify.post('/api/strict', { preHandler: fastify.auth([[fastify.jwtcheck, fastify.userpasscheck], fastify.highergrantscheck], {relation: 'and'}) }, handler_ordinary)

NPM package update

Prerequisites

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

Issue

Latest npm package version still is 4.2.0
But you did important fix in 4.3.0 when we can expect this package to be published to npm?

Version 10 of node.js has been released

Version 10 of Node.js (code name Dubnium) has been released! 🎊

To see what happens to your code in Node.js 10, Greenkeeper has created a branch with the following changes:

  • Added the new Node.js version to your .travis.yml

If you’re interested in upgrading this repo to Node.js 10, you can open a PR with these changes. Please note that this issue is just intended as a friendly reminder and the PR as a possible starting point for getting your code running on Node.js 10.

More information on this issue

Greenkeeper has checked the engines key in any package.json file, the .nvmrc file, and the .travis.yml file, if present.

  • engines was only updated if it defined a single version, not a range.
  • .nvmrc was updated to Node.js 10
  • .travis.yml was only changed if there was a root-level node_js that didn’t already include Node.js 10, such as node or lts/*. In this case, the new version was appended to the list. We didn’t touch job or matrix configurations because these tend to be quite specific and complex, and it’s difficult to infer what the intentions were.

For many simpler .travis.yml configurations, this PR should suffice as-is, but depending on what you’re doing it may require additional work or may not be applicable at all. We’re also aware that you may have good reasons to not update to Node.js 10, which is why this was sent as an issue and not a pull request. Feel free to delete it without comment, I’m a humble robot and won’t feel rejected 🤖


FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

default imports do not work with TypeScript

Prerequisites

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

Fastify version

3.x

Plugin version

1.1.0

Node.js version

14.x

Operating system

Windows

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

10

Description

When trying to use this import in TS using the latest fastify-auth version:

import fastifyAuth from 'fastify-auth';
app
    .register(fastifyJwt, { secret: 'secret' })
    .register(fastifyAuth)

, it actually doesn't work anymore and throws an error:
error TS2339: Property 'auth' does not exist on type 'FastifyInstance'. This is not a problem when using the older version.

I see a similar issue was posted for fastify-jwt here which still replicates.

Steps to Reproduce

Try to import plugin using named import in TypeScript.

Expected Behavior

Named import should work.

Incompatible with fastify 1.0-rc.1

fastify-auth is currently incompatible with 1.0-rc.1, throws the following error:

    fastify-plugin - expected '>=0.13.1' fastify version, '1.0.0-rc.1' is installed

      1 | // npm packages
      2 | const initFastify = require('fastify');
    > 3 | const fastifyAuth = require('fastify-auth');

Updating fastify-plugin should probably fix this, right? Should I send a PR?

Extended Composite Auth in #217 is broken

Prerequisites

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

Fastify version

4.26.1

Plugin version

4.5.0

Node.js version

20.11.0

Operating system

macOS

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

14.3.1 (23D60)

Description

#217 introduced new rules for auth.
The main problem is that now both paths (in or relation) going in parallel instead of waiting until the first path will fail.
I commented all tests except one that clearly showing a problem (and my own production code solution too).

Steps to Reproduce

  1. Clone repo https://github.com/isnifer/fastify-auth and go inside (cd fastify-auth)
  2. Install deps (npm i)
  3. Run tests (npm t)
  4. You will see a message from a second auth path in or relation "THIS IS A SECOND AUTH PATH SHOULD NOT BE CALLED" but you SHOULDN'T.

THE ONLY ROUTE

fastify.route({
  method: "POST",
  url: "/check-two-sub-arrays-or",
  preHandler: fastify.auth(
    [[fastify.verifyBigAsync], [fastify.verifyOddAsync]],
    { relation: "or" }
  ),
  handler: (req, reply) => {
    req.log.info("Auth route");
    reply.send({ hello: "world" });
  },
});

TWO AUTH HANDLERS UPDATED WITH "CONSOLE.LOG"

function verifyBigAsync(request, reply) {
  console.log("If you see it — IT IS OK");
  return new Promise((resolve, reject) => {
    verifyBig(request, reply, (err) => {
      if (err) {
        console.log("FIRST_ERROR", err);
        reject(err);
      }
      resolve();
    });
  });
}

function verifyOddAsync(request, reply) {
  console.log("THIS IS A SECOND AUTH PATH SHOULD NOT BE CALLED");
  return new Promise((resolve, reject) => {
    verifyOdd(request, reply, (err) => {
      if (err) reject(err);
      resolve();
    });
  });
}

// A function behind "verifyBigAsync"
function verifyBig(request, reply, done) {
  const n = request.body.n;

  if (typeof n !== "number" || n < 100) {
    request.big = false;
    return done(new Error("`n` is not big"));
  }

  request.big = true;
  return done();
}

THE ONLY TEST WHERE "verifyBigAsync" is passing, BUT "verifyOddAsync" called to

test("Two sub-arrays Or Relation success", (t) => {
  t.plan(2);

  fastify.inject(
    {
      method: "POST",
      url: "/check-two-sub-arrays-or",
      payload: {
        n: 110, // previously 11 which less than 100
      },
    },
    (err, res) => {
      t.error(err);
      const payload = JSON.parse(res.payload);
      t.same(payload, { hello: "world" });
    }
  );
});

Logs from the terminal

npm t

> @fastify/[email protected] test
> npm run test:unit && npm run test:typescript


> @fastify/[email protected] test:unit
> tap

 PASS ​ ./test/auth.test.js 29 OK 37.593ms
 PASS ​ ./test/example-async.test.js 18 OK 50.854ms
./test/example-composited.test.js 1> If you see it — IT IS OK
./test/example-composited.test.js 1> THIS IS A SECOND AUTH PATH SHOULD NOT BE CALLED
 PASS ​ ./test/example-composited.test.js 2 OK 17.401ms
 PASS ​ ./test/example.test.js 20 OK 46.829ms

Expected Behavior

No response

An in-range update of standard is breaking the build 🚨

The devDependency standard was updated from 13.1.0 to 14.0.0-1.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

standard is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push: The Travis CI build failed (Details).

Commits

The new version differs by 70 commits.

  • 7117e53 14.0.0-1
  • c1ebec6 Disallow spaces inside of curly braces in JSX expressions in children (react/jsx-curly-spacing)
  • 2d619f3 Require linebreaks in curly braces in JSX attributes and expressions to be consistent (react/jsx-curly-newline)
  • 5dfdc83 Require JSX event handler names to follow conventions (react/jsx-handler-names)
  • ff4ef49 Require JSX attributes and logical expressions to be indented correctly (react/jsx-indent)
  • 13e4bee Disallow missing key prop in JSX elements that likely require a key prop (react/jsx-key)
  • 29861dc Disallow accidental comments in JSX from being inserted as text nodes (react/jsx-no-comment-textnodes)
  • c852a11 Prevent usage of unsafe target='_blank' (react/jsx-no-target-blank)
  • 30eddfa Disallow unnecessary curly braces in JSX props and children. (react/jsx-curly-brace-presence)
  • d3d14a5 Require PascalCase for user-defined JSX components (react/jsx-pascal-case)
  • 93027d9 Require shorthand form for JSX fragments (react/jsx-fragments)
  • 093b7f0 Disallow multiple spaces between inline JSX props (react/jsx-props-no-multi-spaces)
  • 6ee9fb1 alphabetize rules
  • d179362 remove rule which already shipped
  • f8551e9 Require JSX closing bracket to be aligned with the opening tag (react/jsx-closing-bracket-location)

There are 70 commits in total.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

always return 401 with relation and

🐛 Bug Report

For a valid auth series, the status code returned with and is always 401.
I tested it also if this feature is not released yet and the test cases don't check the statusCode of the response.

To Reproduce

const fastify = require('fastify')()

function authFactory (id) {
  return function (request, reply, done) {
    console.log(id)
    if (request.query.name === id || request.query.name === 'all') {
      done()
    } else {
      done(new Error('query ' + id))
    }
  }
}

fastify.register(require('fastify-auth'))
  .after(() => {
    fastify.addHook('preHandler', fastify.auth([authFactory('one'), authFactory('two')], { relation: 'and' }))
    fastify.get('/', (req, res) => res.send(42))
  })

fastify.listen(3000)

Expected behavior

Calling curl -X GET 'http://localhost:3000/?name=all'
The response code should be 200, not 401 since all the validation are done()

Paste the results here:

$ curl -v http://localhost:3000?name=all
*   Trying ::1:3000...
* TCP_NODELAY set
*   Trying 127.0.0.1:3000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET /?name=all HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.65.3
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 401 Unauthorized
< content-type: application/json; charset=utf-8
< content-length: 2
< Connection: keep-alive
<
42

Your Environment

  • node version: 12
  • fastify version: >=2.8.0
  • os: Windows

What about Basic Auth?

In hapi you can do this. Pretty straight forward.

Any idea how to do this with fastify?

await server.register(require('hapi-auth-basic'));

    server.auth.strategy('simple', 'basic', { validate });
    server.auth.default('simple');

    server.route({
        method: 'GET',
        path: '/',
        handler: function (request, h) {

            return 'welcome';
        }
    });

    await server.start();

Provide a way to skip an auth handler

🚀 Feature Proposal

Allow an auth handler to inform the plugin the this auth does not apply and skip to the next handler (if any)

Motivation

Creating an abitrary error is clunky and hard to read

From sample repo: https://github.com/cliedeman/fastify-anonymous-example/blob/master/src/app.ts#L40

  await app.decorate(
    'allowAnonymous',
    function (
      request: FastifyRequest,
      _reply: FastifyReply,
      done: DoneFuncWithErrOrRes
    ) {
      if (!request.headers.authorization) {
        return done();
      }

      // Noisy Error
      return done(new Error('not anonymous'));
    }
  );

  await app.decorate(
    'allowBearerAuth',
    function (
      request: FastifyRequest,
      _reply: FastifyReply,
      done: DoneFuncWithErrOrRes
    ) {
      if (request.headers.authorization) {
        ...
        return done();
      }

      // Noisy Error
      return done(new Error('anonymous'));
    }
  );

Please provide an example for how this feature would be used.

import {default as fastifyAuth, errNextAuth } from 'fastify-auth';

...

  await app.decorate(
    'allowAnonymous',
    function (
      request: FastifyRequest,
      _reply: FastifyReply,
      done: DoneFuncWithErrOrRes
    ) {
      if (!request.headers.authorization) {
        return done();
      }

      return done(errNextAuth);
    }
  );

  await app.decorate(
    'allowBearerAuth',
    function (
      request: FastifyRequest,
      _reply: FastifyReply,
      done: DoneFuncWithErrOrRes
    ) {
      if (request.headers.authorization) {
        ...
        return done();
      }

      return done(errNextAuth);
    }
  );

This will also allow the plugin to report a different error if no handler accepted the request.

Typescript bug when used in conjunction with "fastify-type-provider-zod".

Prerequisites

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

Fastify version

4.25.2

Plugin version

4.4.0

Node.js version

20.9.0

Operating system

macOS

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

14.2.1

Description

I came across a weird bug today when I was building my API. Basically when using @fastify/auth plugin along with fastify-type-provider-zod package typescript complains. I get the following error:

Type 'preHandlerHookHandler<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown, FastifySchema, FastifyTypeProviderDefault, FastifyBaseLogger>' is not assignable to type 'onRequestHookHandler<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown, FastifySchema, ZodTypeProvider, FastifyBaseLogger> | onRequestHookHandler<...>[] | undefined'.
  Type 'preHandlerHookHandler<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown, FastifySchema, FastifyTypeProviderDefault, FastifyBaseLogger>' is not assignable to type 'onRequestHookHandler<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown, FastifySchema, ZodTypeProvider, FastifyBaseLogger>'.
    The 'this' types of each signature are incompatible.
      Type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, ZodTypeProvider>' is not assignable to type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProviderDefault>'.
        The types returned by 'after()' are incompatible between these types.
          Type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, ZodTypeProvider> & PromiseLike<...>' is not assignable to type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProviderDefault> & PromiseLike<...>'.
            Type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, ZodTypeProvider> & PromiseLike<...>' is not assignable to type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProviderDefault>'.
              The types of 'addSchema(...).addHook' are incompatible between these types.
                Type '{ <RouteGeneric extends import("/Users/geovla/Developer/Projects/apps/taskmaster/api/node_modules/.pnpm/[email protected]/node_modules/fastify/types/route").RouteGenericInterface = import("/Users/geovla/Developer/Projects/apps/taskmaster/api/node_modules/.pnpm/[email protected]/node_modules/fastify/types/route").RouteGene...' is not assignable to type '{ <RouteGeneric extends import("/Users/geovla/Developer/Projects/apps/taskmaster/api/node_modules/.pnpm/[email protected]/node_modules/fastify/types/route").RouteGenericInterface = import("/Users/geovla/Developer/Projects/apps/taskmaster/api/node_modules/.pnpm/[email protected]/node_modules/fastify/types/route").RouteGene...'. Two different types with this name exist, but they are unrelated.
                  Types of parameters 'hook' and 'hook' are incompatible.
                    Types of parameters 'opts' and 'opts' are incompatible.
                      Type 'RouteOptions<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, ZodTypeProvider, FastifyBaseLogger> & { ...; }' is not assignable to type 'RouteOptions<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, FastifyTypeProviderDefault, FastifyBaseLogger> & { ...; }'.
                        Type 'RouteOptions<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, ZodTypeProvider, FastifyBaseLogger> & { ...; }' is not assignable to type 'RouteOptions<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, FastifyTypeProviderDefault, FastifyBaseLogger>'.
                          Types of property 'handler' are incompatible.
                            Type 'RouteHandlerMethod<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, ZodTypeProvider, FastifyBaseLogger>' is not assignable to type 'RouteHandlerMethod<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, FastifyTypeProviderDefault, FastifyBaseLogger>'.
                              Type 'FastifyTypeProviderDefault' is not assignable to type 'ZodTypeProvider'.

The error is shown both in onRequest and preHandler hooks. I should mention that the error does not show up if I remove the app.auth() function and use the auth functions on their own or in an array.

Steps to Reproduce

Create a fastify server and install the mentioned packages along with zod. Create two auth functions and try to use them in either onRequest or preHandler hooks.

export default async function routes(app: FastifyInstance) {
  app
    .withTypeProvider<ZodTypeProvider>()
    .route({
      method: 'GET',
      url: '/',
      schema: {
        description: 'Get all users',
        tags: ['User'],
        response: {
          200: userResponseSchema.array(),
        },
      },
      onRequest: app.auth([app.authenticate, app.guard]),
      handler: getUsersHandler,
    });
}

Expected Behavior

I would like to use this package without this typescript error like this:

onRequest: app.auth([app.authenticate, app.guard])

statusCode dirty with relation or

🐛 Bug Report

An auth "pipeline" could dirty the reply status code and when the handler execute, the status code returned is in an bad status.

To Reproduce

const fastify = require('fastify')()

function authFactory (id, status) {
  return function (request, reply, done) {
    console.log(id)
    if (request.query.name === id || request.query.name === 'all') {
      done()
    } else {
      reply.code(status)
      done(new Error('query ' + id))
    }
  }
}

fastify.register(require('fastify-auth'))
  .after(() => {
    fastify.addHook('preHandler', fastify.auth([authFactory('one', 501), authFactory('two', 502)], { relation: 'or' }))
    fastify.get('/', (req, res) => res.send(42))
  })

fastify.listen(3000)

Expected behavior

Calling curl -X GET 'http://localhost:3000/?name=two' I would expect 200 but I receive 501 that was set by the first auth handler that has dirtier the replay, I think we should restore the status code across the auth handler execution.

$ curl -v http://localhost:3000?name=two
*   Trying ::1:3000...
* TCP_NODELAY set
*   Trying 127.0.0.1:3000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET /?name=two HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.65.3
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 501 Not Implemented
< content-type: application/json; charset=utf-8
< content-length: 2
< Connection: keep-alive
<
42

Your Environment

  • node version: 12
  • fastify version: >=2.8.0
  • os: Windows
  • any other relevant information

An in-range update of fastify is breaking the build 🚨

Version 1.8.0 of fastify was just published.

Branch Build failing 🚨
Dependency fastify
Current Version 1.7.0
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

fastify is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push The Travis CI build failed Details

Release Notes v1.8.0

Features

  • Configure request id header key - #1024
  • Support case insensitivity. - #1017
  • Feature/#713 beforeHandler for setNotFoundHandler - #1005

Fixes

Docs

  • Update Ecosystem.md - #874
  • add-chinese-doc-link - #1021
  • Update Middlewares.md - #996
  • Rename swaggergen to openapi glue - #1013
  • Update Reply.md - #1003
  • Add fastify-wamp-router plugin to docs/Ecosystem.md - #1002
  • Add missing documentation links - #1001
  • fix syntax in doc - #997
  • Add fastify-xml-body-parser in community list - #782

Typescript

  • fix listen ts types - #1000

Internals

  • test: add test case for request.id - #1030
  • Add test for checking multipart request using form-data module - #1026
  • Resolves issue #1006 - #1008
Commits

The new version differs by 19 commits.

  • 55fc867 Bumped v1.8.0
  • e58c627 test: add test case for request.id (#1030)
  • 7f734fd Add test for checking multipart request using form-data module (#1026)
  • 4949a9c Configure request id header key (#1024)
  • 7476cb4 Update Ecosystem.md (#874)
  • 42d10f6 New instructions have been added for use with fastif-cli (#1020)
  • a137087 add-chinese-doc-link (#1021)
  • 92ee7db Support case insensitivity. (#1017)
  • 0dfb356 Update Middlewares.md (#996)
  • 8955f27 Resolves issue #1006 (#1008)
  • dd337fd Feature/#713 beforeHandler for setNotFoundHandler (#1005)
  • 1b92295 Rename swaggergen to openapi glue (#1013)
  • 2f96a80 fix listen ts types (#1000)
  • 68114ee Add fastify-wamp-router plugin to docs/Ecosystem.md (#1002)
  • 724199f Update Reply.md (#1003)

There are 19 commits in total.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

fastify.auth stalled in jwt verify

I'm using fastify-jwt to verify JWT but. but when i combine it with fastify-auth the auth method just stalled and i did not get any response. what could go wrong?

my server.js

const fastify = require("fastify")({
  logger: {
    prettyPrint: true,
    prettifier: require('pino-pretty')
  }
});

fastify
  .register(require('fastify-cors'), {
    origin: true
  })
  .register(require("./plugins/db"), {
    url: "mongodb://localhost:27017/vfcms"
  })
  .register(require('fastify-jwt'), {
    secret: 'supersecret',
    maxAge: '1d'
  })
  .register(require('fastify-auth'))

  fastify.decorate('verifyJWT', async (req, reply) => {
    try {
      await req.jwtVerify()
    } catch (error) {
      reply.send(error)
    }
  })

fastify.register(require("./services/root"));
fastify.register(require("./services/user"));

fastify.listen(3000, function(err, address) {
  if (err) {
    fastify.log.error(err);
    process.exit(1);
  }
  fastify.log.info(`server listening on ${address}`);
});

my root.js

async function routes(fastify, options) {
  fastify.route({
    method: 'GET',
    url: '/',
    handler: (req, reply) => {
      reply.code(200).send({ api: "ready"})
    }
  })

  fastify.route({
    method: 'GET',
    url: '/dashboard',
    beforeHandler: fastify.auth([
      fastify.verifyJWT
    ]),
    handler: (req, reply) => {
      reply.code(200).send({ imVerified: "ready"})
    }
  })
}

module.exports = routes;

a complete repo my project are on github.

but if I'm not using beforeHandler: fastify.auth([ fastify.verifyJWT ]) and just beforeHandler: [ fastify.verifyJWT ] it pass perfectly, any advice?

An in-range update of rimraf is breaking the build 🚨

The devDependency rimraf was updated from 2.7.0 to 2.7.1.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

rimraf is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push: The Travis CI build failed (Details).

Commits

The new version differs by 2 commits.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

No plugin export in ESM

Prerequisites

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

Fastify version

4.10.2

Plugin version

4.2.0

Node.js version

18.12.1

Operating system

macOS

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

13.1

Description

The result of these exports...

module.exports = fp(fastifyAuth, {
  fastify: '4.x',
  name: '@fastify/auth'
})
module.exports.default = fastifyAuth
module.exports.fastifyAuth = fastifyAuth

...when imported into ESM is that you wind up with two instances of fastifyAuth and no plugin, whereas default is, as so named, the default import. If you try doing import * as fastifyAuthPlugin, you still wind up with just the default and fastifyAuth properties.

For compatibility with modern JavaScript it should really be:

module.exports.default = fp(fastifyAuth, {
  fastify: '4.x',
  name: '@fastify/auth'
})
module.exports.fastifyAuth = fastifyAuth

Steps to Reproduce

See above.

Expected Behavior

For the plugin to be the default export.

No overload matches for fastify plugin

Prerequisites

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

Fastify version

4.21.0

Plugin version

4.3.0

Node.js version

18.x

Operating system

macOS

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

Monterey

Description

Trying to migrate JS->TS for a NodeJS backend service. After changing index.js -> index.ts all my fastify plugins are fine (e.g. FastifyOpenapiDocs, FastifyMultipart, FastifySentry), but FastifyAuth has this error:

No overload matches this call.
  Overload 1 of 3, '(plugin: FastifyPluginCallback<{ defaultRelation: string; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...>): FastifyInstance<...> & PromiseLike<...>', gave the following error.
    Argument of type 'typeof fastifyAuth' is not assignable to parameter of type 'FastifyPluginCallback<{ defaultRelation: string; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'.
      Type 'typeof fastifyAuth' provides no match for the signature '(instance: FastifyInstance<RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'.
      Type 'typeof fastifyAuth' provides no match for the signature '(instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProvider>, opts: { ...; }, done: (err?: Error) => void): void'.
  Overload 2 of 3, '(plugin: FastifyPluginAsync<{ defaultRelation: string; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...>): FastifyInstance<...> & PromiseLike<...>', gave the following error.
    Argument of type 'typeof fastifyAuth' is not assignable to parameter of type 'FastifyPluginAsync<{ defaultRelation: string; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'.
      Type 'typeof fastifyAuth' provides no match for the signature '(instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProvider>, opts: { ...; }): Promise<...>'.
  Overload 3 of 3, '(plugin: FastifyPluginCallback<{ defaultRelation: string; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger> | FastifyPluginAsync<...> | Promise<...> | Promise<...>, opts?: FastifyRegisterOptions<...>): FastifyInstance<...> & PromiseLike<...>', gave the following error.
    Argument of type 'typeof fastifyAuth' is not assignable to parameter of type 'FastifyPluginCallback<{ defaultRelation: string; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger> | FastifyPluginAsync<...> | Promise<...> | Promise<...>'.ts(2769)

Steps to Reproduce

Code looks something like:

// index.ts
import * as FastifyAuth from '@fastify/auth';

fastify.decorate('authfunc1', authfunc1);
fastify.decorate('authfunc2', authfunc2);
await fastify.register(FastifyAuth, { defaultRelation: 'and' }); //error under `FastifyAuth`

Expected Behavior

No response

Typescript bug when used in conjunction with "fastify-type-provider-zod".

Prerequisites

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

Fastify version

4.25.2

Plugin version

4.4.0

Node.js version

20.9.0

Operating system

macOS

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

14.2.1

Description

I came across a weird bug today when I was building my API. Basically when using @fastify/auth plugin along with fastify-type-provider-zod package typescript complains. I get the following error:

Type 'preHandlerHookHandler<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown, FastifySchema, FastifyTypeProviderDefault, FastifyBaseLogger>' is not assignable to type 'onRequestHookHandler<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown, FastifySchema, ZodTypeProvider, FastifyBaseLogger> | onRequestHookHandler<...>[] | undefined'.
  Type 'preHandlerHookHandler<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown, FastifySchema, FastifyTypeProviderDefault, FastifyBaseLogger>' is not assignable to type 'onRequestHookHandler<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown, FastifySchema, ZodTypeProvider, FastifyBaseLogger>'.
    The 'this' types of each signature are incompatible.
      Type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, ZodTypeProvider>' is not assignable to type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProviderDefault>'.
        The types returned by 'after()' are incompatible between these types.
          Type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, ZodTypeProvider> & PromiseLike<...>' is not assignable to type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProviderDefault> & PromiseLike<...>'.
            Type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, ZodTypeProvider> & PromiseLike<...>' is not assignable to type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProviderDefault>'.
              The types of 'addSchema(...).addHook' are incompatible between these types.
                Type '{ <RouteGeneric extends import("/Users/geovla/Developer/Projects/apps/taskmaster/api/node_modules/.pnpm/[email protected]/node_modules/fastify/types/route").RouteGenericInterface = import("/Users/geovla/Developer/Projects/apps/taskmaster/api/node_modules/.pnpm/[email protected]/node_modules/fastify/types/route").RouteGene...' is not assignable to type '{ <RouteGeneric extends import("/Users/geovla/Developer/Projects/apps/taskmaster/api/node_modules/.pnpm/[email protected]/node_modules/fastify/types/route").RouteGenericInterface = import("/Users/geovla/Developer/Projects/apps/taskmaster/api/node_modules/.pnpm/[email protected]/node_modules/fastify/types/route").RouteGene...'. Two different types with this name exist, but they are unrelated.
                  Types of parameters 'hook' and 'hook' are incompatible.
                    Types of parameters 'opts' and 'opts' are incompatible.
                      Type 'RouteOptions<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, ZodTypeProvider, FastifyBaseLogger> & { ...; }' is not assignable to type 'RouteOptions<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, FastifyTypeProviderDefault, FastifyBaseLogger> & { ...; }'.
                        Type 'RouteOptions<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, ZodTypeProvider, FastifyBaseLogger> & { ...; }' is not assignable to type 'RouteOptions<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, FastifyTypeProviderDefault, FastifyBaseLogger>'.
                          Types of property 'handler' are incompatible.
                            Type 'RouteHandlerMethod<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, ZodTypeProvider, FastifyBaseLogger>' is not assignable to type 'RouteHandlerMethod<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, FastifyTypeProviderDefault, FastifyBaseLogger>'.
                              Type 'FastifyTypeProviderDefault' is not assignable to type 'ZodTypeProvider'.

The error is shown both in onRequest and preHandler hooks. I should mention that the error does not show up if I remove the app.auth() function and use the auth functions on their own or in an array.

Steps to Reproduce

Create a fastify server and install the mentioned packages along with zod. Create two auth functions and try to use them in either onRequest or preHandler hooks.

export default async function routes(app: FastifyInstance) {
  app
    .withTypeProvider<ZodTypeProvider>()
    .route({
      method: 'GET',
      url: '/',
      schema: {
        description: 'Get all users',
        tags: ['User'],
        response: {
          200: userResponseSchema.array(),
        },
      },
      onRequest: app.auth([app.authenticate, app.guard]),
      handler: getUsersHandler,
    });
}

Expected Behavior

I would like to use this package without this typescript error like this:

onRequest: app.auth([app.authenticate, app.guard])

fastify-auth does not work with async strategies

Hello,

I don't know if this is a bug or feature, but it seems that fastify-auth does not currently support async authentication strategies:
https://github.com/PP-etc/fastify-auth-with-async-pre-handler/blob/master/__tests__/stucks.test.js#L8

Declaration above will never be resolved, since done is not passed and not called.

While originally preHandler (and it's father beforeHandler) do support async:
https://www.fastify.io/docs/latest/Hooks/#respond-to-a-request-from-a-hook

Should this be fixed?

Using multiple strategies does not work

Hey, thanks for the lovely work.
I was playing around with fastify-auth and I can't actually create a steady case with multiple strategies. Apparently, something does not work as expected. Using the example below the authentication flow does not pass by bar() strategy.

const Fastify = require('fastify');

const build = (opts = {}) => {
  const fastify = Fastify(opts);
  const routes = () => {
    fastify.get(
      '/',
      {
        beforeHandler: fastify.auth([fastify.foo, fastify.bar])
      },
      (request, reply) => ({hello: 'world'})
    );
  };
  return fastify
    .register(require('fastify-auth'))
    .decorate('foo', (req, rep, done) => {
      console.log('foo');
      done();
    })
    .decorate('bar', (req, rep, done) => {
      console.log('bar');
      done();
    })
    .after(routes);
};

const fastify = build();
fastify.listen(3000, err => {
  if (err) throw err;
  console.log(`Server listenting at http://localhost:${fastify.server.address().port}`);
});

Any help will be highly appreciated.

addHook doesn't do anything

hi.
it seems that i'm missing something.
this is my code:

module.exports = function (fastify, opts, next) {
  fastify.register(require('fastify-auth'));
  fastify.decorate('verifyUserAndPassword', function (request, reply, done) {
    console.log(request);
    done(new Error('err'));
  });

  fastify.ready(err => {
    if (err) throw err;
    fastify.addHook('preHandler', fastify.auth([fastify.verifyUserAndPassword]));
  });

  fastify.get('/', async function (req, reply) {
    reply.send({ message: 'hi' });
  })
  fastify.register(require('./users'));
  
  next();
};

but it doesn't have any effect. the above piece of code is a plugin registered as the parent of all of my routes, and i'm throwing an error in done callback, but all the routes are working without any error.
thanks.

Proposal - Provide a better and modular auth mechanism

Hi, I just found this plugin and would like to know how can it help me to implement an auth mechanism in my application but I'm a little bit confused

This module does not provide an authentication strategy, but it provides a very fast utility to handle authentication (also multiple strategies) in your routes, without adding overhead.

Marketing boom!

The documentation is really complex and it is hard to find an intro. In my opinion, it looks like that this plugin only helps to run an array of auth functions and respond with 401 when an error was passed. I really expect something different when I read fastify-auth. This is just my personal feedback and as always I'm engaged to help you with that.

It would be great to provide an interface to implement different auth strategys similiar to hapi so that the implementation details are not visible for the route definition.

fastify.route({
      method: 'POST',
      auth: 'basic',
      url: '/auth',
      handler: ....
})

Proposal:

  • Create an onAddRoute hook to build the beforeHandler for the specific auth strategy at startup time.
function fastifyAuth(fastify, opts, done) {
 fastify.addHook('onAddRoute', (route, done) => {
   if(opts.auth === 'basic') {
     route.beforeHandler.add(require('fastify-basic-auth'))
   }
   done()
 })
}

ChainAlert: npm package release (1.2.0) has no matching tag in this repo

Dear fastify-auth maintainers,
Thank you for your contribution to the open-source community.

This issue was automatically created to inform you a new version (1.2.0) of fastify-auth was published without a matching tag in this repo.

As part of our efforts to fight software supply chain attacks, we would like to verify this release is known and intended, and not a result of an unauthorized activity.

If you find this behavior legitimate, kindly close and ignore this issue. Read more

badge

Use new @fastify org dependencies

Prerequisites

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

Issue

Migrate to the @fastify org dependencies when Fastify v4 releases.

See #151

relation: 'and' is not working

🐛 Bug Report

Npm module fastify-auth has issue and thus relation: 'and' is not working for multiple auth handler inside a route.

To Reproduce

Steps to reproduce the behavior:

preHandler: fastifyApp.auth([fastifyApp.verifyJWT, fastifyApp.verifyOauth], { relation: 'and' }),

Expected behavior

Both verifyJWT & verifyOauth should get called and should be passed before a route is handled.

Paste the results here:

Only first auth handler is called.

Your Environment

  • node version: 12
  • fastify version: >=2.7.1
  • fastify-auth version: >=0.5.0
  • os: Mac

Root cause

When npm module fastify-auth gets installed it has different code than what is in src. When i replaced node_modules/fastify-auth/fastify-auth.js, it started working

Fastify-auth throws and error after an asynchronous call is completed "TypeError: that.done is not a function"

Prerequisites

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

Fastify version

3.27.1

Plugin version

3.0.1

Node.js version

14.17.6

Operating system

Linux

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

Ubuntu 20.04.3 LTS

Description

I'am trying to build an authentication plugin using fastify, fastify-auth, and aws cognito.

The fastify application is generated using create-fastify-app

  1. My route looks like this:
module.exports = async function (fastify, opts) {
    fastify
        .post(
            '/service',
            { ...options, schema: { ...options.schema, body: { ...schema, } } },
            fastify.auth([fastify.verify]),
            crudController.addNew
        )
  1. My fastify.verify decorate function looks like this
const fp = require('fastify-plugin')
const jwt = require('jsonwebtoken')
const jwkToPem = require('jwk-to-pem')

module.exports = fp(async (fastify, opts) => {

    fastify
        .register(require('fastify-auth'))

        .decorate("jwk", { keys: [{ ... //all the secrets here }] })

        .decorate("verify", verifyJWT)

    function verifyJWT(request, reply) {

        const pemjwk = jwkToPem(fastify.jwk.keys[0])

        if (request.body && request.body.failureWithReply) {
            reply.code(401).send({ error: 'Unauthorized' })
            return Promise.reject(new Error())
        }

        if (!request.headers.token) {
            reply
                .type('application/json')
                .code(401)
                .send({ error: 'Missing token header' })
            return Promise.reject(new Error('Missing token header'))
        }

        return new Promise((resolve, reject) => {
            jwt.verify(request.headers.token, pemjwk, { algorithms: ['RS256'] }, (err, payload) => {
                if (err) {
                    return reject(err)
                }
                resolve(payload)
            })
        }).then((payload) => {
            fastify.log.info(`User ${payload['cognito:username']} authenticated !`)
        }).catch((error) => {
            reply
                .code(401)
                .send(error)
            throw new Error(error)
        })

    }
})

**fastify.verify** decorator just verify the JWT token, return the payload if user is authentic otherwise throw an error.

Fastify log the message "user authenticated!" everything works perfect so far.

But when the request is completed, I got the following error that crash the application:

22:06:35  incoming request POST xxx /api/v1/auth/login
22:06:37  request completed 1.4s
22:06:48  incoming request POST xxx /api/v1/service
22:06:48  User ousssddfs authenticated !
TypeError: that.done is not a function
    at Auth.completeAuth (/workstation/dev/haflago/backend/node_modules/fastify-auth/auth.js:115:12)
    at Auth.onAuth (/workstation/dev/haflago/backend/node_modules/fastify-auth/auth.js:89:21)
    at /workstation/dev/haflago/backend/node_modules/fastify-auth/auth.js:79:43
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
[nodemon] app crashed - waiting for file changes before starting...

I also compared my code with their test example, and it looks pretty similar !!

Did anybody get this error before ?

Steps to Reproduce

  1. Create a plugin to register fastify-auth & decorte verify function:
const fp = require('fastify-plugin')
const jwt = require('jsonwebtoken')
const jwkToPem = require('jwk-to-pem')

module.exports = fp(async (fastify, opts) => {

    fastify
        .register(require('fastify-auth'))
        .decorate("verify", async (request, reply) => {

            if (request.body && request.body.failureWithReply) {
                reply.code(401).send({ error: 'Unauthorized' })
                return Promise.reject(new Error())
            }

            if (!request.headers.token) {
                reply
                    .type('application/json')
                    .code(401)
                    .send({ error: 'Missing token header' })
                return Promise.reject(new Error('Missing token header'))
            }

            return new Promise((resolve, reject) => {
                jwt.verify(request.headers.token, pemjwk, { algorithms: ['RS256'] }, (err, payload) => {
                    if (err) {
                        return reject(err)
                    }
                    resolve(payload)
                })
            }).then((payload) => {
                fastify.log.info(`User ${payload.user is authenticated !`)
                reply
                    .code(200)
                    .send({ message: "Authentication successful !" })
                return reply
            }).catch((error) => {
                reply
                    .code(401)
                    .send(error)
                throw new Error(error)
            })

        })
})
  1. Create a route that take preHandler as fastify-auth :
module.exports = async function (fastify, opts) {
    fastify
        .post(
            '/service',
            fastify.auth([fastify.verify]),
            crudController.addNew
        )
  1. Consume the API with postman :
[nodemon] starting `fastify start ./src/index.js`
22:33:28 ✨ DB is provided at :  mongodb://localhost:27028/halago
22:33:28 ✨ Database is connected
22:33:28 ✨ Server listening at http://127.0.0.1:9898
22:33:34 ✨ incoming request POST xxx /api/v1/service
TypeError: that.done is not a function
    at Auth.completeAuth (/workstation/dev/haflago/backend/node_modules/fastify-auth/auth.js:115:12)
    at Auth.onAuth (/workstation/dev/haflago/backend/node_modules/fastify-auth/auth.js:89:21)
    at /workstation/dev/haflago/backend/node_modules/fastify-auth/auth.js:79:43
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
[nodemon] app crashed - waiting for file changes before starting...

Expected Behavior

Fastify-auth decorator log the decoded JWT token and app doesn't crash.

Type definitions as defined in the route schema get removed when using fastify auth

Prerequisites

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

Fastify version

4.0.0

Plugin version

4.5.0

Node.js version

18.18.0

Operating system

Linux

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

Fedora 39

Description

When you define a body schema in the fastify route options, you get Typescript type completion in the handler function.
However, adding the fastify auth function to the onRequest or preHandler hook removes these definitions, and makes body of type unknown.

Steps to Reproduce

Take the following route for example:

fastify.route({
    method: "POST",
    url: "/",
    schema: {
      body: CreateTodo,
      response: {
        201: ReturnTodo,
      },
    },
    onRequest: fastify.auth([fastify.verifyJWT]),
    handler: async ({ body }, reply) => {
      const result = await fastify.prisma.todo.create({ data: body }); // error here: "body is of type `unknown`"
      const todo = {
        ...result,
        createdAt: result.createdAt.toISOString(),
        updatedAt: result.updatedAt.toISOString(),
      };

      reply.code(201);
      return todo;
    },
  });

However, when we remove the onRequest hook, the error disappears.

Expected Behavior

The type definitions from the schema should carry on through the hook where the fastify auth function is used, and should apply to the actual handler function.

Improvements for composite auth

Prerequisites

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

🚀 Feature Proposal

It seems that composite authentication should allow the or relation for sub-arrays, not only and.

It's currently stated in the docs that:

The arrays within an array always have an AND relationship.

but this feels like a limiting and unnecessary constraint.

Motivation

It seems natural for the relation inside an auth subarray to be the opposite of the main array relation.

The only current use-case for composite auth looks like this:

fastify.auth([f1, f2, [f3, f4]], { relation: 'or' })

which results in the f1 OR f2 OR (f3 AND f4) logical expression.

However, it might be also useful to allow composite auth when relation is 'and', so that the code below

fastify.auth([f1, f2, [f3, f4]], { relation: 'and' })

would result in the following logical expression: f1 AND f2 AND (f3 OR f4).

Basically, when the main (default) array relation is and we want the relation for sub-arrays to be or, and vice versa (like with the current implementation): when the main array relation is or the relation inside subarrays is and.

Example

Current implementation of composite auth only allows a single use-case:

preHandler: fastify.auth([
  [fastify.f1, fastify.f2], // relation inside is AND
  fastify.f3
], {
  relation: 'or'
}),

It would be great to allow also the oppisite use-case:

preHandler: fastify.auth([
  [fastify.f1, fastify.f2], // relation inside is OR
  fastify.f3
], {
  relation: 'and'
}),

Action required: Greenkeeper could not be activated 🚨

🚨 You need to enable Continuous Integration on all branches of this repository. 🚨

To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because we are using your CI build statuses to figure out when to notify you about breaking changes.

Since we did not receive a CI status on the greenkeeper/initial branch, we assume that you still need to configure it.

If you have already set up a CI for this repository, you might need to check your configuration. Make sure it will run on all new branches. If you don’t want it to run on every branch, you can whitelist branches starting with greenkeeper/.

We recommend using Travis CI, but Greenkeeper will work with every other CI service as well.

Once you have installed CI on this repository, you’ll need to re-trigger Greenkeeper’s initial Pull Request. To do this, please delete the greenkeeper/initial branch in this repository, and then remove and re-add this repository to the Greenkeeper integration’s white list on Github. You'll find this list on your repo or organiszation’s settings page, under Installed GitHub Apps.

Ability to customize the decorator name

Prerequisites

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

🚀 Feature Proposal

The ability to customize the decorator name, we can keep auth as default, but would be nice to have a way to change this via server options.

May I do my first fastify related PR or do you guys think not worth it?

Thank you!

Motivation

I already have an auth decorator on my application.

Example

No response

Unable to use a Typescript generic for the Params property off of the request

Prerequisites

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

Fastify version

4.13.0

Plugin version

4.3.0

Node.js version

18.17.1

Operating system

macOS

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

14.0

Description

I'm trying to use a generic on the Params property for a function that's sent to @fastify/auth. However, when I do this, I get the following Typescript error:

Type '(request: FastifyRequest<{    Params: Partial<GetUserRequestParams>;}>, reply: FastifyReply) => Promise<void>' is not assignable to type 'FastifyAuthFunction | FastifyAuthFunction[]'.
  Type '(request: FastifyRequest<{    Params: Partial<GetUserRequestParams>;}>, reply: FastifyReply) => Promise<void>' is not assignable to type 'FastifyAuthFunction'.
    Types of parameters 'request' and 'request' are incompatible.
      Type 'FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>' is not assignable to type 'FastifyRequest<{ Params: Partial<{ userId: string; }>; }, RawServerDefault, IncomingMessage, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>'.
        Type 'RouteGenericInterface' is not assignable to type '{ Params: Partial<{ userId: string; }>; }'.
          Types of property 'Params' are incompatible.
            Type 'unknown' is not assignable to type 'Partial<{ userId: string; }>'.ts(2322)

I'm unsure if this is a bug or I'm doing this incorrectly.

Steps to Reproduce

Below is a snippet of what I'm doing:

import { 
  FastifyInstance,
  FastifyReply,
  FastifyRequest, 
} from 'fastify';
import { Static, Type } from '@sinclair/typebox';

const GetUserRequestParamsSchema = Type.Object({
  userId: Type.String(),
});

type GetUserRequestParams = Static<typeof GetUserRequestParamsSchema>;

const usersMutationAccessPolicy =
  (fastify: FastifyInstance) =>
  async (
    // Looking to extend Params so Typescript knows about `userId` (below)
    request: FastifyRequest<{
      Params: Partial<GetUserRequestParams>;
    }>,
    reply: FastifyReply,
  ): Promise<void> => {
    const { user } = request.identity;
    // Without extending request.params, it thinks the type is unknown
    const isOwner = user?.id === request.params.userId;

    // do more work below
  }

// Leaving out app startup code to keep this concise

export async function usersController(fastify: FastifyInstance): Promise<void> {
  fastify.patch<{
    Params: UserParams;
    Body: UserPatchBody;
  }>(
    `/:userId`,
    {
      schema: { ... },
      // Getting TS error 
      onRequest: fastify.auth([usersMutationAccessPolicy(fastify)]),
    },
    async (req, res) => {
      const user = await doPatchStuffWithUser();
      res.send(user);
    },
  );
}

Something to note. If I just added usersMutationAccessPolicy(fastify) to the onRequest directly, everything works as expected. Example:

export async function usersController(fastify: FastifyInstance): Promise<void> {
  fastify.patch<{
    Params: UserParams;
    Body: UserPatchBody;
  }>(
    `/:userId`,
    {
      schema: { ... },
      // Works as expected 
      onRequest: usersMutationAccessPolicy(fastify),
    },
    async (req, res) => {
      const user = await doPatchStuffWithUser();
      res.send(user);
    },
  );
}

However, I'm looking to use @fastify/auth to compose more access policy functions.

Expected Behavior

I don't receive any TS error when using

onRequest: fastify.auth([usersMutationAccessPolicy(fastify)]), from above.

New parameter: run all

🚀 Feature Proposal

Add a new parameter to execute all the auth handler in any case.

Right now if the relation is or it will stop at the first success if it is and it will stop at the first error.

Motivation

The user may want to get all the info from the request and act accordingly in the setErrorHandler function.
In my case, I have a request with many tokens and I need to parse them all

Example

fastify.route({
      method: 'POST',
      url: '/auth-multiple',
      preHandler: fastify.auth([
        fastify.verifyAdmin,
        fastify.verifyReputation
      ], {
        relation: 'and',
        run: 'all'
      }),
      handler: (req, reply) => {
        req.log.info('Auth route')
        reply.send({ hello: 'world' })
      }
})

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.