How to know if user is logged in with passport.js? - node.js

I've been reading passport.js info and samples for two days, but I'm not sure after that I did all the process of authenticating.
How do I know if I'm logged in, for example, I'll have a navigation bar with a login or logout button, is there some variable like code below?
if (login)
<button>logout</button>
else
<button>login</button>

If user is logged in, passport.js will create user object in req for every request in express.js, which you can check for existence in any middleware:
if (req.user) {
// logged in
} else {
// not logged in
}
You can create simple express.js middleware for that, that will check if user is logged in, and if not - will redirect to /login page:
function loggedIn(req, res, next) {
if (req.user) {
next();
} else {
res.redirect('/login');
}
}
And use it:
app.get('/orders', loggedIn, function(req, res, next) {
// req.user - will exist
// load user orders and render them
});

If you would like to use it in your templates as your code sample seems to indicate you can create some middleware such as this:
app.use(function (req, res, next) {
res.locals.login = req.isAuthenticated();
next();
});
Place that code somewhere after you have setup passport.
And then use it in your template (swig example)
{% if login %}
<button>logout</button>
{% else %}
<button>login</button>
{% endif %}

It is not explicitly documented but there is a isAuthenticated() method which is inserted into req by passport.
Can be used as follows,
req.isAuthenticated() // returns true if auth, false if not
// auth.js
module.exports = {
ensureAuthenticated: (req, res, next) => {
if (req.isAuthenticated()) {
return next()
}
res.redirect('/login') // if not auth
},
forwardAuthenticated: (req, res, next) => {
if (!req.isAuthenticated()) {
return next()
}
res.redirect('/dashboard'); // if auth
}
}
// app.js
app.get('/dashboard', ensureAuthenticated, (req, res) => res.render('dashboard'))
app.get('/login', forwardAuthenticated, (req, res) => res.render('login'))
app.get('/register', forwardAuthenticated, (req, res) => res.render('register'))

I was searching such solution and came across this page. Question is how to check login status on client side.
After logging I hide the Login button and show the logout button. On page refresh I again see the login button instead of logout button. The only solution is to save an item in sessionStorage if you are using session (and localStorage if you are using JWT). Delete this item when you logout.
Then in every page load check this sessionStorage item and do accordingly.
if (sessionStorage.getItem('status')) {
$("#btnlogout").show();
$("#btnlogin").hide();
// or what ever you want to do
} else {
$("#btnlogout").hide();
$("#btnlogin").show();
}
function Login() {
var data = {
username: $("#myModal #usr").val(),
password: $("#myModal #pw").val()
};
$.ajax({
type: 'POST',
url: '/login',
contentType: 'application/JSON; charset=utf-8',
data: JSON.stringify(data),
success: funcSuccess,
error: funcFail
});
function funcSuccess(res) {
sessionStorage.setItem('status', 'loggedIn');
$("#btnlogout").show();
$("#btnlogin").hide();
}
function funcFail() { $("#pp").text('Login Failed'); };
};
function Logout() {
$.ajax({
type: 'GET',
url: '/logout',
contentType: 'application/JSON; charset=utf-8',
success: funcSuccess,
error: funcFail,
});
function funcSuccess(res) {
$("#btnlogout").hide();
$("#btnlogin").show();
sessionStorage.removeItem("status");
};
function funcFail() { alert('Login method Failed'); };
};

use below code inside app.get() or router.get()
router.get('/edit/:id', (req, res)=>{
if(req.isAuthenticated()){
if(req.isAuthenticated()){
//
}
else{
res.redirect('/users/login');//your particular login routes
}
});

Good question, I had some issues while trying to implement something like this, when there is a un-authenticated request the handlebars skips the if block if the res.locals variable returns a falsy value. To solve this issue you need to setup a middleware in your app.js file to make the req.user available globally in your app Like so..
app.use(function (req, res, next) {
res.locals.login = req.user;
next();
});
In your header file you can do this check for authenticated user and display according content like so..
{{#if login }}
<li>User Account</li>
<li role="separator" class="divider"></li>
<li>Logout</li>
{{/if}}
{{#unless login}}
<li>Sign up</li>
<li>Sign in</li>
{{/unless}}

Related

Logout in nodejs

My app is on express, mongoDB. I add authorization by session. Don't work logout. There is the link on a page (this is pug):
a(href="/logout") logout
It's handler:
app.get('/logout', function (req, res, next) {
if (req.session) {
// delete session object
req.session.destroy(function (err) {
if (err) {
return next(err);
} else {
return res.redirect('/');
}
});
}
});
When clicked, it displays this and redirect does not occur. If you delete everything except redirect, then redirect will work. But I need to delete the authorization session.
try something like this
app.get('/logout', function (req, res, next) {
// If the user is loggedin
if (req.session.loggedin) {
req.session.loggedin = false;
res.redirect('/');
}else{
// Not logged in
res.redirect('/');
}
});
If you are using passportjs, you need to call the req.logout() to terminate a login session. See documentation here

How to store a user's object in every routes

I set up a navbar.ejs as a partial so that i don't need to rewrite it again. Every routes require navbar.ejs. Im using passport for Auth
<% if (!user) { %>
<li>Signup</li>
<li>Login</li>
</ul>
<% } else{ %>
<li>Dashboard</li>
<li><a><%= user.profile.name %></a></li>
<% } %>
Example routes
app.get('/', function(req, res) {
res.render('home');
}
app.get('/dashboard', isLoggedIn, function(req, res) {
res.render('dashboard', { user: req.user });
}
app.get('/about', function(req, res) {
res.render('about');
}
For routes that have user's object as the response are working fine when rendered but others will keep showing
user is not defined
I know that the obvious solution is to pass in a user's object in every single route, but that is really insane.
Imagine if I have to do this
app.get('/1', function(req, res) {
res.render('/1', {user: req.user});
}
app.get('/2', function(req, res) {
res.render('/2', {user: req.user});
}
app.get('/3', function(req, res) {
res.render('/3', {user: req.user});
}
app.get('/4', function(req, res) {
res.render('/4', {user: req.user});
}
app.get('/5', function(req, res) {
res.render('/5', {user: req.user});
}
If routes that require authentication, I don't really mind to pass the user's object, but simple routes like home, about or contact I don't feel like the need to pass in.
If you don't want to keep passing the user's object in every single route then you have to set up a middleware
somewhere after your passport configuration add the following codes
app.use(function(req, res, next){
res.locals.user = req.user;
});
then you no longer needed to pass the user's object in every single route
app.get('/1', function(req, res) {
res.render('/1');
}
app.get('/2', function(req, res) {
res.render('/2');
}

Automatically redirect logged in users nodejs passport.js

Currently using node.js, express & passport.js to create a custom website/application.
Having followed several guides, I have a functioning login/logout system with authentication. However, should a user revisit and their session is still active, it doesn't redirect them to the 'dashboard'.
Current root route:
/* GET login page. */
router.get('/',function(req, res) {
// Display the Login page with any flash message, if any
res.render('index', { message: req.flash('message') });
});
I am making use of the isAuthenticated function, as below:
var isAuthenticated = function (req, res, next) {
if (req.isAuthenticated())
return next();
res.redirect('/');
}
How do I get it to automatically redirect users with an existing session? Any pointers most welcome!
Ok, I figured it out. In the / route, I queried whether req.user was set.
/* GET login page. */
router.get('/',function(req, res) {
if(req.user){
res.redirect("/dashboard");
}else{
// Display the Login page with any flash message, if any
res.render('index', { message: req.flash('message') });
}
});
You can attach a middleware with "/" endpoint something like this.
router.get('/', sessionValidate, function(req, res, next) {
res.render('login');
});
Where sessionValidate looks something like this :
function sessionValidate(req,res,next){
console.log(req.user,"i am here");
users.findById(req.user,function(err, user) {
if(user!=null){
req.session.user = user;
res.locals.user=user;
res.redirect("/home")
}
else {
next();
}
});
}

render() - how it loads controllers?

I give 401 Uanthourised acces header with
module.exports = function() {
return function(req, res, next) {
if (!req.isAuthenticated()) {
res.status(401);
console.log('Unauthorized');
res.render('login.html', { csrfToken: req.csrfToken() });
return;
}
next();
};
};
Now login.html is a view handled by completly different controller:
var passport = require('passport');
var config = require(__dirname + '/../lib/config');
module.exports = function (router) {
router.get('/', function (req, res) {
// Error flash messages
var errorFlash = req.flash('error');
if (errorFlash && errorFlash.length) {
res.locals.error = errorFlash;
}
res.render('login.html', { csrfToken: req.csrfToken() });
});
router.post('/',
passport.authenticate('local', {
failureRedirect: '/login/',
failureFlash: true
}),
function(req, res) {
res.cookie(config.cookie.name, req.user.email, { signed: true, maxAge: 1234 });
res.redirect('/');
}
);
};
My concern is how I can be sure what part of login controller fires up? render('index.html') somehow hooks up to router.post() in index controller and login form is handler as usual. How that can be?
If you ever have any doubt which function is being called then a good approach is to (temporarily) add a console.log() within that function. I will often do something like:
console.log('filename.js - functionname() called with args: [%s] [%s]', arg1, arg2);
And then watch the log for information about what fired off and what variables you're trying to watch.
It could be that you need to redirect rather than render in your 401-related code.
res.redirect('/login');
(Edited)
The controller will always go to "index" if you did not provide any method.
The reason that it is a "POST" is probably that you are trying to send a form to the index page.
If you just request the index page. for example "(url)/login/index" or just "(url)/login" then it would be a "GET" request, but what are you trying to tell, I don't understand your question completely

Redirecting to previous page after authentication in node.js using passport.js

I'm trying to establish a login mechanism using node.js, express and passport.js. The Login itself works quite nice, also sessions are stored nicely with redis but I do have some troubles with redirecting the user to where he started from before being prompted to authenticate.
e.g. User follows link http://localhost:3000/hidden is then redirected to http://localhost:3000/login but then I want him to be redirected again back to http://localhost:3000/hidden.
The purpose of this is, if the user access randomly a page he needs to be logged in first, he shall be redirected to the /login site providing his credentials and then being redirected back to the site he previously tried to access.
Here is my login post
app.post('/login', function (req, res, next) {
passport.authenticate('local', function (err, user, info) {
if (err) {
return next(err)
} else if (!user) {
console.log('message: ' + info.message);
return res.redirect('/login')
} else {
req.logIn(user, function (err) {
if (err) {
return next(err);
}
return next(); // <-? Is this line right?
});
}
})(req, res, next);
});
and here my ensureAuthenticated Method
function ensureAuthenticated (req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login');
}
which hooks into the /hidden page
app.get('/hidden', ensureAuthenticated, function(req, res){
res.render('hidden', { title: 'hidden page' });
});
The html output for the login site is quite simple
<form method="post" action="/login">
<div id="username">
<label>Username:</label>
<input type="text" value="bob" name="username">
</div>
<div id="password">
<label>Password:</label>
<input type="password" value="secret" name="password">
</div>
<div id="info"></div>
<div id="submit">
<input type="submit" value="submit">
</div>
</form>
In your ensureAuthenticated method save the return url in the session like this:
...
req.session.returnTo = req.originalUrl;
res.redirect('/login');
...
Then you can update your passport.authenticate route to something like:
app.get('/auth/google/return', passport.authenticate('google'), function(req, res) {
res.redirect(req.session.returnTo || '/');
delete req.session.returnTo;
});
I don't know about passport, but here's how I do it:
I have a middleware I use with app.get('/account', auth.restrict, routes.account) that sets redirectTo in the session...then I redirect to /login
auth.restrict = function(req, res, next){
if (!req.session.userid) {
req.session.redirectTo = '/account';
res.redirect('/login');
} else {
next();
}
};
Then in routes.login.post I do the following:
var redirectTo = req.session.redirectTo || '/';
delete req.session.redirectTo;
// is authenticated ?
res.redirect(redirectTo);
Take a look at connect-ensure-login, which works along side Passport to do exactly what you want!
My way of doing things:
const isAuthenticated = (req, res, next) => {
if (req.isAuthenticated()) {
return next()
}
res.redirect( `/login?origin=${req.originalUrl}` )
};
GET /login controller:
if( req.query.origin )
req.session.returnTo = req.query.origin
else
req.session.returnTo = req.header('Referer')
res.render('account/login')
POST /login controller:
let returnTo = '/'
if (req.session.returnTo) {
returnTo = req.session.returnTo
delete req.session.returnTo
}
res.redirect(returnTo);
POST /logout controller (not sure if there is 100% ok, comments are welcome):
req.logout();
res.redirect(req.header('Referer') || '/');
if (req.session.returnTo) {
delete req.session.returnTo
}
Clear returnTo middleware (clears returnTo from session on any route except auth routes - for me they are /login and /auth/:provider ):
String.prototype.startsWith = function(needle)
{
return(this.indexOf(needle) == 0)
}
app.use(function(req, res, next) {
if ( !(req.path == '/login' || req.path.startsWith('/auth/')) && req.session.returnTo) {
delete req.session.returnTo
}
next()
})
This approach have two features:
you can protect some routes with isAuthenticated middleware;
on any page you can simply click on login URL, and after login return to that page;
If you are using connect-ensure-login there is a super-easy, integrated way to do this with Passport using the successReturnToOrRedirect parameter. When used, passport will send you back to the originally requested URL or fallback to the URL you provide.
router.post('/login', passport.authenticate('local', {
successReturnToOrRedirect: '/user/me',
failureRedirect: '/user/login',
failureFlash: true
}));
https://github.com/jaredhanson/connect-ensure-login#log-in-and-return-to
#chovy and #linuxdan answers have bug with not clearing session.returnTo if user goes to another page after login redirect (thats doesn't require authentication) and logins through there. So add this code to their implementations:
// clear session.returnTo if user goes to another page after redirect to login
app.use(function(req, res, next) {
if (req.path != '/login' && req.session.returnTo) {
delete req.session.returnTo
}
next()
})
If you do some ajax requests from login page, you can also exclude them.
Another approach is to use flash in ensureAuthenticated
req.flash('redirectTo', req.path)
res.redirect('/login')
And then in GET login
res.render('login', { redirectTo: req.flash('redirectTo') })
In view add hidden field to login form (example in jade)
if (redirectTo != '')
input(type="hidden" name="redirectTo" value="#{redirectTo}")
In POST login
res.redirect(req.body.redirectTo || '/')
Notice that redirectTo will clear after first GET login with it.
Easiest (and properly) way to achieve this is setting failureRedirect and successRedirect options.

Resources