Search code examples
node.jstypescriptmongoosepassport.jspassport-local-mongoose

Passport-local-mongoose serializeUser incorrect type


I'm trying to use Passportjs, and mongoose for authentication but I am having a hard time getting the correct type with typescript.

Passport.use(UserModel.createStrategy())
Passport.serializeUser(UserModel.serializeUser()) // <---- Error

I get the error:

No overload matches this call.
  Overload 1 of 2, '(fn: (user: User, done: (err: any, id?: any) => void) => void): void', gave the following error.
    Argument of type '(user: PassportLocalModel<User>, cb: (err: any, id?: any) => void) => void' is not assignable to parameter of type '(user: User, done: (err: any, id?: any) => void) => void'.
      Types of parameters 'user' and 'user' are incompatible.
        Type 'User' is missing the following properties from type 'PassportLocalModel<User>': authenticate, serializeUser, deserializeUser, register, and 68 more.
  Overload 2 of 2, '(fn: (req: IncomingMessage, user: User, done: (err: any, id?: unknown) => void) => void): void', gave the following error.
    Argument of type '(user: PassportLocalModel<User>, cb: (err: any, id?: any) => void) => void' is not assignable to parameter of type '(req: IncomingMessage, user: User, done: (err: any, id?: unknown) => void) => void'.
      Types of parameters 'user' and 'req' are incompatible.
        Type 'IncomingMessage' is missing the following properties from type 'PassportLocalModel<User>': authenticate, serializeUser, deserializeUser, register, and 53 more.

This is what my User class looks like:


export interface User extends Document {
  email: String
  password: String
  displayName: String
}

const UserSchema = new Schema(
  {
    email: { type: String, unique: true },
    password: String,
    displayName: String,
  },
  { timestamps: true }
)

UserSchema.plugin(PassportLocalMongoose, {
  usernameField: 'email',
})

export const UserModel = model('User', UserSchema)

Solution

  • UPDATE - my PR landed so this has been fixed in v6.1.0 of @types/passport-local-mongoose. To get the fix update this dependency to 6.1.0 then extend the Express.User class from your own mongo User. Because your Mongo User is using the same class name as the Express User you need to create an alias, as shown below. Do this before you call Passport.serializeUser and you should be good to go. You can put the Express.User extension anywhere you want, I added in my routes/user.ts as that is the most appropriate place in my codebase.

    type _User = User;
    
    declare global {
      namespace Express {
        interface User extends _User {
        }
      }
    }
    
    Passport.use(UserModel.createStrategy())
    Passport.serializeUser(UserModel.serializeUser()) // <---- No Error
    

    -- ORIGINAL ANSWER --

    I believe this is a bug in @types/passport-local-mongoose which I reported today. You can fix the bug by adding a cast to any like this.

    Passport.use(UserModel.createStrategy())
    Passport.serializeUser(UserModel.serializeUser() as any) // <---- No Error