i am using passport with google strategy for authentication
my folder structure:
views
home.html
enter.html (this has just one google+ button)
app.js
routes
auth.js (for google login)
i want the client to be directed to enter.html and not be able to use home.html if req.user is not set ( req.user is set when user is authenticated using google )
once authentication is done user should be redirected to home.html
app.use(express.static()) makes both of them available which is not what i want
the google login page comes by auth/google
and i also need to know what i should keep as the callback uri
in app.js
i have done mongodb configuration
i have done passport configuration
what to do next?
in auth.js
const router = require('express').Router();
const passport = require('passport');
router.route('/google')
.get(passport.authenticate('google', { scope: ["profile"] }));
router.route('/google/redirect')
.get(passport.authenticate('google'), (req, res, next) => {
// res.redirect what
});
module.exports = router;
To serve the home.html page you could redirect to a protected home route. Here an example of how I would go about implementing this.
auth.js
router.route('/google/redirect')
.get(passport.authenticate('google', { failureRedirect: '/' }), (req, res, next) => {
// Set to redirect to your home route / html page
res.redirect('/home')
});
To prevent users from going to home without authorization, you should also add a route guard to your /home route.
routes.js
const { checkAuth } = require('./guards'); // Protected routes
router.get('/home', checkAuth, async (req, res) => {
res.render('home')
});
guards.js
module.exports = {
checkAuth(req, res, next) {
if (req.isAuthenticated()) {
return next()
} else {
res.redirect('/')
}
},
}
Related
This part of code handles the login authorization routing in my app
const express = require("express");
const authController = require('../controllers/authController');
const indexController = require('../controllers/indexController');
const router = express.Router();
router.use("/login", (req, res, next) => {
if(req.session.loggedIn) {
res.redirect('/account');
}
next();
});
router.use("/account", (req, res, next) => {
if(!req.session.loggedIn) {
res.redirect('/login');
}
next();
});
router.get('/', (req, res) => {
res.redirect('/login');
});
router.get('/login', (req, res) => {
res.render('login');
});
router.get('/log-out', authController.logOut);
router.get("/account", indexController.getAccountData);
module.exports = router;
There were no problems and it was working fine till recent days.
I haven't change anything in this file nor authController nor indexController.
When I make a change (in other parts), nodemon restarts the app and I automatically jump to login page cause obviously all sessions are destroyed. But I get an error in getAccountData function (Error says req.session is undefined).
As you can see there's no way for the app to reach that function with no sessions set.
I have to restart the app again to act correct.
The session will be cleared each time the server restarts. So to escape from that you need to save the session to database. If you are using mongodb or I can give you example using mongodb.
import MongoStore from "connect-mongo";
import session from "express-session";
app.use(
session({
store: MongoStore.create({
mongoUrl: process.env.MONGODB_URI || "mongodb://localhost:27017/project",
}),
secret: "secret key",
cookie: { maxAge: sessionExpireInMilliseconds },
})
);
req.session.loggedIn would error out whenever session is undefined. You need to check if it is defined before trying to access loggedIn.
Try:
router.use("/login", (req, res, next) => {
if(req.session && req.session.loggedIn) {
res.redirect('/account');
}
next();
});
router.use("/account", (req, res, next) => {
if(!req.session || !req.session.loggedIn) {
res.redirect('/login');
}
next();
});
I'm using Vue Router for navigation and passport.js for login/registration for my application. The login/registration system works fine, and are set-up with a mongodb database.
However, I can't get my route protection to work with the passport function for ensuring authentication.
Here is my code so far:
const { ensureAuthenticated } = require('../../Configurators/userAuthentication');
const routes = [
{path: '/afterLogin', name: 'AfterLogin',
component: () => import('../views/afterLogin.vue'), beforeEnter : ensureAuthenticated},
]
Here is my authentication function:
module.exports = {
ensureAuthenticated: function(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
req.flash('error_msg', 'Please log in to view that resource');
res.redirect('/users/login');
}
};
I'm very new to Vue Router so any help would be much appreciated.
I need help while putting different functions like sign up, sign in, delete a profile, edit profile, in a single file named Users in express.
I have put signup over the '/' and now I am unable to find the way to go to the sign in function in users file.
for signup, I used the express method as
app.use('/signup' , Users)
and I wanted to know that how can I access the sign in function now
//this is the code in Users.js file
router.post("/", (req, res) => {
var user = new User();
user.name = req.body.name;
user.DOB = req.body.DOB;
user.email = req.body.email;
user.city = req.body.city;
user.password = req.body.password;
user.gender = req.body.gender;
user.image = req.body.image;
user.Phone = req.body.PhoneNumber;
user.MsgNumber = req.body.MsgNumber;
user.about = req.body.about;
user.JoinDate = new Date;
user.save(function(err, result) {
if (err) {
res.json({
status: err
})
} else {
res.json({
status: 'ok'
});
}
});
});
//now the second function of signin
router.post("/signIn", passport.authenticate("local"), (req, res) => {
if (usernotfound == 0) {
res.send(JSON.stringify(req.user));
} else {
res.send('Not Found')
}
// here is the code from the main server js file to send data to these functions
app.use('/signUp', users)
app.use('/signin', users)
{signup is on '/' so that is called directly as the
root function. now how can i access the signin function}
Not quite sure what you are looking for but you are using the same handler for different routes when you do this:
app.use('/signUp', users)
app.use('/signin', users)
What you are telling express here is "I want the same thing to happen when users go to signUp and signin"
You can have the users route in same file but the handlers need to be different.
index.js
const epxress = require('express')
const userRouter = require('./users')
const app = express()
app.use('/users, userRouter)
The index file is a simple express app which requires user.js
users.js
const expreess = require('express')
const router = express.Router()
router.post("/signIn", passport.authenticate("local"), (req, res) => {
// Your code for signing in
})
router.post('/signUp', (req, res) => {
// your code for signing up
})
module.exports = router
So users.js is a simple express router. So now visitors can go to /users/signIn and /users/signUp - basically they go to '/users' and get routed to the user.js file which has other routes defined /signIn and /signUp so the complete path becomes /users/signIn and /users/signUp
Another way would be to export two handlers in your users.js file. Something like
index.js
app.post('/signIn', users.signIn)
app.post('/signUp', users.signUp)
And then in your users.js
exports.signIn = function(req, res) {
// Your code for signin in
}
exports.signUp = function(req, res) {
// Your code for signing up
}
I have a server.js file in which I have required services.js that further requires all the other API files
require('./web_services/services')(app);
I have two other files Users.js and Events.js that holds the actual apis. The services.js require both the files
var logger = require("../logger/logger");
module.exports = function (app) {
require('./User/Users')(app,logger);
require('./Event/Events')(app,logger);
// in case of api error
app.use(function (err, req, res, next) {
res.status(err.status || 500);
res.send({ 'status': res.status, 'message': err });
logger.error(err);
});
}
Now I want to prefix /users before users apis and events/ before event apis.
I have tried
app.use('/user/', require('./User/Users')(app));
Users.js
module.exports = function (app, logger) {
// apis
};//exports
but this is not working? Am i missing something?
I believe you can solve your problem by using express routing
You can initialize router in service.js and then pass it around to enable routes and middleware in it and can define prefix in it in the service file as you were trying to do.
// service.js
var express = require('express')
var router = express.Router();
...
app.use('/users', require('./User/Users')(router, logger));
app.use('/events', require('./Event/Events')(router, logger));
...
All the routes in this file will be prefixed by the /users because we have mentioned it in the service file.
// Users.js
module.exports = function (router, logger) {
// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
logger('users middleware', req); // or however you are using your logger
next();
})
// this route will be `/user/`
router.get('/', function (req, res) {
res.send('Users home page')
})
// this route will be `/users/about`
router.get('/about', function (req, res) {
res.send('About users')
})
return router;
};//exports
It's pretty straight forward I guess. You can refer this boilerplate, I made a while back ago using express and express router. Some naming convention is not good but yeah you can see how the router is defined and used in the boilerplate to enable route prefixing and middleware for specific routes.
I´m facing a confusion issue while implementing the authentication for my restful api using passport local strategy.
Note:
I got the authentication working successfully when I´m doing it all in my index.js. But I want to use in Classes for better Code separation.
I have a passport.js Module
// config/passport.js
// load all the things we need
var LocalStrategy = require('passport-local').Strategy;
// load up the user model
var mysql = require('mysql');
var dbconfig = require('./database');
var connection = mysql.createConnection(dbconfig.connection);
module.exports = function(passport) {
// passport needs ability to serialize and unserialize users out of session
passport.serializeUser(function (user, done) {
//console.log("SER");
console.log(user),
done(null, user);
});
passport.deserializeUser(function (user, done) {
console.log("XXXX");
console.log(user);
connection.query("SELECT * FROM users WHERE name = ? ",user.name, function(err, rows){
console.log("DER");
console.log(rows);
done(err, rows[0]);
});
});
// passport local strategy for local-login, local refers to this app
passport.use('local-login', new LocalStrategy(
function (username, password, done) {
console.log("hhh");
console.log(username);
connection.query("SELECT * FROM users WHERE name = ? ",username, function(err, rows){
console.log(rows);
return done(err, rows[0]);
});
})
);
// route middleware to ensure user is logged in
function isLoggedIn(req, res, next) {
if (req.isAuthenticated())
return next();
res.sendStatus(401);
}
};
This is my Controller Class:
class AuthenticateController {
constructor(router, passport) {
this.router = router;
this.registerRoutes();
this.passport = passport;
}
registerRoutes() {
this.router.post('/login/:username/:password', this.login.bind(this));
//this.router.get('/logout', this.logout.bind(this));
this.router.get('/content', this.content.bind(this));
}
login(req, res) {
this.passport.authenticate("local-login", { failureRedirect: "/login"}),
res.redirect("/content");
}
content(req, res ) {
console.log(req.user);
if (req.isAuthenticated()) {
res.send("Congratulations! you've successfully logged in.")
} else {
res.sendStatus(401);
}
}
isLoggedIn(req, res, next) {
console.log(req.user);
if (req.isAuthenticated())
return next();
res.sendStatus(401);
}
}
module.exports = AuthenticateController;
The Controller gets the router and passport fully configured as parameters from my index.js.
//index.js
var express = require('express')
, cors = require('cors')
, app = express()
, passport = require('passport')
, morgan = require('morgan');
require('./config/passport')(passport); // pass passport for configuration
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(require('express-session')({secret: 'vidyapathaisalwaysrunning',
resave: true,
saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());
app.use(cors());
var apiRouter = express.Router();
app.use('/api', apiRouter);
//
var apiV1 = express.Router();
apiRouter.use('/v1', apiV1);
var authenticateApiV1 = express.Router();
apiV1.use('/auth', authenticateApiV1);
var AuthenticateController = require('./controllers/authenticate');
var ac = new AuthenticateController(authenticateApiV1, passport); //pass in our fully configured passport
//If I call this /login instead of the /auth/login/ in the Controller Class it works!
//app.post("/login",
// passport.authenticate("local-login", { failureRedirect: "/login"}),
// function (req, res) {
// res.redirect("/content");
// });
What is working and what is not working
The Authentication in general is working. In my posted index.js you see app.post("/login", .... If I call this one the authentication is successfully and if I try to reach the restricted content in /auth/content/ req.user has a value (the user object) and I can successfully call req.isAuthenticated() .
BUT, If I use the authentication from /auth/login/username/password the req.user is undefined when trying to reach the restricted Content.
I get no error and the response of /auth/login/username/password/ HTTP Code 301 - 'redirecting to /content.
I have currently no idea what I´m doing wrong here and I´m pretty new to the topic of Node/express/ passport ..
Hope someone has an Idea. If you need something else to help me, just mention it in the comments and I will do my best to provide you everything you need.
Thanks
EDIT:
I recently tried to read the req.user in the login function and even there it is undefined
login(req, res) {
this.passport.authenticate("local-login", { failureRedirect: "/login"}),
console.log(req.user) //undefined
res.redirect("/content");
}
I guess it could be some async problem and I should use some callback functions, but I don´t know how to apply this in my login()
EDIT 2:
Another Issue I´m facing is the integration of the isLoggedIn() request.
If I do this:
registerRoutes() {
this.router.get('/', this.isLoggedIn, this.getUsers.bind(this));
this.router.get('/:id', this.getSingleUser.bind(this));
}
it results in 401 - Unauthorized
A console.log(req.user); in the isLoggedIn() results in undefined.
But if I call the first route without calling isLoggedIn() and do console.log(req.user); the user object exists.
The correct use of callback with passport authentication for local strategy can be as below:
function(req, res, next){
passport.authenticate('local-login', function(err, user, info){
if(err)
return logger.log('error', err);
if(user)
req.login(user, function(err){
if(err) return next(err);
return res.json({'success': true});
});
if(!user)
return res.json({'error':true, 'message': info.message, 'type': info.type});
})(req, res, next);
}
Please note the use of req.login() to explicitly set user in session.
The only thing I'm finding strange is that your route declarations are different.
In the AuthenticateController the route is declared as:
this.router.post('/login/:username/:password', ...
While in index.js, the route is simply declared as
app.post("/login", ...
How is your client submitting the login credentials to the server? If it is by form, like the tutorial, could it be that having :username and :password declared as route params but being sent by form messes with passport?
Try registering the route exactly like index.js
this.router.post('/login', ...
EDIT:
I've found another dicrepancy. In AuthenticateController the res.redirect("/content"); is not wrapped inside a callback. So it is being executed before Authenticate finishes running.
In the index.js example, passport is being used as a route middleware:
app.post("/login",
passport.authenticate("local-login", { failureRedirect: "/login"}),
function (req, res) {
res.redirect("/content");
});
While in the passport.js it is inside the callback. Consider declaring it in the route:
registerRoutes() {
this.router.post('/login', this.passport.authenticate("local-login", { failureRedirect: "/login"}), this.login.bind(this));
(...)
}
login(req, res) {
res.redirect("/content");
}
O, better yet, why not use passport's option to declare both success and failure redirects, since that seems to be all that you are doing:
login(req, res) {
this.passport.authenticate("local-login", { successRedirect: "/content", failureRedirect: "/login" });
}
You are passing this.login.bind(this) as a middleware to this.router.post('/login/:username/:password', this.login.bind(this)); but login(req, res) only responds to the request with res.redirect("/content"); i.e. redirecting to /content
So like you said, you need to supply a callback that does something with the user that is returned from passports middleware verify callback.
app.post("/login",
passport.authenticate("local-login", { failureRedirect: "/login"}),
function (req, res) {
console.log(req.user); // log user in console
res.json({user: req.user}); // send user as json response
});
The custom callback mentioned by #divsingh is if you want to explicitly have control of setting the session, error messages and redirecting the request. Any other information can be found under http://passportjs.org/docs