I have implemented the OAUth 2.0 flow as single authentication method for my express application.
In real situation, once a user connected using OAuth, a session is created and accessible server-side in req.session.passport.
In this session, there is the OAuth access token that is required to perform authenticated requests on my REST API. To be able to do so, I wrote a middleware that copies the token from req.session.passport to the Authorization header.
app.use(function(req, res, next) {
if (req.session.passport) {
req.headers['Authorization'] = req.session.passport.user.accessToken;
}
next();
});
Now, I would like to test this middleware without going through the entire oauth flow. Ideally, I would like to create a session manually and inject test data inside req.session.passport, then call any protected endpoint of my API to check that it can correctly perform authenticated requests.
I tried to manually set data inside req.session in the following test
it('Should be able to make an authenticated request with session data',
function(done) {
var req = request(app).get('/api/SomeProtectedEndpoint');
req.session = {
passport: {
user: {
accessToken : 'eade123d3ffwhatever'
}
}
}
req.expect(200, function(err, res){
if (err) return done(err);
done();
});
});
But if I display the contents of req.session inside my middleware, the manual data is gone. I believe it is removed by express-session.
How can I manually inject session data in a request for testing purposes ?
Related
I'm a little new to this. I have REST API made with Node.js and Express.js. Some routes have authentication middleware. To use those routes, a header has to be set with the user's auth token which gets verified. I have been doing this with no problem with static sites using local storage. I'm making my first dynamic site now (using Express) and for certain routes I have middleware that loads all the data I need to display the page. How do I access and use auth tokens now that I don't have local storage's help?
EDIT(for clarification):
So here is one of my api routes that fetches all transactions from a database(mongoDB).
app.get('/transactions', authenticate, (req, res) => {
Transaction.find().then((transaction) => {
res.send({transaction});
}, (e) => {
res.status(400).send();
});
});
This is the authentication middleware that gets run.
var authenticate = (req, res, next) => {
var token = req.header('x-auth');
User.findByToken(token).then((user) => {
if (!user) {
return Promise.reject();
}
req.user = user;
req.token = token;
next();
}).catch((e) => {
res.status(401).send();
});
};
Now on my express webserver, I have a following route, where I use getTransactions to fetch all my data. (which I display with handlebars)
router.get('/orders', getTransactions, (req, res) => {
res.render('orders.hbs', {
transaction: req.transactions.data.transaction
});
});
and this is the middleware
var getTransactions = (req, res, next) => {
axios.get('https://serene-wave-28270.herokuapp.com/transactions')
.then((response) => {
req.transactions = response;
console.log(req.transactions.data.transaction);
next();
}).catch((e) => {
console.log(e);
})
}
So when I was just making a static site without using express as a webserver, I would just have the user sign in and save the auth token in local storage. Also, I should note that the first two blocks are from my api, and the bottom two from webserver, both hosted separately on Heroku. I'm not sure if that's standard design so I thought I should mention it.
There's not a whole lot of detail in your question for exactly what you're trying to do, but I can explain the general concepts available to you in Express:
The usual scheme for Express is to authenticate the user initially and then set a session cookie that indicates that user has been authenticated. Since the cookie is automatically stored by the browser and then automatically sent from the browser to the server with every request, you will have that cookie which the server can then use to identify a server-side session and then you can use any info you want from the session (user identify or other state you store in the session object) when creating pages or responding to API requests for that user.
The NPM module express-session handles much of this work for you as it will automatically create a session object, a session cookie and hook the two together on every request.
If, on the other hand, you already have an auth token in the client and you just want that to be automatically communicated to the server with every request, then you can just put that auth token into a cookie and have the server look for it in the cookie on each request. You can even make it a bit more secure by setting the cookie to HttpOnly so that the auth token cannot be accessed from client-side Javascript (this will not affect the server's ability to access it).
There is not much detail in your question but here are a few thoughts.
You can either use cookies (as detailed by #jfriend00 below) or use the requests' headers to check for a valid authorization token (which I describe below)
In Express you can access the headers through req.headers so you can just write a middleware that you will call before your current middleware loading all the data to ensure that the user is authorized to continue (calling next() to call the next middleware) or using a custom Error type to flag the authentication error if he is not (calling next(err) to skip all the other middleware and jump to your error middleware)
For example (assuming you have a subclass of Error named AuthorizationError defined somewhere):
const express = require('express');
const AuthorizaztionError = require('<some path>');
const app = express();
function checkAuthTokenMiddleware(req, res, next) {
if (req.headers && req.headers.authorization) {
let token;
const parts = req.headers.authorization.split(' ');
if (parts.length == 2) {
const [scheme, credentials] = parts;
if (/^Bearer$/i.test(scheme)) { // or any other scheme you are using
token = credentials;
}
if (token === undefined) {
// access token - missing
return next(new AuthorizationError(
"Invalid access token.", // error_description
"invalid_token" // error
));
}
// add something here to ensure the token is valid
return next();
}
} else {
// No authorization header => invalid credentials
return next(new AuthorizationError(
"Authorization header required.", // error_description
"invalid_request" // error
));
}
}
// Add this in your route declaration
app.use(
"/auth/test",
checkAuthTokenMiddleware,
function(req, res, next) {
// do something
}
);
// this must come last
app.use(function errorMiddleware(err, req, res, next) {
// return something
if (err instanceof AuthenticationError) {
// do something for example
res.status(401).send(err.error_description);
} else {
// generic error handling, for example
res.status(500).send("Error "+err);
}
})
// ...
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.
Hello I am new to react and Nodejs. Currently I am trying to make a login component in react which contains a button through which app can authenticate the user through Facebook. I am using passport.js for authentication. I am also using reflux for handling any api call to node server.
Below is the respective code for each
actions.js
var Reflux = require('reflux');
module.exports = Reflux.createActions([
"facebookLogin"
]);
store.js
var Reflux = require('reflux');
var Actions = require('../actions');
var Api = require('../utils/api');
module.exports = Reflux.createStore({
listenables: [Actions],
facebookLogin: function(email) {
Api.FBrequest(email)
.then(function(response){
console.log('returned user for facebook from server : ' + response);
}.bind(this));
}
});
api.js
module.exports = {
FBrequest: function() {
return (
fetch('/auth/facebook', {
method: 'get'
}).then(function(response){
return response.json();
})
);
}
};
facebook.js
// file at server for handling passport authentication calls
module.exports = function(app, passport){
// call to facebook for authenticating user
// in response to this call, facebook will reply in /auth/facebook/callback with the user object
app.get('/auth/facebook', passport.authenticate('facebook', {scope:'email'}));
// TODO return error on failure of login instead of navigating to the login route
app.get('/auth/facebook/callback', passport.authenticate('facebook',
{failureRedirect: '/login'}), function(req, res){
// user successfully authenticated by facebook
// return the user object
return req.user;
});
};
When I click the fbLogin button on my app I get the following error
Fetch API cannot load https://www.facebook.com/v2.2/dialog/oauth?response_type=code&redirect_uri=…3A3000%2Fauth%2Ffacebook%2Fcallback&scope=email&client_id=1807765906116990. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
I want to know that is it correct approach to use fetch for directly sending the get to server for '/auth/facebook' and let the server handle the request and return with user object. If it is then where this is going wrong ? If it is not then what is the correct approach for achieving the same with react?
For anyone else who stumbles upon this question like I did tonight - we solved this error by having the login button wrapped in an a href tag - which then takes the user to our authorization route. Once the user is authorized, facebook then returns the user to us. Then, every time our component mounts, we make sure the state has our logged in user mounted. If it doesn't we make a get request via axios to a route which passes back the user object, and we set our state appropriately.
I'm using jwt token validation in my project to protect some importent data:
if (req.headers.auth) {
var token = req.headers.auth.split(' ')[5];
var payload = jwt.decode(token, 'blablabla...');
if (!payload.sub) {
res.status(401).send({
message: 'Authentication failed'
});
}
if (!req.headers.auth) {
return res.status(301).send({
message:'You are not authorized'
});
}
res.send(data);
} else {
res.header(404).send('Go away!');
}
Is it possible to use this method to protect the static rout, added using express static middleware?
UPDATE!!!
Ok, now jwt token validation set on my static route. But I have got another problem - how to send this token to node.js server BEFORE my angular app uploaded (beacose now it is blocked with new middleware) and started to insert tokens in to the http headers. Do I need some extra module, or maby my new middleware can request somehow that jwt token from the browser?
Yes, you can attach an extra middleware to execute for your express static resources like this:
var staticMiddleware = function(req, res, next) {
console.log('Hello from staticMiddleware!');
next();
};
app.use(staticMiddleware, express.static(__dirname + '/public'));
Note that if you do add this JWT token checking middleware you should only be returning a response in this code (eg res.send()) if authentication fails. If the JWT is valid, to allow the code to proceed to the static route, call next().
I would have two additional notes regarding your code:
Your if(!req.headers.auth) block will never be executed, you're already in side an if(req.headers.auth) block.
In all failure cases here (req.headers.auth is missing, or payload.sub is missing) you should return a 401 Unauthorized. 301 and 404 would both be incorrect.
I am using nodejs (express) with mongodb and I am trying to figure out how cookies work. I am currently able to let a user login and authenticate it. How do I bring cookies into play and how do I use cookies to query mongodb for the user's info to pull it onto the next page and pages after that, once they login.
Currently I have a route file that posts the login request and then redirects based on success to a userProfile page, I want to include user specific details on that page and then be able to show user other pages and have him return to his unique pages again while querying.
UPDATED CODE: (Can cookie be called the way it is called in the updated code?)
login post route file
exports.loginPost = function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err) }
if (!user) { return res.redirect('loginError'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
res.cookie('name', req.params.email, { expires: new Date(Date.now() + 900000), httpOnly: true });
return res.redirect('userProfile');
});
})(req, res, next);
};
read this first What does middleware and app.use actually mean in Expressjs?
then use cookieparser and cookiesession
app.use(express.cookieParser('yoursecretkeyhere'));
app.use(express.cookieSession();
and split your above function into an authentication middleware method and an authenticate POST handler.
Your authentication middleware just needs to check whether the session has an Authenticated flag and if not redirect to your login page. (if this is part of a single page app, just return a 401 and ask for credentials in your browser)
Your authenticate POST handler then checks the posted username and password credentials (or other credentials against a 3rd party api) and sets the session.Authenticated flag followed by a redirect.