I am using googleapis oauth2.0 for user registration on my website through through OAuth2.0. I get to the point where I get the access token and id token and save the new user in my database, from that point on I want to create a user session and I am trying to use passport for it but passport needs one of its strategies to implement which I do not need at this point because I already have verified user email and everything and have saved the user in my database all I now need to do is create a user session and send the user to the home page but there seems to be no way to just create user session without a strategy in passport.
here is my oauth2.0 redirect function where I get the access token etc.
router.get('/oauth-redirect', function (req, res, next) = > {
const data = await googleAuth.getToken(req.code);
const user = getUserInfoFrom(data);
const savedUser = save(user);
//here: use passport to create a session and send the user to home page ????
})
found the way to do it, there is req.login() function exposed by passport that you can use to manually log the user in.
router.get('/oauth-redirect', function (req, res, next) = > {
const data = await googleAuth.getToken(req.code);
const user = getUserInfoFrom(data);
const savedUser = save(user);
//this below is what will create a session and will set connect.sid cookie
req.login(savedUser, function(err) {
if (err) {
console.log(err);
}
return res.redirect('/home');
});
})
Related
I'm new to NodeJS and developing an API using it,
I want the API to be authenticated with an API token method (only people with a token stored in DB, created through a specific encryption should be able to access the API resource.)
I'm using an SQL server, NodeJS, and the Express framework.
Please guide me in what I should use to authenticate the API request.
Thanks in advance.
You could use passport.js with JwtStrategy. This is the idea:
mypassport.js
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const opts = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'yourSecret'
};
passport.use(new JwtStrategy(opts, (payload, done) => {
const user = findUserById(payload.id);
if (!user) {
return done('user not exists', null);
}
return done(null, user);
}));
server.js (using express)
require('./mypassport'); // <- initialize passport strategies
//you could also use passport with local strategy for this
app.post('login', (req, res) => {
const username = req.query.username;
const password = req.query.password;
if (validLogin(username, password)) {
const user = findUserByUsername(username);
const jwt = createTokenWithSecret(user, 'yourSecret'); // You can use jwt-simple for this
res.json({ token: jwt });
} else {
//send unauthorized
}
});
const requireLogin = passport.authenticate('jwt');
app.get('/something', requireLogin, (req, res) => {
//here, user is authenticated and available in 'req.user'
});
First, you must login with POST /login { username: 'john', password: '1234' }. That will return a JSON with the jwt token like this:
{ token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' }
In subsequent requests, you must send a header Authorization with value: Bearer {token} so passportjs and JwtStrategy can authorize the request.
Hope it helps!
NOTE: I have not tested code above, it just shows the approach.
For API Authenication use Passport JS
You can use json web token (jwt) for API authorization. There are node modules available which provides authentication functionality and you can simply use.
Please have a look at this article: https://medium.freecodecamp.org/securing-node-js-restful-apis-with-json-web-tokens-9f811a92bb52?gi=89f3f4d89dfd
I'm protecting an API in my web app using passport. Before hitting the resource, I want the user(from oauth client) to login/authorize first.
Have tried both LocalStrategy and BasicStrategy - their implementation are almost 100% the same, just look for a user by email and verify password. If I use BasicStrategy, the browser will bring up a dailog saying 'Authentication Required' and ask for username & password. However if I use LocalStrategy, it just says 'Unauthorized', and no chance to login.
So my questions are:
How does browser decide to bring up the login dialog, or is it done by BasicStrategy?
Is it possible to show a login page with some UI, instead of the simple dialog?
Note that it's part of OAuth process so I don't really want to redirect to the login page.
Passport is used only for authentication. Later after one needs to maintain session , to check if user is logged in or not.
So you can make a middleware call before every route.
Middlleware checking if user is logged in or not .
'use strict';
var express = require('express');
module.exports = {
isLoginCheck : function (request, response, next) {
if(!request.session.user && request.path != '/login'){
response.redirect('/login');
}else{
next();
}
},
};
In routes file import the middleware,
var express = require('express'),
indexController = require('./../controller/index'),
middleware = require('./../middleware/index'),
passport = require('passport'),
router = express.Router();
router.get('/addUser', indexController.addUser);
router.post('/saveUser', indexController.saveUser);
router.use(middleware.isLoginCheck);
router.get('/', indexController.index);
router.get('/login', indexController.login);
router.post('/login', function(request, response, next){
passport.authenticate('local', function(err, user, info) {
if (err) {
return next(err);
}
if (!user) {
var message = "Invalid credentials";
return response.render('login',{message: info.message, userLoggedIn : null});
}
request.logIn(user, function(err) {
if (err) { return next(err); }
request.session.user = request.user;
response.redirect('/userList');
});
})(request, response, next);
});
Note : in the success we are saving the verified user in session , which will persist till the server is running. Middleware above we are checking for the session object.
Turned out the Authentication dialog is the sequela of response header:
The browser detects that the response to my call on the XMLHttpRequest object is a 401 and the response includes WWW-Authenticate headers. It then pops up an authentication dialog asking, again, for the username and password.
Here's a very helpful post.
As for the standalone signin page, because it's part of OAuth process, it's better to use express's redirect.
Background
I have a MEAN application with CRUD capabilities fully tested with postman. I have been trying to persist login for quite some time now with no luck. I have read and tried the following
passport docs
toon io's blog about login
Express session
Scotch io, node auth made easy
Plus a buch of other light reading materials (Lots of SO questions)
But I have only been able to register and log a user in, not persist login with a session.
My App
Here is a link to the full github repo (if you are looking for the latest changes check develop branch)
My Understanding of Auth/Login
Here is my understanding of user login with code examples from my project and screenshot of postman results as well as console logs.
Passport setup
I have the following auth.js file, it configs passport
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
module.exports = function(app, user){
app.use(passport.initialize());
app.use(passport.session());
// passport config
passport.use(new LocalStrategy(user.authenticate()));
passport.serializeUser(function(user, done) {
console.log('serializing user: ');
console.log(user);
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
user.findById(id, function(err, user) {
console.log('no im not serial');
done(err, user);
});
});
};
This gets called in the server file like
//code before
var user = require('./models/user.js');
var auth = require('./modules/auth.js')(app, user);
// code after
Routing for login
In my routes I have the login route as follows
router.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
return next(err);
}
if (!user) {
return res.status(401).json({
err: info
});
}
req.logIn(user, function(err) {
if (err) {
return res.status(500).json({
err: 'Could not log in user'
});
}
res.status(200).json({
status: 'Login successful!'
});
});
})(req, res, next);
});
This route works as tested with postman. I enter the details 'joe' and 'pass' and get the following response.
When this route is hit we can also see in the console that the user is serialized.
So what next?
This is where I get lost. I have a few questions.
Is the user now in a session on my server?
Should I send the req.session.passport.user back to the client?
Do I need the session ID on all future requests?
Testing the Session
I have a second route setup for testing the session it is as follows
router.get('/checkauth', passport.authenticate('local'), function(req, res){
res.status(200).json({
status: 'Login successful!'
});
});
The part passport.authenticate('local') (I thought) is there to test if the user session exists before giving access to the route but I never get a 200 response when I run this, even after a login.
Does this route expect a req.session.passport.user passed in the head or as a data argument on a http request that requires auth?
If I missed anything or am understanding something wrong please tell me, any input is appreciated. Thanks all.
Is the user now in a session on my server?
No, You need to use the express-session middleware before app.use(passport.session()); to actually store the session in memory/database. This middleware is responsible for setting cookies to browsers and converts the cookies sent by browsers into req.session object. PassportJS only uses that object to further deserialize the user.
Should I send the req.session.passport.user back to the client?
If your client expects a user resource upon login, then you should. Otherwise, I don't see any reason to send the user object to the client.
Do I need the session ID on all future requests?
Yes, for all future requests, the session id is required. But if your client is a browser, you don't need to send anything. Browser will store the session id as cookie and will send it for all subsequent requests until the cookie expires. express-session will read that cookie and attach the corresponding session object as req.session.
Testing the Session
passport.authenticate('local') is for authenticating user credentials from POST body. You should use this only for login route.
But to check if the user is authenticated in all other routes, you can check if req.user is defined.
function isAuthenticated = function(req,res,next){
if(req.user)
return next();
else
return res.status(401).json({
error: 'User not authenticated'
})
}
router.get('/checkauth', isAuthenticated, function(req, res){
res.status(200).json({
status: 'Login successful!'
});
});
As #hassansin says you need to use a middleware that implement session management. The passport.session() middleware is to connect the passport framework to the session management and do not implement session by itself. You can use the express-session middleware to implement session management. You need to modify your auth.js in the following way
var passport = require('passport');
var session = require('express-session');
var LocalStrategy = require('passport-local').Strategy;
module.exports = function(app, user){
app.use(session({secret: 'some secret value, changeme'}));
app.use(passport.initialize());
app.use(passport.session());
// passport config
passport.use(new LocalStrategy(user.authenticate()));
passport.serializeUser(function(user, done) {
console.log('serializing user: ');
console.log(user);
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
user.findById(id, function(err, user) {
console.log('no im not serial');
done(err, user);
});
});
};
Notice that in this case the session engine is using the in memory store and it didn't work if you scale your application and apply load balancing. When you reach this development state something like the connect-redis session store will be needed.
Also notice that you need to change the secret value used on the session midleware call and use the same value on all application instances.
As per the passport documentation, req.user will be set to the authenticated user. In order for this to work though, you will need the express-session module. You shouldn't need anything else beyond what you already have for passport to work.
As far as testing the session, you can have a middleware function that checks if req.user is set, if it is, we know the user is authenticated, and if it isn't, you can redirect the user.
You could for example have a middleware function that you can use on any routes you want authenticated.
authenticated.js
module.exports = function (req, res, next) {
// if user is authenticated in the session, carry on
if (req.user) {
next();
}
// if they aren't redirect them to the login page
else {
res.redirect('/login');
}
};
controller
var authenticated = require('./authenticated');
router.get('/protectedpage', authenticated, function(req, res, next) {
//Do something here
});
I don't know of a way to check all existing sessions, but Passport is handling the issuing of session ids. Try checking that you have req.user on your test endpoint after logging in
Auth0 documentation describes how to set up express-jwt middleware to protect endpoints. The trouble is that the documentation doesn't seem to cover how you get a valid JWT in the first place.
On the angular side, there's documentation on using angular plugins to implement a login page, that's fine. How would one implement a route using express that would take a username/password and return to the client the appropriate JWT such that subsequent requests would be authorized?
I think I may be missing a basic concept about JWT here; via Auth0, when using Username-Password-Authentication, my guess is that Auth0 acts as the repo for those credentials. There's documentation out there about wiring passport to auth0 and JWT, the problem with those is that this documentation assumes that the username/password database is some MongoDB instance locally...I want to avoid that type of setup which was an initial attraction with auth0.
Are there sample projects that cover this, showing how to get a valid JWT on a back-end, without some separate front-end angular app requesting it first?
I use passport.js built in local strategy for authentication and store user information in a JWT that I read on routes that require authorization.
User id's can be serialized/deserialized into and out of the express sessionto obtain the user identifier using the auth token (JWT) in the request. This is in my opinion the best approach since it limits the amount of data stored on the client and provides better security than storing any user information. Here's an example of this in express:
//Set a session secret
var secrets = { sessionSecret: process.env.secret || 'my secret string'};
//Require express-jwt and set a secret for the cookie
var expressJwt = require('express-jwt');
var validateJwt = expressJwt({ secret: secrets.sessionSecret });
//Returns a jwt token signed by the app secret
var signToken = function(id) {
return jwt.sign({
id: id
}, secrets.sessionSecret, {
expiresInMinutes: 60 * 24 // 24 hours
});
};
//Set token cookie directly
var setTokenCookie = function(req, res) {
if (!req.user) {
return res.status(404).json({
message: 'Error during user validation'
});
}
var token = signToken(req.user.id, req.user.role);
res.cookie('token', JSON.stringify(token));
};
//Check to see if user is authenticated (call this when a route is requested)
var isAuthenticated = function(req, res, next) {
// allow access_token to be passed through query parameter as well
if (req.body && req.body.hasOwnProperty('access_token')) {
req.headers.authorization = 'Bearer ' + req.body.access_token;
}
// Validate jwt token
return validateJwt(req, res, next);
};
You can use these methods as middleware in express. Say the above code was token.js, you can force it to execute on each request to a route like this:
app.get('/employee', token.isAuthenticated, employeeController.getEmployees);
I haven't worked with angular but it works great on the backbone projects i've worked on and this process should work with any browser based client that can supply a X-auth cookie on each request. You can do this by using the ajax setup:
$(document).ajaxSend(function(event, request) {
var token = readCookie('token');
if (token) {
request.setRequestHeader('authorization', 'Bearer ' + token);
}
});
Here is an example of middleware that validates a users login and returns a token to the client that can be used on subsequent requests:
var validateLogin = function (req, res, next) {
var username = req.params.username;
// Authenticate using local strategy
passport.authenticate('local', function(err, user, info) {
if (err) {
return next(err);
}
if (!user) {
return res.status(404).json({
info: [{
msg: info.message
}]
});
}
// Send user and authentication token
var token = token.signToken(user.id, user.role);
res.cookie('token', token);
res.render('index', {token: token, user: user});
})(req, res, next);
};
#FrobberOfBits
This is to answer the follow-up Q posted by FrobberOfBits on Feb 6, 2016 at 3:04
I use auth0 for local + social media authentication.
The way auth0 works is, you hand over the approach to authenticate to auth0 ...either it be local with db or social media.
It is a bundled approach where local db and social media authentication is all bundled and provided as a service to you by auth0.
Hope this helps.
I am testing my MEAN stack based app on my local. For authentication feature i am using passportjs.
But while debugging i have to login every time i restart the server.
Is there a easy configuration which i can do to disable passportjs without chaning much of my code.
I got it working with the following piece of code. Only following change was required to skip authentication for my app.
var isAuthenticated = function (req, res, next) {
var isAuthorised = req.isAuthenticated();
// TODO remove followoing on production code
// Set user as authorised in local
isAuthorised = true;
req.user = {};
// id of whichever account you want to load
req.user._id = '12345whatever';
if (isAuthorised) {
return next();
}
// if the user is not authenticated then redirect him to the login page
res.redirect('/login');
};
Hope it helps someone.