LJNielsenDk
LJNielsenDk

Reputation: 1460

passport-local-mongoose: createStrategy is not a function / authenticate is not a function

I'm building on top of this starter project and trying to add user login with passport-local-mongoose.

Depending on which of the two ways I try to use the strategy I'm getting either this error:

[1] passport.use(User.createStrategy());
[1]                   ^
[1] TypeError: User.createStrategy is not a function

or this error:

[1] passport.use(new LocalStrategy(User.authenticate()));
[1]                                     ^
[1] TypeError: User.authenticate is not a function

I've tried googling around a lot for a solution but can't seem to find anything relevant.

Edit: As I understand it userSchema.plugin(passportLocalMongoose); in ./models/user.ts should add the functions to the model. Configure Passport/Passport-Local and Simplified Passport/Passport-Local Configuration show how to set it up but it doesn't seem to work for me.

Relevant dependency versions:

  "dependencies": {
    "cookie-parser": "^1.4.3",
    "express": "^4.14.0",
    "express-session": "^1.15.3",
    "mongoose": "^4.7.2",
    "passport": "^0.3.2",
    "passport-local": "^1.0.0",
    "passport-local-mongoose": "^4.0.0",
    "session-file-store": "^1.0.0",
    "typescript": "~2.2.0",
  },
  "devDependencies": {
    "ts-node": "~2.0.0",
  },

./models/user.ts

import * as mongoose from 'mongoose';
const passportLocalMongoose = require('passport-local-mongoose');

const userSchema = new mongoose.Schema({
  email: String,
  password: String,
  displayname: String,
  groups: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Group'
  }]
});

userSchema.plugin(passportLocalMongoose, {
  usernameField: 'email'
});

const User = mongoose.model('User', userSchema);

export default User;

Relevant parts of ./app.ts

import * as express from 'express';
import * as cookieParser from 'cookie-parser';
import * as session from 'express-session';
const FileStore = require('session-file-store')(session);
import * as mongoose from 'mongoose';
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;

const app: express.Express = express();

const cookieSecret = 'asdf';
app.use(cookieParser(cookieSecret));
app.use(session({
  secret: cookieSecret,
  resave: false,
  saveUninitialized: false,
  store: new FileStore()
}));

// Configure passport middleware
app.use(passport.initialize());
app.use(passport.session());

// Configure passport-local to use User model for authentication
const User = require('./models/user');
passport.use(User.createStrategy());
// Alternatively: passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());

Complete ./app.ts

import * as express from 'express';
import * as bodyParser from 'body-parser';
import * as cookieParser from 'cookie-parser';
import * as session from 'express-session';
const FileStore = require('session-file-store')(session);
import * as path from 'path';
import * as logger from 'morgan';
import * as mongoose from 'mongoose';
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;

// import index from './routes/index';
// import users from './routes/users';

const app: express.Express = express();

// // view engine setup
// app.set('views', path.join(__dirname, 'views'));
// app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
// app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
const cookieSecret = '';
app.use(cookieParser(cookieSecret));
app.use(session({
  secret: cookieSecret,
  resave: false,
  saveUninitialized: false,
  store: new FileStore()
}));
app.use(express.static(path.join(__dirname, 'public')));

// Configure passport middleware
app.use(passport.initialize());
app.use(passport.session());

// Configure passport-local to use User model for authentication
const User = require('./models/user');
passport.use(User.createStrategy());
// Alternatively: passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());

// app.use('/', index);
// app.use('/users', users);

// routes
const routeModules = require('require-all')({
  dirname: __dirname + '/routes',
  filter: (filename: string) => {
    filename = filename.toLowerCase();
    if ((filename.endsWith('.ts') && !filename.endsWith('.spec.ts'))
      || (filename.endsWith('.js') && !filename.endsWith('.spec.js'))) {
      return filename.substr(0, filename.length - 3);
    }
  },
  map: name => '/' + name
});
function resolve(root: string, modules): void {
  for (const name of Object.keys(modules)) {
    if (!name.startsWith('/')) {
      return;
    }
    const module = modules[name];
    if (module.default && module.default.route) {
      console.log(`Add router ${root + name}`);
      const router = module.default as express.Router;
      app.use(root, router);
    } else {
      resolve(root + name, module);
    }
  }
}
resolve('', routeModules);

// Default to main page, angular route takes over
app.use((req, res) => {
  res.sendFile(path.join(__dirname, 'public/index.html'));
});

// // catch 404 and forward to error handler
// app.use((req, res, next) => {
//   var err = new Error('Not Found');
//   err['status'] = 404;
//   next(err);
// });

// // error handlers

// // development error handler
// // will print stacktrace
// if (app.get('env') === 'development') {

//   app.use((error: any, req, res, next) => {
//     res.status(error['status'] || 500);
//     res.render('error', {
//       message: error.message,
//       error
//     });
//   });
// }

// // production error handler
// // no stacktraces leaked to user
// app.use((error: any, req, res, next) => {
//   res.status(error['status'] || 500);
//   res.render('error', {
//     message: error.message,
//     error: {}
//   });
//   return null;
// });

export default app;


// Connect to MongoDB
mongoose.connect('mongodb://localhost/test');
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', () => {
  console.log('MongoDB connected');
});

Upvotes: 5

Views: 8760

Answers (8)

Romijul Laskar
Romijul Laskar

Reputation: 11

Just add userSchema.plugins before your model creation like this :

userSchema.plugin(passportLocal);
userSchema.plugin(findOrCreate);
userSchema.plugin(passportLocalMongoose);
const User = mongoose.model('Model',userSchema);

then use the model. Its work just fine for me .

Upvotes: 0

ABHISHEK SINGH
ABHISHEK SINGH

Reputation: 1

const session = require("express-session");

app.use(session({
  secret: "We have to remember this for future reference",
  resave: false,
  saveUninitialized: false
}));

app.use(passport.initialize());
app.use(passport.session());

Upvotes: 0

Ishank COD
Ishank COD

Reputation: 1

It worked for me. Try using this format:

userSchema.plugin(passportLocalMongoose);
const User  = mongoose.model("User",userSchema);

Upvotes: 0

Sujith .K
Sujith .K

Reputation: 21

I faced this same issue and i resolved it by just adding:

 userSchema.plugin(passportLocalMongoose);

this code under the userSchema declaration before declaration of User schema model.

Example:

mongoose.connect("mongodb://localhost:27017/userDB", {
      useNewUrlParser: true,
      useUnifiedTopology: true
    });

    // userSchema 
    const userSchema = new mongoose.Schema({
      email: String,
      password: String
    });

    //**Like this**

    userSchema.plugin(passportLocalMongoose);

   // User Schema model
   const User = new mongoose.model("User", userSchema);
   mongoose.set("useCreateIndex", true);

Upvotes: 2

Kirill Pahl
Kirill Pahl

Reputation: 31

To resolve the problem I have checked how they have actually tested the types - https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/passport-local-mongoose/passport-local-mongoose-tests.ts Usage becomes pretty clear.

the following worked for me (please pay attention to usage of PassportLocalDocument, PassportLocalSchema, PassportLocalModel):

import mongoose, {
  PassportLocalDocument,
  PassportLocalSchema,
  PassportLocalModel,
} from 'mongoose';
import passportLocalMongoose from 'passport-local-mongoose';
import { updateIfCurrentPlugin } from 'mongoose-update-if-current';

export enum UserStatus {
  Created = 'created',
  Approved = 'approved',
  Banned = 'banned',
}

export enum UserRole {
  Member = 'member',
  Admin = 'admin',
  Public = 'public',
}

// An interface for props to create a new user
interface UserAttrs {
  email: string;
  password: string;
  status?: UserStatus;
  role?: UserRole;
}

// An interface that describes the properties of User document
interface UserDoc extends PassportLocalDocument {
  email: string;
  password: string;
  status: UserStatus;
  role: UserRole;
}

// An interface that describes User model
export interface UserModel extends PassportLocalModel<UserDoc> {
  build(attrs: UserAttrs): UserDoc;
}

const userSchema = new mongoose.Schema(
  {
    email: {
      type: String,
      required: true,
    },
    password: {
      type: String,
      required: true,
    },
    status: {
      type: String,
      required: true,
    },
    role: {
      type: String,
      required: true,
    },
  },
  {
    toJSON: {
      transform(doc, ret) {
        delete ret.password;
        ret.id = ret._id;
        delete ret._id;
      },
    },
  },
) as PassportLocalSchema;

userSchema.set('versionKey', 'version');
userSchema.plugin(passportLocalMongoose, {
  usernameField: 'email',
});
userSchema.plugin(updateIfCurrentPlugin);
userSchema.statics.build = (attrs: UserAttrs) => {
  return new User(attrs);
};

const User = mongoose.model<UserDoc, UserModel>('User', userSchema);

export { User };

Upvotes: 1

Philipl
Philipl

Reputation: 415

Sorry for the late answer. I find a way to resolve this.

import mongoose, { PassportLocalSchema  } from 'mongoose';
import passportLocalMongoose from 'passport-local-mongoose';

const { Schema } = mongoose;

const UserSchema = new Schema({});

UserSchema.plugin(passportLocalMongoose);

const User: mongoose.PassportLocalModel<mongoose.PassportLocalDocument> =
  mongoose.model('User', UserSchema as PassportLocalSchema );

export default User;

Upvotes: 2

Seymur
Seymur

Reputation: 111

Update your user.ts file like this:

import * as mongoose from 'mongoose';
import * as passportLocalMongoose from 'passport-local-mongoose';
import { PassportLocalSchema } from 'mongoose';

const userSchema = new mongoose.Schema({
  email: String,
  password: String,
  displayname: String,
  groups: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Group'
  }]
});

userSchema.plugin(passportLocalMongoose, {
  usernameField: 'email'
});

const User = mongoose.model('User', userSchema as PassportLocalSchema);

export default User;

it worked for me.

Upvotes: 8

J S
J S

Reputation: 1198

If I can understand correctly, this is merely caused by an unresolved type, and is not a real code error.

To resolve it -

  1. Include the type definition file in your project, which will extend the types in mongoose: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/passport-local-mongoose/index.d.ts

  2. In your app.ts, import the needed types from mongoose (notice that PassportLocalModel is from the definition file above):

import { Document, PassportLocalModel } from "mongoose";

  1. Now you can cast the updated types to User:

(User as PassportLocalModel<Document>).createStrategy()

(User as PassportLocalModel<Document>).authenticate()

Upvotes: 1

Related Questions