Code Monkey home page Code Monkey logo

fastify-passport's Introduction

@fastify/passport

CI NPM version code style: prettier

@fastify/passport is a port of passport for the Fastify ecosystem. It lets you use Passport strategies to authenticate requests and protect Fastify routes!

Status

Beta. @fastify/passport is still a relatively new project. There may be incompatibilities with express-based passport deployments, and bugs. Please report any issues so we can correct them!

Installation

npm i @fastify/passport

Google OAuth2 Video tutorial

The community created this fast introduction to @fastify/passport: Google OAuth2 Tutorial Passport

Example

import fastifyPassport from '@fastify/passport'
import fastifySecureSession from '@fastify/secure-session'

const server = fastify()
// set up secure sessions for @fastify/passport to store data in
server.register(fastifySecureSession, { key: fs.readFileSync(path.join(__dirname, 'secret-key')) })
// initialize @fastify/passport and connect it to the secure-session storage. Note: both of these plugins are mandatory.
server.register(fastifyPassport.initialize())
server.register(fastifyPassport.secureSession())

// register an example strategy for fastifyPassport to authenticate users using
fastifyPassport.use('test', new SomePassportStrategy()) // you'd probably use some passport strategy from npm here

// Add an authentication for a route which will use the strategy named "test" to protect the route
server.get(
  '/',
  { preValidation: fastifyPassport.authenticate('test', { authInfo: false }) },
  async () => 'hello world!'
)

// Add an authentication for a route which will use the strategy named "test" to protect the route, and redirect on success to a particular other route.
server.post(
  '/login',
  { preValidation: fastifyPassport.authenticate('test', { successRedirect: '/', authInfo: false }) },
  () => {}
)

server.listen()

Alternatively, @fastify/session is also supported and works out of the box for session storage.
Here's an example:

import { Authenticator } from '@fastify/passport'
import fastifyCookie from '@fastify/cookie'
import fastifySession from '@fastify/session'

const server = fastify()

// setup an Authenticator instance which uses @fastify/session
const fastifyPassport = new Authenticator()

server.register(fastifyCookie)
server.register(fastifySession, { secret: 'secret with minimum length of 32 characters' })

// initialize @fastify/passport and connect it to the secure-session storage. Note: both of these plugins are mandatory.
server.register(fastifyPassport.initialize())
server.register(fastifyPassport.secureSession())

// register an example strategy for fastifyPassport to authenticate users using
fastifyPassport.use('test', new SomePassportStrategy()) // you'd probably use some passport strategy from npm here

Session cleanup on logIn

For security reasons the session is cleaned after login. You can manage this configuration at your own risk by:

  1. Include keepSessionInfo true option when perform the passport .authenticate call;
  2. Include keepSessionInfo true option when perform the request .login call;
  3. Using clearSessionOnLogin (default: true) and clearSessionIgnoreFields (default: ['passport', 'session']).

Difference between @fastify/secure-session and @fastify/session

@fastify/secure-session and @fastify/session are both session plugins for Fastify which are capable of encrypting/decrypting the session. The main difference is that @fastify/secure-session uses the stateless approach and stores the whole session in an encrypted cookie whereas @fastify/session uses the stateful approach for sessions and stores them in a session store.

Session Serialization

In a typical web application, the credentials used to authenticate a user will only be transmitted once when a user logs in, and after, they are considered logged in because of some data stored in their session. @fastify/passport implements this pattern by storing sessions using @fastify/secure-session, and serializing/deserializing user objects to and from the session referenced by the cookie. @fastify/passport cannot store rich object classes in the session, only JSON objects, so you must register a serializer / deserializer pair if you want to say fetch a User object from your database, and store only a user ID in the session.

// register a serializer that stores the user object's id in the session ...
fastifyPassport.registerUserSerializer(async (user, request) => user.id);

// ... and then a deserializer that will fetch that user from the database when a request with an id in the session arrives
fastifyPassport.registerUserDeserializer(async (id, request) => {
  return await User.findById(id);
});

API

initialize()

A hook that must be added. Sets up a @fastify/passport instance's hooks.

secureSession()

A hook that must be added. Sets up @fastify/passport's connector with @fastify/secure-session to store authentication in the session.

authenticate(strategy: string | Strategy | (string | Strategy)[], options: AuthenticateOptions, callback?: AuthenticateCallback)

Returns a hook that authenticates requests, in other words, validates users and then signs them in. authenticate is intended for use as a preValidation hook on a particular route like /login.

Applies the given strategy (or strategies) to the incoming request, in order to authenticate the request. Strategies are usually registered ahead of time using .use, and then passed to .authenticate by name. If authentication is successful, the user will be logged in and populated at request.user and a session will be established by default. If authentication fails, an unauthorized response will be sent.

Strategies or arrays of strategies can also be passed as instances. This is useful when using a temporary strategy you only intend to use once for one user and don't want to register into the global list of available strategies.

Options:

  • session Save login state in session, defaults to true
  • successRedirect After successful login, redirect to given URL
  • successMessage True to store success message in req.session.messages, or a string to use as override message for success.
  • successFlash True to flash success messages or a string to use as a flash message for success (overrides any from the strategy itself).
  • failureRedirect After failed login, redirect to given URL
  • failureMessage True to store failure message in req.session.messages, or a string to use as override message for failure.
  • failureFlash True to flash failure messages or a string to use as a flash message for failures (overrides any from the strategy itself).
  • assignProperty Assign the object provided by the verify callback to given property
  • state Pass any provided state through to the strategy (e.g. for Google Oauth)
  • keepSessionInfo True to save existing session properties after authentication

An optional callback can be supplied to allow the application to override the default manner in which authentication attempts are handled. The callback has the following signature:

(request, reply, err | null, user | false, info?, (status | statuses)?) => Promise<void>

where request and reply will be set to the original FastifyRequest and FastifyReply objects, and err will be set to null in case of a success or an Error object in case of a failure. If err is not null then user, info and status objects will be undefined. The user object will be set to the authenticated user on a successful authentication attempt, or false otherwise.

An optional info argument will be passed, containing additional details provided by the strategy's verify callback - this could be information about a successful authentication or a challenge message for a failed authentication.

An optional status or statuses argument will be passed when authentication fails - this could be a HTTP response code for a remote authentication failure or similar.

fastify.get(
  '/',
  { preValidation: fastifyPassport.authenticate('test', { authInfo: false }) },
  async (request, reply, err, user, info, status) => {
    if (err !== null) {
      console.warn(err)
    } else if (user) {
      console.log(`Hello ${user.name}!`)
    }
  }
)

Examples:

// create a request handler that uses the facebook strategy
fastifyPassport.use(new FacebookStrategy('facebook', {
  // options for the facebook strategy, see https://www.npmjs.com/package/passport-facebook
})))
fastifyPassport.authenticate('facebook');

// create a request handler to test against the strategy named local, and automatically redirect when it succeeds or fails
fastifyPassport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' });

// create a request handler that won't use any user information stored in the secure session
fastifyPassport.authenticate('basic', { session: false });

Note that if a callback is supplied, it becomes the application's responsibility to log-in the user, establish a session, and otherwise perform the desired operations.

Multiple Strategies

@fastify/passport supports authenticating with a list of strategies, and will try each in order until one passes. Pass an array of strategy names to authenticate for this:

// somewhere before several strategies are registered
fastifyPassport.use('bearer', new BearerTokenStrategy())
fastifyPassport.use('basic', new BasicAuthStrategy())
fastifyPassport.use('google', new FancyGoogleStrategy())

// and then an `authenticate` call can test incoming requests against multiple strategies
fastify.get(
  '/',
  { preValidation: fastifyPassport.authenticate(['bearer', 'basic', 'google'], { authInfo: false }) },
  async (request, reply, err, user, info, status) => {
    if (err !== null) {
      console.warn(err)
    } else if (user) {
      console.log(`Hello ${user.name}!`)
    }
  }
)

Note that multiple strategies that redirect to start an authentication flow, like OAuth2 strategies from major platforms, shouldn't really be used together in the same authenticate call. This is because @fastify/passport will run the strategies in order, and the first one that redirects will do so, preventing the user from ever using the other strategies. To set up multiple OAuth2 strategies, add several routes that each use a different strategy in their own authenticate call, and then direct users to the right route for the strategy they pick.

Multiple strategies can also be passed as instances if you only intend to use them for that route handler or for that request.

// use an `authenticate` call can test incoming requests against multiple strategies without registering them for use elsewhere
fastify.get(
  '/',
  {
    preValidation: fastifyPassport.authenticate([new BearerTokenStrategy(), new BasicAuthStrategy()], {
      authInfo: false,
    }),
  },
  async (request, reply, err, user, info, status) => {
    if (err !== null) {
      console.warn(err)
    } else if (user) {
      console.log(`Hello ${user.name}!`)
    }
  }
)

authorize(strategy: string | Strategy | (string | Strategy)[], options: AuthenticateOptions = {}, callback?: AuthenticateCallback)

Returns a hook that will authorize a third-party account using the given strategy, with optional options. Intended for use as a preValidation hook on any route. .authorize has the same API as .authenticate, but has one key difference: it doesn't modify the logged in user's details. Instead, if authorization is successful, the result provided by the strategy's verify callback will be assigned to request.account. The existing login session and request.user will be unaffected.

This function is particularly useful when connecting third-party accounts to the local account of a user that is currently authenticated.

Examples:

fastifyPassport.authorize('twitter-authz', { failureRedirect: '/account' })

.authorize allows the use of multiple strategies by passing an array of strategy names, and allows the use of already instantiated Strategy instances by passing the instance as the strategy, or an array of instances.

use(name?: string, strategy: Strategy)

Utilize the given strategy with optional name, overridding the strategy's default name.

Examples:

fastifyPassport.use(new TwitterStrategy(...));

fastifyPassport.use('api', new http.Strategy(...));

unuse(name: string)

Un-utilize the strategy with given name.

In typical applications, the necessary authentication strategies are static, configured once and always available. As such, there is often no need to invoke this function.

However, in certain situations, applications may need dynamically configure and de-configure authentication strategies. The use()/unuse() combination satisfies these scenarios.

Example:

fastifyPassport.unuse('legacy-api')

registerUserSerializer(serializer: (user, request) => Promise)

Registers an async user serializer function for taking a high level User object from your application and serializing it for storage into the session. @fastify/passport cannot store rich object classes in the session, only JSON objects, so you must register a serializer / deserializer pair if you want to say fetch a User object from your database, and store only a user ID in the session.

// register a serializer that stores the user object's id in the session ...
fastifyPassport.registerUserSerializer(async (user, request) => user.id)

registerUserDeserializer(deserializer: (serializedUser, request) => Promise)

Registers an async user deserializer function for taking a low level serialized user object (often just a user ID) from a session, and deserializing it from storage into the request context. @fastify/passport cannot store rich object classes in the session, only JSON objects, so you must register a serializer / deserializer pair if you want to say fetch a User object from your database, and store only a user ID in the session.

fastifyPassport.registerUserDeserializer(async (id, request) => {
  return await User.findById(id);
});

Deserializers can throw the string "pass" if they do not apply to the current session and the next deserializer should be tried. This is useful if you are using @fastify/passport to store two different kinds of user objects. An example:

// register a deserializer for database users
fastifyPassport.registerUserDeserializer(async (id, request) => {
  if (id.startsWith("db-")) {
    return await User.findById(id);
  } else {
    throw "pass"
  }
});

// register a deserializer for redis users
fastifyPassport.registerUserDeserializer(async (id, request) => {
  if (id.startsWith("redis-")) {
    return await redis.get(id);
  } else {
    throw "pass"
  }
});

Sessions may specify serialized users that have since been deleted from the datastore storing them for the application. In that case, deserialization often fails because the user row cannot be found for a given id. Depending on the application, this can either be an error condition, or expected if users are deleted from the database while logged in. @fastify/passport's behaviour in this case is configurable. Errors are thrown if a deserializer returns undefined, and the session is logged out if a deserializer returns null or false. This matches the behaviour of the original passport module.

Therefore, a deserializer can return several things:

  • if a deserializer returns an object, that object is assumed to be a successfully deserialized user
  • if a deserializer returns undefined, @fastify/passport interprets that as an erroneously missing user, and throws an error because the user could not be deserialized.
  • if a deserializer returns null or false, @fastify/passport interprets that as a missing but expected user, and resets the session to log the user out
  • if a deserializer throws the string "pass", @fastify/passport will try the next deserializer if it exists, or throw an error because the user could not be deserialized.

Request#isUnauthenticated()

Test if request is unauthenticated.

Using with TypeScript

@fastify/passport is written in TypeScript, so it includes type definitions for all of it's API. You can also strongly type the FastifyRequest.user property using TypeScript declaration merging. You must re-declare the PassportUser interface in the fastify module within your own code to add the properties you expect to be assigned by the strategy when authenticating:

declare module 'fastify' {
  interface PassportUser {
    id: string
  }
}

or, if you already have a type for the objects returned from all of the strategies, you can make PassportUser extend it:

import { User } from './my/types'

declare module 'fastify' {
  interface PassportUser extends User {}
}

Using multiple instances

@fastify/passport supports being registered multiple times in different plugin encapsulation contexts. This is useful to implement two separate authentication stacks. For example, you might have a set of strategies that authenticate users of your application, and a whole other set of strategies for authenticating staff members of your application that access an administration area. Users might be stored at request.user, and administrators at request.admin, and logging in as one should have no bearing on the other. It is important to register each instance of @fastify/passport in a different Fastify plugin context so that the decorators @fastify/passport like request.logIn and request.logOut do not collide.

To register @fastify/passport more than once, you must instantiate more copies with different keys and userPropertys so they do not collide when decorating your fastify instance or storing things in the session.

import { Authenticator } from '@fastify/passport'

const server = fastify()

// setup an Authenticator instance for users that stores the login result at `request.user`
const userPassport = new Authenticator({ key: 'users', userProperty: 'user' })
userPassport.use('some-strategy', new CoolOAuthStrategy('some-strategy'))
server.register(userPassport.initialize())
server.register(userPassport.secureSession())

// setup an Authenticator instance for users that stores the login result at `request.admin`
const adminPassport = new Authenticator({ key: 'admin', userProperty: 'admin' })
adminPassport.use('admin-google', new GoogleOAuth2Strategy('admin-google'))
server.register(adminPassport.initialize())
server.register(adminPassport.secureSession())

// protect some routes with the userPassport
server.get(
  `/`,
  { preValidation: userPassport.authenticate('some-strategy') },
  async () => `hello ${JSON.serialize(request.user)}!`
)

// and protect others with the adminPassport
server.get(
  `/admin`,
  { preValidation: adminPassport.authenticate('admin-google') },
  async () => `hello administrator ${JSON.serialize(request.admin)}!`
)

Note: Each Authenticator instance's initialize plugin and session plugin must be registered separately.

It is important to note that using multiple @fastify/passport instances is not necessary if you want to use multiple strategies to login the same type of user. @fastify/passport supports multiple strategies by passing an array to any .authenticate call.

Differences from Passport.js

@fastify/passport is an adapted version of Passport that tries to be as compatible as possible, but is an adapted version that has some incompatibilities. Passport strategies that adhere to the passport strategy API should work fine, but there are some differences in other APIs made to integrate better with Fastify and to stick with Fastify's theme of performance.

Differences:

  • serializeUser renamed to registerUserSerializer and always takes an async function with the signature (user: User, request: FastifyRequest) => Promise<SerializedUser>
  • deserializeUser renamed to registerUserDeserializer and always takes an async function with the signature (serialized: SerializedUser, request: FastifyRequest) => Promise<User>
  • transformAuthInfo renamed to registerAuthInfoTransformer and always takes an async function with the signature (info: any, request: FastifyRequest) => Promise<any>
  • .authenticate and .authorize accept strategy instances in addition to strategy names. This allows for using one time strategy instances (say for testing given user credentials) without adding them to the global list of registered strategies.

License

MIT

fastify-passport's People

Contributors

airhorns avatar climba03003 avatar delvedor avatar dependabot[bot] avatar fdawgs avatar fox1t avatar github-actions[bot] avatar is2ei avatar jcbain avatar jonaskello avatar jsumners avatar kukidon-dev avatar l2jliga avatar louisnk avatar lucianovirmes avatar mcollina avatar mgcrea avatar mmahkamov avatar philipwee avatar rafaelgss avatar reilem avatar salmanm avatar sameer-coder avatar serayaeryn avatar simoneb avatar uzlopak avatar wojcikt 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-passport's Issues

Socket IO middleware Support

Prerequisites

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

๐Ÿš€ Feature Proposal

Right now fastifyPassport registers as a fastify plugin, but it is incompatible with Socket IO middleware

I don't mind helping to implement this feature

Motivation

I don't want to have to register the passport strategies twice for socket IO and fastify passport

Example

Assuming you are using fastify socket io:
`
server.register(fastifySecureSession, { key: fs.readFileSync(path.join(__dirname, 'secret-key')) })
// initialize fastify-passport and connect it to the secure-session storage. Note: both of these plugins are mandatory.
server.register(fastifyPassport.initialize())
server.register(fastifyPassport.secureSession())

server.io.use(fastifyPassport.io.initialize())
server.io.use(fastifyPassport.io.secureSession())
`

passport-github verify callback never called?

Prerequisites

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

Issue

Use Case

  • An app that will make calls to the GitHub API on behalf of user (e.g. git commits, gists, etc.)
  • I want to use the GitHub strategy, both for AutheNtication (who is user?) and AuthoriZation (which repos belonging to user does app have access to?).

Problem

  • AuthN layer works as in a user is redirected to login if they are not logged in.
  • But I cannot figure out how to get the access token or user profile

Example Code

Here is a reduced code sample. In my testing, the console.log(profile) is never called in Fastify. The same setup and code works in Express.

The /protected route will redirect to GitHub, which will redirect to the /auth/github/callback route, but the verify callback is never called :-(

Getting Access Tokens

Per GitHub Docs I can take the code from the callback URL, e.g. http://127.0.0.1:3000/auth/github/callback?code=12fxxxxxx to fetch the access and refresh tokens. But I don't want to if passport can do it for me.

I did rush this this morning. So maybe I am doing it wrong?

const PORT = process.env.port || 3000

const fastify = require('fastify')({ logger: true })
const fastifyPassport = require('fastify-passport')
const fastifySecureSession = require('fastify-secure-session')

const GitHubStrategy = require('passport-github').Strategy
const strategyOpts = {
  clientID: process.env.GITHUB_CLIENT_ID,
  clientSecret: process.env.GITHUB_CLIENT_SECRET,
  callbackURL: 'http://127.0.0.1:3000/auth/github/callback'
}

const notForProductionKey = 'no-encryption-key-for-local-dev-min-32-bytes'
fastify.register(fastifySecureSession, { key: notForProductionKey })
fastify.register(fastifyPassport.initialize())
fastify.register(fastifyPassport.secureSession())

fastifyPassport.use('github', new GitHubStrategy(strategyOpts, function (accessToken, refreshToken, profile, cb) {
  console.log('**** Is this ever called??? *******') // <-- NEVER called in Fastify โ‰๏ธ
  console.log(profile) // <-- for testing only
  return cb(null, profile)
}
))


/**
 * Routes
 */

fastify.get('/', function (request, reply) {
  reply.send({
    hello: 'world',
    protected: false
  })
})

fastify.get('/protected', { preValidation: fastifyPassport.authenticate('github', { authInfo: false }) }, async (request, reply, err, user, info, status) => {
  if (err !== null) {
    console.warn(err)
  } else if (user) {
    console.log(`Hello ${user.name}!`)
    reply.send(user)
  }

  reply.send({
    hello: 'protected',
    protected: true
  })
})

fastify.get('/auth/github/callback', (req, reply) => {
  // reply.redirect('/')
  reply.send('called back')
})


/**
 * Run the Server
 */
fastify.listen(PORT, function (err, address) {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
})

Calling Strategy.fail with 2 arguments is misinterpreted by the compiler

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.2.1

Plugin version

2.1.0

Node.js version

16.4.2

Operating system

Windows

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

10

Description

Here's the declaration of the fail() method in the base Strategy class: https://github.com/fastify/fastify-passport/blob/main/src/strategies/base.ts#L55

fail!: ((challenge?: any, status?: number) => void) | ((status?: number) => void)

When calling with 1 number argument, there's no problem:

this.fail(403);

But TypeScript is failing to find the variant with 2 arguments:

return this.fail({
  message: request.query['error_description'],
}, 400);

getting an error:

TS2554: Expected 0-1 arguments, but got 2

Steps to Reproduce

  1. Create a custom strategy subclassed from the Strategy class of @fastify/passport
  2. Call this.fail(null, 401)

Expected Behavior

The code should compile when calling Strategy.fail() with 2 arguments.

Flash plugin requires a valid session error

I'm trying to use this plugin with NestJS. My session works fine.

// main.ts
await app.register(fastifySecureSession, {...})

// auth.controller.ts
login(@Req() req): any {
  console.log(!!req.session) // print true
  return true
}

But after adding passport I get error "Flash plugin requires a valid session." https://github.com/fastify/fastify-flash/blob/master/src/index.ts#L12

await app.register(fastifySecureSession, {...})
await app.register(fastifyPassport.initialize())
await app.register(fastifyPassport.secureSession())

How I can fix it?

The second question: is it possible to make fastify-flash optional? In my project I don't need any flash.

Migrate to `node:test` and `c8`

Prerequisites

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

Issue

No response

Local Strategy bad 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

3

Plugin version

0.4.3

Node.js version

14

Operating system

Linux

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

20

Description

Hi, I trying to use LocalStrategy.
I succeeded to do login but the other request return a Bad Request.

Steps to Reproduce

module.exports = fp(async function (fastify, opts) {
    fastify.register(fastifySecureSession, {
        key: fs.readFileSync(path.join(__dirname, '../secret-key')),
    })
    fastify.register(fastifyPassport.initialize())
    fastify.register(fastifyPassport.secureSession())

    fastifyPassport.use("local", new LocalStrategy(
        function (username, password, done) {
            User.findOne({email: username}, function (err, user) {
                if (err) {
                    return done(err);
                }
                if (!user) {
                    return done(null, false);
                }
                if (!user.comparePassword(password)) {
                    return done(null, false);
                }
                return done(null, user);
            });
        }
    ));
    // register a serializer that stores the user object's id in the session ...
    fastifyPassport.registerUserSerializer(async (user, request) => user.id);

    // ... and then a deserializer that will fetch that user from the database when a request with an id in the session arrives
    fastifyPassport.registerUserDeserializer(async (id, request,) =>{
        return await User.findById(id);
    });

})

and this is my route

  // always return Bad Request
    fastify.get('/auth', {preValidation:  fastifyPassport.authenticate('local',{ successFlash: 'Welcome!' })},
        async (req) => {
            return {
                "title": 'hello world!',
                user: req.user

            }
        }
    )
    
    //work good!
    fastify.post(
        '/login',
        {
            preValidation: fastifyPassport.authenticate('local', {
                successRedirect: "/",
                failureRedirect: "/login",
                failureFlash: true,
            })
        },
        () => {
            return {hello: "world"}
        }
    )

Expected Behavior

Expected to get the user and not Bad Request

`state` for passport causes `fastify-passport` to fail

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.22.1

Plugin version

0.4.3

Node.js version

16.x

Operating system

macOS

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

macOS Big Sure 11.3.1

Description

When I am trying to pass some encoded state to the strategy the callback URLs shows the user as unauthenticated, it's also unclear how I could pass the encoded state from the redirect endpoint dynamically.

Steps to Reproduce

fastifyPassport.use(
    "github",
    new Github.Strategy(
      {
        clientID: "xxx",
        clientSecret: "xxx",
        callbackURL: "http://lvh.me:3000/callback",
        scope: ["user:email", "user,user:email"],
        userProfileURL: "https://api.github.com/user",
        state: Buffer.from(JSON.stringify({ returnTo: "/mypage" })).toString(
          "base64"
        ),
        passReqToCallback: true,
      },
      function (req, accessToken, refreshToken, profile, done) {
        done(null, { ...profile, accessToken, query });
      }
    )
  );

server.get(
    "/github",
    {
      preValidation: fastifyPassport.authenticate("github", {
        successRedirect: "/callback",
        authInfo: false,
        assignProperty: "user",
      }),
    },
    () => {}
  );
  
  server.get(
  "/callback",
  {
    preValidation: fastifyPassport.authenticate("github", {
      authInfo: false,
      session: false,
    }),
  },
  async (request, reply, err, user = {}, info = {}, status = {}) => {
    reply.send({ user: request.user || {} });
  }
);

Expected Behavior

Step through the prevalidation hook and return the encoded state to the endpoint.

Support @fastify/session out of the box

Prerequisites

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

๐Ÿš€ Feature Proposal

As https://github.com/fastify/session became part of the org, we should list it here as one of the default session manager. I think it would just work, but it still needs tests and docs.

Motivation

No response

Example

No response

Authenticate callback function does not work

Prerequisites

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

Fastify version

3.19.2

Plugin version

0.4.3

Node.js version

16.2.0

Operating system

Windows

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

10

Description

Callback in fastifyPassport.authenticate function does not trigger if authorization is incorrect, and has only request, reply parameters in case of success authorization.

Steps to Reproduce

import fastify from "fastify";
import fastifySecureSession from "fastify-secure-session";
import fastifyPassport from "fastify-passport";
import LocalStrategy from "passport-local";

fastify.register(fastifySecureSession, {
 cookieName: "ses",
 key: "464d01eddfc334cfebb5d69612554e1b270a7b1493213d84af986db586692f07",
 cookie: { path: "/" },
});
fastify.register(fastifyPassport.initialize());
fastify.register(fastifyPassport.secureSession());

fastifyPassport.use( "local", new LocalStrategy(
   { usernameField: "email", passwordField: "password" },
   (username, password, done) => { done(null, false) },
 ),
);

fastify.post(
 "/login",
 { preValidation: fastifyPassport.authenticate("local", { authInfo: false, session: false })},
 async (request, reply, err, user, info, status) => { reply.send({ hello: "world" }) },
);

Expected Behavior

The callback function is triggered on any request

`state` param is not included in the Typescript interface for AuthenticateOptions

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.x

Plugin version

No response

Node.js version

18.x

Operating system

Linux

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

Ubuntu 22.04

Description

This is a Typescript bug.

When using the Passport Google Oauth2 package to implement a Google auth strategy, we are unable to pass the state property though the passport.authenticate options object.

This works code-wise, but does not compile because the interface definition for AuthenticateOptions does not include state.

Working non-TS example here

This is basically an extension of #376 that only appears when devs have the misfortune of using typescript.

Steps to Reproduce

Example code:

const preValidation = async (
    request: FastifyRequest<{
      Querystring: {
        state?: string
      }
    }>,
    reply: FastifyReply,
  ): Promise<void> => {
   const state: string = request.query?.state || ""

  // return passport.authenticate("google", { state, assignProperty: 'state' })(request, reply)
  return passport.authenticate("google", { state, assignProperty: 'state' }, (() => null))(request, reply)
}
...
fastify.get("/auth/google", { preValidation }, async () => "never returns anywhere")

When swapping the commented out lines we get varying errors that state is not defined on the interface for either the SingleStrategyCallback or AuthenticateOptions.

Example: Object literal may only specify known properties, and 'state' does not exist in type 'AuthenticateOptions'.

Expected Behavior

A Typescript developer can pass a state property through the AuthenticateOptions object without error.

using fastify-passport with passport-jwt strategy

Prerequisites

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

Issue

I am using fastify-passport so that I can use many strategies available in passport.js ecosystem. I started to use JWT authentication using passport-jwt and I have a problem wiring things up. Looking at the fastify-passport documentation and passport-jwt documentation in above link has confused me.

So, I have wired up the fastify server as per your documentation

  const fs = require('fs')
  const path = require('path')
  const jwtStrategy = require('passport-jwt').Strategy // installed passport-jwt from npm
  const extractJwt = require('passport-jwt').ExtractJwt; //this is needed according to passport-jwt documentation

then I setup the options for passport-jwt

  var opts = {
    jwtFromRequest : extractJwt.fromAuthHeaderAsBearerToken(),
    secretOrKey : 'secret',
    issuer : 'accounts.examplesoft.com',
    audience : 'yoursite.net',
 }

then register the plugins as mentioned in fastify-passport documentation

  await server.register(fastifySecureSession, { key: fs.readFileSync('./secret-key') })
  await server.register(fastifyPassport.initialize());
  await server.register(fastifyPassport.secureSession())

Then I setup the jwt strategy like below

[Question - 1] - Is this call back needed (as per passport-jwt documentation?)

  fastifyPassport.use('jwt',new jwtStrategy(opts,function(jwt_payload: any, done: any) {
        console.log("test")
  }))

Finally in my route I have;

[Question - 2] - Is below call back the proper place to validate the jwt and authenticate user against database?

  {
        method: 'POST',
        url: '/users',
        schema: postUserSchema,
        handler: createUser(userRepository),
        preValidation: fastifyPassport.authenticate('jwt', { successRedirect: '/', authInfo: false },  async (request, reply, err, user, info, status) => {
        if (err !== null) {
          console.warn(err)
        } else if (user) {
          console.log(`Hello`)
        }
      }
    )}

There is no error and if I put a break point to the call back in my route, it hits the breakpoint and the [info] object says jwt malformed which is ok because I just send some garbage as bearer data.

My question is out of the two places (mentioned in Question -1 & Question -2) what is the correct place I should use to do the user authentication/validation.

Consider releasing to NPM or adding precompiled dist or removing package.json "files" property

First off: Great work! Been waiting for something like this and can't wait for the first release.

๐Ÿš€ Feature Proposal

I am currently testing this package locally with NestJS and it is working great so far. However, it is kind of cumbersome to add this package as a dependency. It is currently not released to NPM and the "files" property in the package.json would require a precompiled "dist" directory to install this package directly via npm as a github dependency (it currently is just installing an empty directory with the readme and package.json as there is no "dist" directory).

We could handle this in one of three ways:

  • Release to NPM
  • Add precompiled dist to the repository
  • Remove "files" property from package.json (which would require the consumer to compile it first)

Motivation

To be able to test this dependency more comfortably by just installing it as a dependency.

Error: passport.initialize() plugin not in use

Prerequisites

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

Fastify version

3.19.1

Plugin version

0.4.2

Node.js version

16.4.2

Operating system

macOS

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

11.4

Description

The error location:
node_modules/fastify-passport/dist/AuthenticationRoute.js:18:23

I made the setup for fastifyPassport like this:

  await app.register(fastifyHelmet);

  await app.register(fastifySecureSession, {
    key: fs.readFileSync(path.join(__dirname, '../secret.key')),
  });
  await app.register(fastifyPassport.initialize());
  await app.register(fastifyPassport.secureSession());

  await app.listen(3000);

And i tried the passport-google-oauth20 strategy:

import { Strategy as GoogleOAuth2Strategy } from 'passport-google-oauth20';

.
.
.

fastifyPassport.use(
    'google',
    new GoogleOAuth2Strategy(googleConfig.strategy, this.validate.bind(this)),
  );

But when i use the middleware

    fastifyPassport.authenticate('google', {
          scope: googleConfig.scope,
        })

It fails.

Steps to Reproduce

Setup the passport, add the passport-google-oauth20 and try the redirect middleware to google's login page.

Expected Behavior

To redirect to the google's login page.

passport-auth0 provider callback route not triggered

Prerequisites

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

Fastify version

^3.18.1

Plugin version

No response

Node.js version

16.3.0

Operating system

Linux

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

Ubuntu 20.04

Description

Using passport-auth0 things seem to work fine until it comes to the callback route, the callback route is not triggered, in the browser when checking the console, errors below show:

message: "Timeout after calling success log."
description: "Should have closed the web view."

Steps to Reproduce

For this project I set up an auth0 regular web application, then I followed the passport-auth0 and fastify-passport docs to set up the provider, when I go to the auth0 route, I can sign in and in the console I see a success message with an authResult object, however after a while a white page is shown with console errors as shown in this issue description

fastifyPassport.use(
  new Auth0Strategy(
    {
      domain: process.env.AUTH0_DOMAIN,
      clientID: process.env.AUTH0_CLIENT_ID,
      clientSecret: process.env.AUTH0_CLIENT_SECRET,
      callbackURL: `${process.env.SERVER_URL}/auth/auth0/callback`,
    },
    (_accessToken, _refreshToken, _extraParams, profile, done) => {
      return done(undefined, profile)
    }
  )
)

fastify.get(
  "/auth0",
  {
    preValidation: fastifyPassport.authenticate("auth0", {
      scope: "openid email profile",
    }),
  },
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  async () => {}
)

fastify.get(
  "/auth0/callback",
  {
    preValidation: fastifyPassport.authenticate("auth0"),
  },
  async (_request, reply) => {
    return reply.send(JSON.stringify(_request.user, undefined, 2))
  }
)

Expected Behavior

I would expect to be redirected to the callback route

Handler does not seem to be called

Prerequisites

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

Fastify version

3.19.1

Plugin version

0.4.2

Node.js version

16.5.0

Operating system

Linux

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

20.04

Description

At my company, we decide to move from expressjs to Fastify.

With express we use Passport for auth our users, but now with Fastify is little bit more compliced...
It why I try this plugin, but event with this plugin I can't have a successfull login.

I try to login with Google oauth2, but I never get the user on the "auth google callback" (and no passport callback fired too).

Steps to Reproduce

function getInGoogleC (req, res, err, user, status) {
  console.log('FastifyPassport Callback of GoogleStrategy Passport fired...');
  console.log('err:', err);
  console.log('user:', user);
  console.log('status:', status);

  console.log('getInGoogle finish...');
}

function getCallbackGoogleC (req, res) {
  res.redirect(`${APP_URL + AUTHENTICATION_BACK_PATH}?u=${encodeURIComponent(JSON.stringify(req.user))}`)
}

const defRoutes = [
    {
      method: 'GET',
      url: `/get-in/google`,
      preValidation: passport.authenticate('google', { session: true, scope: ['profile', 'email'] }),
      handler: getInGoogleC              // Never fired -_-
    },
    {
      method: 'GET',
      url: `/auth/google/callback`,
      handler: getCallbackGoogleC  // no data into user decorator -_-
    }
]

Expected Behavior

With expressjs and Passport we get user infos

`Forbidden` error after getting authorization code from okta using `passport-okta-oauth` library as a strategy

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.25.2

Plugin version

No response

Node.js version

16.13.1

Operating system

macOS

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

11.6.1

Description

I am using OKTA as a authenticator in my project.In fastify-passport, I used the passport-okta-oauth library as an strategy. After user succesfully logins with okta.The okta provides authorization code to my callback URL.After receiving code,fastify-passport have to send the post request to the OKTA with code and client_secret as parameter. But in my case this post request is not hitting instead of this its showing forbidden error.

Screenshot 2021-12-24 at 4 10 27 PM

Steps to Reproduce

const fastify = require("fastify")({ logger: true });
const fastifyPassport = require("fastify-passport");
const oktaStrategy = require("passport-okta-oauth").Strategy;
const fastifySecureSession = require("fastify-secure-session");
const fs = require("fs");
const path = require("path");

fastify.register(fastifySecureSession, {
  key: fs.readFileSync(path.join(__dirname, "not-so-secret-key.txt")),
});
fastify.register(fastifyPassport.initialize());
fastify.register(fastifyPassport.secureSession());

fastifyPassport.use(
  "okta",
  new oktaStrategy(
    {
      audience: "YOUR_AUDIENCE_URL",
      clientID: "CLIENT_ID",
      clientSecret: "CLIENT_SECRET",
      scope: ["openid", "email", "profile"],
      response_type: "code",
      callbackURL: "http://localhost:3000/auth/okta/callback",
    },
    function (accessToken, refreshToken, profile, done) {
      console.log("I am in OKTA");
      done(undefined, profile);
      return profile;
    }
  )
);

fastify.get("/", (req, res) => res.send("hii"));
fastify.get("/success", (req, res) => res.send("Success OKTA"));
fastify.get("/failure", (req, res) => res.send("Okta Oauth failure"));

fastify.get(
  "/okta",
  {
    preValidation: fastifyPassport.authenticate("okta", {
      failureRedirect: "/failure",
      session: "false",
    }),
  },
  (req, res) => {}
);
fastify.get(
  "/auth/okta/callback",
  { preValidation: fastifyPassport.authenticate("okta") },
  (req, res) => {
    res.redirect("/success");
  }
);

fastify.listen(3000);

Expected Behavior

After receiving the code from okta, the fastify-passport have to hit the post request and user details will receive in callback function

the user property remains occupied by the type when userProperty is changed

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.5.2

Plugin version

2.2.0

Node.js version

18.12.1

Operating system

Linux

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

20.04

Description

The user property remains occupied by the type PassportUser when userProperty is changed

import { Authenticator } from "@fastify/passport";
const fastifyPassport = new Authenticator({
    userProperty: "authUser",
});
//...
const userGroup = await Group.findOne({
    where: {
          name: req.user?.group,
     },
});

image

image

Steps to Reproduce

import { Authenticator } from "@fastify/passport";
const fastifyPassport = new Authenticator({
    userProperty: "authUser",
});
//...
const userGroup = await Group.findOne({
    where: {
          name: req.user?.group,
     },
});

Expected Behavior

The property user should not have remained a busy type for further use

Let's talk about, goals, features and porting quirks

Hi, I am in the process of porting passport to Fastify. The goal of this project is to have 100% compatibility with all the passport strategies already developed.
I just finished porting the code 1:1 to TypeScript and now I am going to rewrite some parts that are not suitable anymore:

  1. original project doesn't use strict mode
  2. almost all types are wrong
  3. some parts could be optimized and speed up

There are some part of the code that need to be approached carefully and I'll use this thread to talk about them. First of all, there are two folders that need our attention, framework and middleware. These folders contains all the implementation that is bound to connect middleware, used by passport. As we can see on this.framework(connect()), in the authenticator module, this, so called "framework", is pluggable using framework() method. All the Fastify's specific implementation must be done inside a "compatible framework".
Unfortunately there are some issues.

  1. Let's start with req.flash. Passport uses flash messages to notify success or failure during authentication process (line 173 and 281). Are we gonna support this?
  2. Passport's connect framework monkey patch http.IncomingMessage just to add some helpers methods, as logIn and authenticate. I really do not want to do that and, instead, I want to add it to Fastify's request object. However we need to be sure that all strategies will still work.
  3. Speaking of that, SessionStrategy, the only included with passport, support pausing IncomingMessage, if options.pauseStream is passed. This works because req is a express.Request, that is extended from IncomingMessage. This is, of course, a problem for Fastify's request object.

fastifyPassport.initialize is not a function

๐Ÿ› Bug Report

A clear and concise description of what the bug is.

To Reproduce

Steps to reproduce the behavior:

// Paste your code here
const fastify = require('fastify')();
const fastifyPassport= require('fastify-passport')
const fastifySecureSession= require('fastify-secure-session')
// set up secure sessions for fastify-passport to store data in
fastify.register(fastifySecureSession, { key: fs.readFileSync(path.join(__dirname, "secret-key")) });
// initialize fastify-passport and connect it to the secure-session storage. Note: both of these plugins are mandatory.
fastify.register(fastifyPassport.initialize());
fastify.register(fastifyPassport.secureSession());
console.log(fastifyPassport);

Error FastifyPassport.initialize is not a function

image

Expected behavior

A clear and concise description of what you expected to happen.

fastifyPassport.initialize should initialize passport
also on same note when someone console `fastifyPassport`
below appears
{
  Strategy: [Getter],
  default: Authenticator {
    key: 'passport',
    userProperty: 'user',
    strategies: { session: [SessionStrategy] },
    serializers: [],
    deserializers: [],
    infoTransformers: [],
    sessionManager: SecureSessionManager {
      key: 'passport',
      serializeUser: [Function: bound serializeUser] AsyncFunction
    }
  }
}

Your Environment

  • node version: v14.15.1
  • fastify version: >=^3.9.1
  • os: windows-wsl-ubuntu

Incomplete docs for "authenticate" function

๐Ÿ› Bug Report

(Sorry if this is not the correct issue type)

The authenticate function has an optional callback parameter, but the signature is not provided. Maybe I am not looking carefully, but the docs say "The callback has the following signature", but I can't seem to see the signature anywhere.

I had to look in the source code to find out what was going wrong, and only then realised that the user is given as 4th parameter in the callback and is not available in the request object.

Just adding something like this to the docs would've made my life a lot easier 30 minutes ago ๐Ÿ˜…

Callback: (request, reply, err, user?, info?, status | statuses?) => Promise<void>

Let me know if there is any way I can help.

Infinite authentication loop on protected route

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.27.0

Plugin version

2.4.0

Node.js version

20.11.1

Operating system

macOS

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

14.4.1

Description

Hello. I am trying to use passport-microsoft and my issue is that after I have been authenticated and the callback URL has been reached and I am redirected to the protected route '/', I get prompted to log in again, this happens over and over again.

Here's an MRE:

"use strict";

const { readFileSync } = require("fs");
const { join } = require("path");

const fastify = require("fastify")({ logger: true });

const passport = require("@fastify/passport");
const secureSession = require("@fastify/secure-session");
const { Strategy: MicrosoftStrategy } = require("passport-microsoft");

const PORT = 3000;
const BASE_URL = "http://localhost";
const CLIENT_ID = "...";
const CLIENT_SECRET = "...";
const CALLBACK = `${BASE_URL}:${PORT}/microsoft/callback`;

// --- plugins (from the Fastify ecosystem) ---
fastify.register(secureSession, {
  key: readFileSync(join(__dirname, "secret-key")),
});
fastify.register(passport.initialize());
fastify.register(passport.secureSession());

passport.use(
  "microsoft",
  new MicrosoftStrategy(
    {
      clientID: CLIENT_ID,
      clientSecret: CLIENT_SECRET,
      callbackURL: CALLBACK,
      scope: ["user.read"],
    },
    function (accessToken, refreshToken, profile, done) {
      done(null, profile);
    }
  )
);
passport.registerUserSerializer(async (user) => user.id);
passport.registerUserDeserializer(async (user) => user);

// --- routes                               ---
const defRoutes = [
  {
    method: "GET",
    url: `/auth/login`,
    preValidation: passport.authenticate("microsoft", {
      authInfo: false,
    }),
    handler: (req, res, err, user, status) => {},
  },
  {
    method: "GET",
    url: `/microsoft/callback`,
    preValidation: passport.authenticate("microsoft", {
      authInfo: false,
      successRedirect: "/",
    }),
    handler: (req, res) => {},
  },
  {
    method: "GET",
    url: `/`,
    preValidation: passport.authenticate("microsoft", { authInfo: false }),
    handler: (req, res) => {
      return res.send(req.user);
    },
  },
];

// Add all routes into Fastify route system
for (const route of defRoutes) {
  fastify.route(route);
}

async function start() {
  try {
    fastify.listen({ port: PORT });
  } catch (e) {
    throw e;
  }
}

start();

I am not sure if I am missing anything or what the issue is.

Link to code that reproduces the bug

No response

Expected Behavior

I am not prompted to log in again when redirected to the '/' route

Use session or not as an option

Prerequisites

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

๐Ÿš€ Feature Proposal

  1. I am writing a hasura side project, in this application scenario, the auth server only provides the remote schema and the necessary redirect url, the user does not have the opportunity to save or use the session, all the authentication will be done through jwt
  2. passport itself supports "disable session"

Based on the above two points, I would like to make it a choice whether to use session or not
but๏ผŒfor now, If I don't register โ€˜fastify-secure-sessionโ€™

2022-01-03T06:19:44.289Z *9884 @ElvisMini.local - error: #req-1 Flash plugin requires a valid session.
                         ร—Error 500: Flash plugin requires a valid session.
    at Object.<anonymous> (/Volumes/Root/Users/Elvis/WIP/SeaLight/server/node_modules/.pnpm/[email protected]/node_modules/fastify-flash/lib/index.js:13:18)
    at hookIterator (/Volumes/Root/Users/Elvis/WIP/SeaLight/server/node_modules/.pnpm/[email protected]/node_modules/fastify/lib/hooks.js:237:10)
    at next (/Volumes/Root/Users/Elvis/WIP/SeaLight/server/node_modules/.pnpm/[email protected]/node_modules/fastify/lib/hooks.js:164:16)
    at contentSecurityPolicyMiddleware (/Volumes/Root/Users/Elvis/WIP/SeaLight/server/node_modules/.pnpm/[email protected]/node_modules/helmet/dist/middlewares/content-security-policy/index.js:124:4)
    at Object.<anonymous> (/Volumes/Root/Users/Elvis/WIP/SeaLight/server/node_modules/.pnpm/[email protected]/node_modules/fastify-helmet/index.js:45:7)
    at hookIterator (/Volumes/Root/Users/Elvis/WIP/SeaLight/server/node_modules/.pnpm/[email protected]/node_modules/fastify/lib/hooks.js:237:10)
    at next (/Volumes/Root/Users/Elvis/WIP/SeaLight/server/node_modules/.pnpm/[email protected]/node_modules/fastify/lib/hooks.js:164:16)
    at internalNext (/Volumes/Root/Users/Elvis/WIP/SeaLight/server/node_modules/.pnpm/[email protected]/node_modules/helmet/dist/index.js:142:6)
    at xXssProtectionMiddleware (/Volumes/Root/Users/Elvis/WIP/SeaLight/server/node_modules/.pnpm/[email protected]/node_modules/helmet/dist/middlewares/x-xss-protection/index.js:6:3)
    at internalNext (/Volumes/Root/Users/Elvis/WIP/SeaLight/server/node_modules/.pnpm/[email protected]/node_modules/helmet/dist/index.js:145:6)
                         & {"req":{"method":"GET","url":"/","hostname":"0.0.0.0:3001","remoteAddress":"127.0.0.1","remotePort":55152},"res":{"statusCode":500}}
2022-01-03T06:19:44.296Z *9884 @ElvisMini.local -  info: #req-1 request completed
                         & {"res":{"statusCode":500},"responseTime":8.393865000456572}
2022-01-03T06:19:45.311Z *9884 @ElvisMini.local - trace: client error
                         ร—Error 500: Parse Error: Invalid method encountered

Boom ~ ๐Ÿคทโ€โ™‚๏ธ

Motivation

No response

Example

No response

Invalid Typings for PassportUser in Typescript

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.20.0

Plugin version

2.3.0

Node.js version

16.17.0

Operating system

macOS

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

Ventura 13.4

Description

In my log in route, I wish to send back to the user their id (therefore I must access the id). I used request.user to attempt achieve this (of type PassportUser) but found that PassportUser has no properties (see below)
Screenshot 2023-07-19 at 23 42 36

Hence, when trying to access id (a property of User โ€“ what I'm expecting type PassportUser to be & is [if you console log it]) I run into this problem:

Screenshot 2023-07-19 at 23 38 30

Steps to Reproduce

Relevant server.ts code:

// Sessions
app.register(fastifySecureSession, {
  key: fs.readFileSync(path.join(__dirname, "../example-key")),
});

app.register(fastifyPassport.initialize());
app.register(fastifyPassport.secureSession());

// On login
fastifyPassport.use(
  new passportLocal.Strategy(
    {
      usernameField: "email",
      passwordField: "password",
    },
    // login method
    async (email, password, cb) => {
      const user = await app.prisma.user.findFirst({
        where: {
          email,
        },
      });

      if (!user) {
        return cb("User with this email doesn't exist");
      }

      const isCorrectPassword = await bcrypt.compare(password, user.password);

      if (!isCorrectPassword) {
        return cb("Incorrect password");
      }

      // null and false for all other cases
      return cb(null, user);
    }
  )
);

// register a serializer that stores the user object's id in the session ...
fastifyPassport.registerUserSerializer<User, string>(
  async (user, request) => user.id
);

// ... and then a deserializer that will fetch that user from the database when a request with an id in the session arrives

// TODO: what is unknown??
fastifyPassport.registerUserDeserializer<string, unknown>(
  async (id, request) => {
    return await app.prisma.user.findFirst({
      where: {
        id,
      },
    });
  }
);

Relevant route code:

fastify.post(
    "/",
    {
      preHandler: passport.authenticate("local"),
    },
    async (request, reply) => {
      const user = request.user;

      if (!user) {
        throw new Error(); // Something went wrong...
      }

      stdReply(reply, {
        clientMessage: `Successfully logged in as ${user.id}`,
      });
    }
  );

Expected Behavior

I should be able to access the same user object as registered here (of type User).

Screenshot 2023-07-19 at 23 40 39

Request user is null when reading from within Mercurius

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.20.2

Plugin version

0.4.3

Node.js version

14.16.0

Operating system

macOS

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

10.15.7

Description

I've been trying to integrate Mercurius with my Rest API backend. I have it working without auth, and I really would like to be able to read the request.user field from the Mercurius context. I don't understand why the value is always null.

instance.register(passportSetup);
instance.register(
  Mercurius,
  {
    schema,
    graphiql: true,
    async context(request) {
      return {
        user: request.user, // always null
        origin: getRequestOrigin(request)
      };
    }
  }
);
export const passportSetup = fp(async (instance) => {
  instance.register(fastifySecureSession, {
    key: Buffer.from(COOKIE_SECRET, 'hex'),
    cookieName: 'session',
    cookie: {
      maxAge: 24 * 60 * 60 * 1000 * 30,
      // Do not change the lines below, they make cy.auth() work in e2e tests
      secure: process.env.NODE_ENV !== 'development' && !process.env.INSECURE_AUTH,
      signed: process.env.NODE_ENV !== 'development' && !process.env.INSECURE_AUTH,
    },
  });
  instance.register(passport.initialize());
  instance.register(passport.secureSession());

  passport.registerUserSerializer(async (passportUser: { email: string, redirect: string }) => {
    const { email, redirect } = passportUser;
    await instance
      .db('users')
      .insert({
        email,
        confirmed_at: new Date(),
      })
      .onConflict('email')
      .ignore();

    const [user] = await instance
      .db<{ id: string, email: string }>('users')
      .where('email', email)
      .select('id');

    return { userId: user.id, redirect };
  });

  passport.registerUserDeserializer(async ({ userId, redirect }: { userId: string, redirect: string }) => {
    const [user] = await instance.db('users').where('id', userId);
    return { ...user, redirect };
  });

  passport.use(STRATEGY, magicLink);
});

Steps to Reproduce

Create a custom context and try to access the passportUser from within the request or the reply.request objects.

Expected Behavior

It should be possible to access the session user after deserialization by passport.

Using fastify-secure-session with openid-client strategy

Prerequisites

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

Issue

I'm trying to use fastify-secure-session with openid-client passport strategy.

It redirects correctly to the OIDC provider and redirects back correctly but then I get this error:

did not find expected authorization request details in session, req.session[\"oidc:xxxx\"] is undefined

I've looked into the source code for possible causes of this and I found something that I think may be the cause. Looking at how fastify-secure-session requires you to set session values using the request.session.set(key, value) method like this (example from the readme):

  request.session.set('data', request.body)
})

And then looking into the source code for openid-client which expects to be able to do req.session[key] = value as for example in this line:

 req.session[sessionKey] = pick(params, 'nonce', 'state', 'max_age', 'response_type');

I think this cannot really be compatible? On the one side req.session.set(key, value) is required, and on the other side req.session[key] = value is used. Or is there some adaptation going on inside of fastify-passport to make this compatible?

Release v0.2.0

@mcollina a few key fixes have landed on main that I'd like to release! I thought I might have had publish permissions to the fastify-passport npm package but I don't seem to. Would you mind adding me or releasing v0.2.0 (which has been tagged and pushed)? Thanks!

Add support of 'keepSessionInfo' bool param for strategies

Prerequisites

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

๐Ÿš€ Feature Proposal

Need to allow users to prevent clearing session data during passport login. I use @fastify/passport with @fastify/cookies and @fastify/session (redis store). After each passport login, the session is regenerated which is reasonable (prevent token/CSRF fixation attack). But in some my cases, I want to just change session Id, I do not want to clear whole session data since I have required info before calling login function.
Currently, it is handled here:

if (request.session.regenerate) {

It would be much better to handle it as in original express passport: https://github.com/jaredhanson/passport/blob/0575de90dc0e76c1b8ca9cc676af89bd301aec60/lib/sessionmanager.js#L38

The idea is to give user a choice to choose clear session on regenerate call or not in each run of strategy. It is achieved by passing keepSessionInfo parameter in options to the strategy.
The description of why this was implemented in express passport - https://medium.com/passportjs/fixing-session-fixation-b2b68619c51d.

Motivation

Currently, to achieve behaviour of keeping session on login function call, we have to keep the list of property names that we want to keep in session which is not flexible (with clearSessionIgnoreFields params). Also, there is no way to keep session only for particular strategies. You can only always prevent clearing data from the session or always clear data.
Also, if you present new custom field in the session, you have to remember to always add it to the list of clearSessionIgnoreFields.

Example

Now, we can only do something like that:

import { Authenticator } from '@fastify/passport';

const authenticator = new Authenticator({ clearSessionIgnoreFields: ['field1', 'field2'] });

authenticator.use('strategyName', strategy);
server.register(authenticator.initialize());
server.register(authenticator.secureSession());

....

server.get(
  '/login',
  {
    preValidation: [
      authenticator.authenticate('strategyName', {redirect_uri: 'https://example.com/login/callback'})
    ],
  },
  () => {}
);

It would be great to have something like:

import fastifyPassport from '@fastify/passport';

fastifyPassport.use('strategyName', strategy);
server.register(fastifyPassport.initialize());
server.register(fastifyPassport.secureSession());

....

server.get(
  '/login',
  {
    preValidation: [
      fastifyPassport.authenticate('strategyName', {redirect_uri: 'https://example.com/login/callback', keepSessionInfo: true})
    ],
  },
  () => {}
);

server.get(
  '/alter-login',
  {
    preValidation: [
      fastifyPassport.authenticate('strategyName', {redirect_uri: 'https://example.com/alter-login/callback', keepSessionInfo: false})
    ],
  },
  () => {}
);

fastifyPassport.initialize is not a function

As soon as fastifyPassport starts i get "fastifyPassport.initialize is not a function", if a debug fastifyPassport i have only default and strategy present

Steps to reproduce

  app.register(fastifySecureSession, { key: "akey" });
  
  app.register(fastifyPassport.initialize());
  app.register(fastifyPassport.secureSession());
  • node version: 15.0.1
  • fastify version: >=3.8.0
  • os: Linux

passport types not exists when using ESM + Typescript

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.15.0

Plugin version

2.2.0

Node.js version

18

Operating system

macOS

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

13.2.1

Description

When using ESM with TypeScript the types are missing.

Example with this code:

import fastifyPassport from '@fastify/passport';

fastifyPassport.initialize()
fastifyPassport.secureSession()

fastifyPassport.use(undefined as any, undefined as any);

I get:

$ tsc --notEmit
src/index.ts:3:17 - error TS2339: Property 'initialize' does not exist on type 'typeof import("<repo-path>/node_modules/@fastify/passport/dist/index")'.

3 fastifyPassport.initialize()
                  ~~~~~~~~~~

src/index.ts:4:17 - error TS2339: Property 'secureSession' does not exist on type 'typeof import("<repo-path>/node_modules/@fastify/passport/dist/index")'.

4 fastifyPassport.secureSession()
                  ~~~~~~~~~~~~~

src/index.ts:6:17 - error TS2339: Property 'use' does not exist on type 'typeof import("<repo-path>/node_modules/@fastify/passport/dist/index")'.

6 fastifyPassport.use(undefined as any, undefined as any);
                  ~~~


Found 3 errors in the same file, starting at: src/index.ts:3

Steps to Reproduce

Repro

or:

  1. run:
npm init -y

npm i typescript fastify @fastify/passport @types/node

./node_modules/.bin/tsc --init
  1. add this to src/index.ts file:
import fastifyPassport from '@fastify/passport';

fastifyPassport.initialize()
fastifyPassport.secureSession()

fastifyPassport.use(undefined as any, undefined as any);
  1. Add "type": "module" in package.json
  2. in tsconfig.json replace "module": "commonjs" with "module": "Node16"
  3. run tsc

Expected Behavior

should compile without errors

@fastify/jwt and @fastify/passport FST_ERR_DEC_ALREADY_PRESENT

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.17.0

Plugin version

2.3.0

Node.js version

18.15.0

Operating system

macOS

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

13.3.1

Description

I installed @fastify/jwt and @fastify/passport plugins but getting an error if I use them both. If I remove one of them everything works good.
My motivation was to use fastify ecosystem as much as possible and I wanted to use passport for oauth2 and jwt to requests.

When running with both plugins getting this error:

FastifyError [Error]: The decorator 'user' has already been added!
    at decorateConstructor (/backend/node_modules/fastify/lib/decorate.js:41:11)
    at Object.decorateRequest (/backend/node_modules/fastify/lib/decorate.js:123:3)
    at fastifyJwt (/backend/node_modules/@fastify/jwt/jwt.js:174:13)
    at Plugin.exec (/backend/node_modules/avvio/plugin.js:130:19)
    at Boot.loadPlugin (/backend/node_modules/avvio/plugin.js:272:10)
    at processTicksAndRejections (node:internal/process/task_queues:82:21) {
  code: 'FST_ERR_DEC_ALREADY_PRESENT',
  statusCode: 500
}

I read documentation about plugins and decorators but I don't know if it applies to installed plugins.

Steps to Reproduce

  • Install @fastify/jwt and @fastify/passport
  • Register plugins
  • Run app

Expected Behavior

App is not crushing and I can use passport for oauth2 and jwt for signing.

Failed to serialize user into session

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.28.0

Plugin version

0.5.0

Node.js version

17.8.0

Operating system

Windows

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

21H2

Description

when i use registerUserSerializer and registerUserDeserializer i get this error
image

Steps to Reproduce

code:
code

Expected Behavior

I need help please

[TypeScript] Unable to override type of request.user

๐Ÿ› Bug Report

Not sure if this is an actual bug or a typescript misconfiguration on my side but I haven't found a way to properly override the type of the request.user, and it is not explained in the docs:

To Reproduce

Steps to reproduce the behavior:

import { User } from 'src/typings';

declare module 'fastify' {
  interface FastifyRequest {
    user: User;
  }
}

Triggers an error:

Subsequent property declarations must have the same type.  Property 'user' must be of type 'unknown', but here has type 'User'.ts(2717)

Expected behavior

Working override

Your Environment

  • node version: 14
  • fastify-passport version: ^0.1.0
  • os: Mac
  • TypeScript: 4.1.2

Cannot publish new module

Prerequisites

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

Issue

Requires a build process that looks for other modules:

> @fastify/[email protected] build
> rimraf ./dist && mkdir dist && tsc --outDir dist && git rev-parse HEAD > BUILD_SHA

src/AuthenticationRoute.ts:1:23 - error TS2688: Cannot find type definition file for 'fastify-secure-session'.

1 /// <reference types="fastify-secure-session" />
                        ~~~~~~~~~~~~~~~~~~~~~~

src/AuthenticationRoute.ts:16:33 - error TS2339: Property 'session' does not exist on type 'FastifyRequest<RouteGenericInterface, Server, IncomingMessage, unknown, FastifyLoggerInstance>'.

16   const existing: any = request.session.get('messages')
                                   ~~~~~~~

src/AuthenticationRoute.ts:18:11 - error TS2339: Property 'session' does not exist on type 'FastifyRequest<RouteGenericInterface, Server, IncomingMessage, unknown, FastifyLoggerInstance>'.

18   request.session.set('messages', messages)
             ~~~~~~~

src/AuthenticationRoute.ts:156:29 - error TS2339: Property 'session' does not exist on type 'FastifyRequest<RouteGenericInterface, Server, IncomingMessage, unknown, FastifyLoggerInstance>'.

156                 if (request.session && request.session.get('returnTo')) {
                                ~~~~~~~

src/AuthenticationRoute.ts:156:48 - error TS2339: Property 'session' does not exist on type 'FastifyRequest<RouteGenericInterface, Server, IncomingMessage, unknown, FastifyLoggerInstance>'.

156                 if (request.session && request.session.get('returnTo')) {
                                                   ~~~~~~~

src/AuthenticationRoute.ts:157:33 - error TS2339: Property 'session' does not exist on type 'FastifyRequest<RouteGenericInterface, Server, IncomingMessage, unknown, FastifyLoggerInstance>'.

157                   url = request.session.get('returnTo')
                                    ~~~~~~~

src/AuthenticationRoute.ts:158:27 - error TS2339: Property 'session' does not exist on type 'FastifyRequest<RouteGenericInterface, Server, IncomingMessage, unknown, FastifyLoggerInstance>'.

158                   request.session.set('returnTo', undefined)
                              ~~~~~~~

src/session-managers/SecureSessionManager.ts:1:23 - error TS2688: Cannot find type definition file for 'fastify-secure-session'.

1 /// <reference types="fastify-secure-session" />
                        ~~~~~~~~~~~~~~~~~~~~~~

src/session-managers/SecureSessionManager.ts:23:13 - error TS2339: Property 'session' does not exist on type 'FastifyRequest<RouteGenericInterface, Server, IncomingMessage, unknown, FastifyLoggerInstance>'.

23     request.session.set(this.key, object)
               ~~~~~~~

src/session-managers/SecureSessionManager.ts:27:13 - error TS2339: Property 'session' does not exist on type 'FastifyRequest<RouteGenericInterface, Server, IncomingMessage, unknown, FastifyLoggerInstance>'.

27     request.session.set(this.key, undefined)
               ~~~~~~~

src/session-managers/SecureSessionManager.ts:31:20 - error TS2339: Property 'session' does not exist on type 'FastifyRequest<RouteGenericInterface, Server, IncomingMessage, unknown, FastifyLoggerInstance>'.

31     return request.session.get(this.key)
                      ~~~~~~~

src/type-extensions.ts:2:30 - error TS2307: Cannot find module 'fastify-flash/lib/flash' or its corresponding type declarations.

2 import { flashFactory } from 'fastify-flash/lib/flash'
                               ~~~~~~~~~~~~~~~~~~~~~~~~~


Found 12 errors in 3 files.

Errors  Files
     7  src/AuthenticationRoute.ts:1
     4  src/session-managers/SecureSessionManager.ts:1
     1  src/type-extensions.ts:2
npm ERR! code 1
npm ERR! path /private/tmp/fastify-passport
npm ERR! command failed
npm ERR! command sh -c npm run build

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/jsumners/.npm/_logs/2022-04-27T14_04_08_506Z-debug-0.log

fastify-passport on ๎‚  main took 10s
โฏ

Duplicate 'passport' decorator error when instantiating multiple Authenticator instances

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.17.0

Plugin version

2.3.0

Node.js version

16.2

Operating system

macOS

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

14.2.1

Description

Hello,

I followed instructions as per documentation but the Fastify server does not start up with the error quoted below:

{
    "level": 50,
    "time": 1706381991730,
    "pid": 19819,
    "hostname": "<redacted>",
    "err":
    {
        "type": "FastifyError",
        "message": "The decorator 'passport' has already been added!",
        "stack": "FastifyError: The decorator 'passport' has already been added!\n    at decorateConstructor (/Users/<redacted>/Work/<redacted>/node_modules/fastify/lib/decorate.js:41:11)\n    at Object.decorateRequest (/Users/<redacted>/Work/<redacted>/node_modules/fastify/lib/decorate.js:123:3)\n    at /Users/<redacted>/Work/<redacted>/node_modules/@fastify/passport/dist/CreateInitializePlugin.js:10:17\n    at Plugin.exec (/Users/<redacted>/Work/<redacted>/node_modules/avvio/plugin.js:130:19)\n    at Boot.loadPlugin (/Users/<redacted>/Work/<redacted>/node_modules/avvio/plugin.js:272:10)\n    at processTicksAndRejections (node:internal/process/task_queues:83:21)",
        "name": "FastifyError",
        "code": "FST_ERR_DEC_ALREADY_PRESENT",
        "statusCode": 500
    },
    "msg": "The decorator 'passport' has already been added!"
}

Best regards,

David

Steps to Reproduce

'use strict'

const fastify = require('fastify')({
    disableRequestLogging: true,
    logger: true,
    level: 'info'
});

const {Authenticator} = require('@fastify/passport');

const fastifyCookie = require('@fastify/cookie');
const fastifySession = require('@fastify/session');
fastify.register(fastifyCookie);
fastify.register(fastifySession, {
    secret: '6b8de8c5-2440-4903-8157-595adeaed92e',
    cookie: { secure: false },
    expires: 1800000
})

const _username1='user1';
const _password1 = 'password1';

const _username2 = 'user2';
const _password2 = 'password2';

const LocalStrategy = require('passport-local');

let passport1 = new Authenticator({ key: 'userAuth1', userProperty: 'user1' })
passport1.use('local-1', new LocalStrategy({},
    function (username, password, done) {
        if (username === _username1 && password == _password1) {
            return done(null, {username});
        }
        return done('local-1: You got this wrong');
    }));
fastify.register(passport1.initialize());
fastify.register(passport1.secureSession());


let passport2 = new Authenticator({ key: 'userAuth2', userProperty: 'user2' })
passport2.use('local-2', new LocalStrategy({},
    function (username, password, done) {
        if (username === _username2 && password == _password2) {
            return done(null, {username});
        }
        return done('local-2: You got this wrong');
    }));
fastify.register(passport2.initialize());
fastify.register(passport2.secureSession());

passport1.registerUserSerializer(async (user, request) => user);
passport1.registerUserDeserializer(async (serUser, request) => {
    return {...serUser}
});

passport2.registerUserSerializer(async (user, request) => user);
passport2.registerUserDeserializer(async (serUser, request) => {
    return {...serUser}
});

fastify.post("/login-password-1", {
    preValidation: passport1.authenticate('local-1', {
        failureMessage: true,
        failWithError: true,
    })
}, async function (request, reply) {
    console.log(request.user)
    reply.code(200).send({...request.user1})
})

fastify.post("/login-password-2", {
    preValidation: passport2.authenticate('local-2', {
        failureMessage: true,
        failWithError: true,
    })
}, async function (request, reply) {
    console.log(request.user)
    reply.code(200).send({...request.user2})
})

fastify.post("/auth-test", {
}, async function (request, reply) {
    console.log(request.user)
    reply.code(200).send({...request.user})
})

fastify.listen({ port: 3000, host: '0.0.0.0' }, function (err, address) {
    if (err) {
        fastify.log.error(err)
        process.exit(1)
    }
})

Expected Behavior

The fastify server should start up and I should have 2 authentication stacks.

Move fastify-secure-session dependency to devDependencies

๐Ÿš€ Feature Proposal

Currently fastify-passport has fastify-secure-session as a dependency.

However, the package is only ever used to import its types or for testing.

Would be great to move the dep to devDependencies.

Motivation

You can use fastify-passport with another session provider than fastify-secure-session (as long as you keep a compatible session API), in that case, fastify-secure-session won't ever be used. This can lead to issues if you have to target systems where building native modules is not an option (eg. windows devices where you don't fully control the system) since it depends on sodium-native.

Example

On most of my projects, I need a Redis storage + signed cookie so I went with a custom solution fitting my needs:

On another one that must target windows-based devices, I'm using only

However I encountered the libsodium build issue and discovered it was a hard dependency of this project.

Will gladly send a PR if you consider my request.

How to print custom error message instead of default "Unauthorized" or "Bad request"

Prerequisites

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

Issue

Hi,

I'm trying to setup @fastify/passport with @fastify/cookie and @fastify/secure-session. I'm using the local strategy passport-local

After many hits and trials, I was able to set up everything, now only one issue, how to send a custom error message instead of "Unauthorized".

Below is my code for LocalStrategy

fastifyPassport.use("local", new LocalStrategy({
    usernameField: "email",
    passwordField: "password"
  }, async (email, password, done) => {
    if (!email || !password) {
      return done(null, false, { message: "Missing email or password" });
    }
    await methods.FindRecord("users", { email }).then(user => {
      if (!user) {
        return done(null, false, { message: "User not found", status: 404 });
      }
      bcrypt.compare(password, user.password, (err, isValid) => {
        if (err) {
          return done(err);
        }
        if (!isValid) {
          return done(null, false, { message: "Invalid password", status: 401 });
        }
        return done(null, user);
      });
    });
  }));

fastifyPassport.registerUserSerializer(async (user, request) => {
    return user;
})

fastifyPassport.registerUserDeserializer(async (user, request,) => {
    return user
});

This is code in fastify /login route

fastify.post("/login", { 
    preValidation: fastifyPassport.authenticate("local", {
        failureMessage: 'Invalid email or password',
        successMessage: 'Logged in successfully',
        authInfo: false,
    })
}, async (request, reply) => {
    reply.send({ message: "Logged in successfully from new response" });
})

In response i can only see "Unauthorized" instead of any other message when password/username is wrong, I can see
my custom message Logged in successfully from new response only after credentials are correnct. Any help or guidance on this?

I tried fastify-flash but that was giving me some kind of decorator error.

Fastify Custom Strategy, redirect method not working - TypeError: res.setHeader is not a function

Prerequisites

  • I have searched existing issues to ensure the bug has not already been reported

  • I have written a descriptive issue title

Fastify version

4.6.3

Plugin version

No response

Node.js version

16

Operating system

Windows

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

11.3

Description

Hi,
I am trying to develop a custom strategy for Paypal. I am using [email protected].

I am using Fastify with Nest.js.

Here is my Strategy:

import { Strategy as FastifyStrategy } from '@fastify/passport';
import * as paypal from 'paypal-node-sdk';

class Strategy extends FastifyStrategy {
  private options;
  public name: string;
  private callback;
  constructor(options, callback) {
    super('paypal');
    this.options = options;
    this.name = 'paypal';
    this.callback = callback;
    paypal.configure({});
  }
  async authenticate(
    req,
    options?: any,
  ) {
    if (req.query && req.query['error']) {
      throw 'Query Error';
    }
    if (req.query && req.query['code']) {
      let code = req.query['code'];
      try {
        return this.getUserProfile(code);
      } catch (e) {
        return this.error(e);
      }
    }
    const location = await paypal.openIdConnect.authorizeUrl({
      scope: this.options.scope,
    });

    return this.redirect(location, 302);
   // the line above causes a problem 
  }

  verified(err, user, info) {
    if (err) {
      return this.error(err);
    }
    if (!user) {
      return this.fail(info);
    }
    this.success(user, info);
  }

  getUserProfile = async (code) => {
    // function to get the user data from Paypal;
  };
}

export default Strategy;

the redirect method seems to have a bug or something, it causes this error:

res.setHeader('Location', url);
            ^
TypeError: res.setHeader is not a function

Steps to Reproduce

Just try to implement a strategy and call the redirect method.

Expected Behavior

I expect the redirect method to work properly and redirect to the intended destination.

Use another session manager other than fastify-secure-session

๐Ÿš€ Feature Proposal

Using another session manager other than the only supported fastify-secure-session.

Motivation

Motivation is that, one can easily switch to a stateful session manager such as fastify-session and store the sessions on the supported session stores in fastify-session.

Example

Example will be when people want to use fastify-passport but still want the power to manually control sessions and store them.

I see that there is already a pull request going on #93 which will implement same feature, but any update on it?

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.