I am setting up a site with nodejs, express, mongoose and swig template following this tutorial :
Authenticate a Node.js API with JSON Web Tokens
In this tutorial the author uses Postman to set the token in the header.
I have googled for days to find out how I can set the jwt token in the header of my site, but it is not working for me.
If you want the client to include the token in it's request headers, you can use a cookie parser with express. (HTML5 Web Storage is another option). About Cookies:
Express can set the response headers to tell the client "add the token to a cookie".
Once the client sets the cookie with the token, the token will be in the client's request headers for each request. Let's get to baking with a little
npm install cookie-parser
Sprinkle on some
var cookieParser = require('cookie-parser')
app.use(cookieParser())
Access and set a cookie:
app.use(function (req, res, next) {
var cookie = req.cookies.jwtToken;
if (!cookie) {
res.cookie('jwtToken', theJwtTokenValue, { maxAge: 900000, httpOnly: true });
} else {
console.log('let's check that this is a valid cookie');
// send cookie along to the validation functions...
}
next();
});
You will probably want to do these things with the cookies (or whatever method you go with in the end):
set the cookie to hold the token when a user is authenticated.
check the cookie header value before allowing access to protected
routes.
send back an unauthorized status if a user doesn't have their token
when they are trying to access api routes that require a token.
May help someone in future...
Storing token in cookie with httpOnly:true flag is pretty secure from XSS attack but it can be vulnerable to CSRF attack.
Adding custom request headers for all routes in express using a middleware might be a feasible solution like that:
var token;
//asign 'accessToken' to 'token' in app.post('/login')
token=accessToken;
app.all('*' , (req, res, next) => {
if (!token) {
console.log('token: undefined');
} else {
req.headers.authorization = 'Bearer ' + token;
}
next();
});
this will add authorization=Bearer <token> header in each and every get request coming from browser. Now verify token in each secure route by adding this middleware:
let in app.get('/dashboard')
const authenticateToken=(req, res, next)=>{
var authHeader=req.headers['authorization'];
var token=authHeader && authHeader.split(' ')[1];
if(token==null){
return res.sendStatus(401);
}
jwt.verify(token, process.env.JWT_ACCESS_TOKEN, (err, user)=>{
if(err){
return res.sendStatus(403);
}
req.user=user;
next();
})
}
//in app.get(...)
app.get('/dashboard', authenticateToken ,()=>{
//stuff for authorized user
})
In case if you defined app.post('/login') in another file then,
export addHeader middleware as under:
//var to access token outside app.post('/login') route
var token;
app.post('/login' , (req , res)=>{
//authenticate the user
//create token
const accessToken=jwt.sign(user, secretKey);
//assign 'accessToken' to 'token' var
token=accessToken
//redirect to secure route
res.redirect('dashboard');
}
//middleware to add in your 'index.js' or 'app.js' file.
//export it only if you define app.post('/login') in another file
exports.addHeader = (req, res, next) => {
if (!token) {
console.log('token: undefined');
} else {
req.headers.authorization = 'Bearer ' + token;
}
next();
}
In index.js or app.js
//import file in which app.post('/login') is defined. let it is defined in controller/auth
const authController=require('./controller/auth');
//to add custom header in all routes
app.all('*', authController.addHeader);
Related
I'm using passport to authenticate using Google API, I'm sending a token by URL to the client (React app) which saves it in the localStorage.
I want to use that token : With every API call (get, post, put) I want to send that token to the server , but I didn't know how to verify that token on the server side.
Passport Startegy :
app.use(passport.initialize()); // Used to initialize passport
app.use(passport.session()); // Used to persist login sessions
passport.use(new GoogleStrategy({
clientID: 'IDxxxxx',
clientSecret: 'SecreXXX',
callbackURL: 'http://localhost:3000/callback'
},
(accessToken, refreshToken, profile, done) => {
// Directory API here
var userData = {
name: profile.displayName,
token: accessToken
};
done(null, userData);
Authentication :
app.get('/auth/google', passport.authenticate('google', {
scope: ['profile'] // Used to specify the required data
}));
// The middleware receives the data from Google and runs the function on Strategy config
app.get('/callback', passport.authenticate('google'), (req, res) => {
var token = req.user.token;
res.redirect("http://localhost:8000?token=" + token);
});
API in express (which contains CRUD methods) :
app.use('/api', movieRouter)
In react side : Getting the token
componentWillMount() {
var query = queryString.parse(this.props.location.search);
if (query.token) {
window.localStorage.setItem("jwt", query.token);
// appel a directory api (avec token) puis sauvergarder dans redux puis redirection vers liste demandes
this.props.history.push("/");
}
}
Doing API calls :
import axios from 'axios'
const api = axios.create({
baseURL: 'http://localhost:3000/api',
})
export const insertMovie = payload => api.post(`/movie`, payload)
I just need to send the token in every call and check it in the server side.
Thanks
You want to set the token in a header most likely, try changing your axios client to something like
const api = axios.create({
baseURL: 'http://localhost:3000/api',
headers: {
Authorization: `Bearer ${your_token_here}`
}
})
I'm not 100% sure if this is the correct header form that passport will be expecting, but it's the general idea you need to do.
If the token is correctly set in the header, session, or cookie by the client as noted by Bill Metcalf, then express is able to authenticate a route/endpoint by adding the passport.authenticate middleware function to the route, like so
app.use('/api', passport.authenticate('google', {failureRedirect:'/login'}), movieRouter)
Refer to http://www.passportjs.org/docs/google/ for more information
For every API that you want to verify the token, you can pass a verify token function (which I call 'isCorrectToken') before taking action like this:
router.get("/api", isCorrectToken, (req, res) => {
// your api content })
And then, this is our isCorrectToken function:
const isCorrectToken = (req, res, next) => {
const token = req.headers.authorization;
if(token){
const onlyToken = token.slice(7, token.length);
jwt.verify(onlyToken, accessToken, (err, decode) => {
if(err) return res.status(401).send({ msg: 'Invalid Token' });
req.user = decode;
next();
return;
});
}
else return res.status(401).send({ msg: 'Token is not supplied'});}
The number 7 is the length of 'Bearer ' (from Bill Metcalf's answer above).
I am trying to make Firebase authentication work on the server.
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser')();
const cors = require('cors')({origin: true});
//const expressSanitizer = require('express-sanitizer');
const app = express();
// Express middleware that validates Firebase ID Tokens passed in the Authorization HTTP header.
// The Firebase ID token needs to be passed as a Bearer token in the Authorization HTTP header like this:
// `Authorization: Bearer <Firebase ID Token>`.
// when decoded successfully, the ID Token content will be added as `req.user`.
const validateFirebaseIdToken = (req, res, next) => {
console.log('Check if request is authorized with Firebase ID token');
if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&
!(req.cookies && req.cookies.__session)) {
console.error('No Firebase ID token was passed as a Bearer token in the Authorization header.',
'Make sure you authorize your request by providing the following HTTP header:',
'Authorization: Bearer <Firebase ID Token>',
'or by passing a "__session" cookie.');
res.redirect("/login");
return;
}
let idToken;
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
console.log('Found "Authorization" header');
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split('Bearer ')[1];
} else if(req.cookies) {
console.log('Found "__session" cookie');
// Read the ID Token from cookie.
idToken = req.cookies.__session;
} else {
// No cookie
res.redirect("/login");
return;
}
admin.auth().verifyIdToken(idToken).then((decodedIdToken) => {
console.log('ID Token correctly decoded', decodedIdToken);
req.user = decodedIdToken;
return next();
}).catch((error) => {
console.error('Error while verifying Firebase ID token:', error);
res.redirect("/login");
});
};
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static("/public"));
app.use(cors);
app.use(cookieParser);
//app.use(expressSanitizer());
//app.use(validateFirebaseIdToken);=
app.set("view engine", "ejs");
// This HTTPS endpoint can only be accessed by your Firebase Users.
// Requests need to be authorized by providing an `Authorization` HTTP header
// with value `Bearer <Firebase ID Token>`.
exports.app = functions.https.onRequest(app);
app.post("/login", (request, response) => {
var idToken = request.body.token;
console.log("REQUEST BODY = " + idToken);
response.header("Authorization" , "Bearer " + idToken);
return response.redirect("dashboard");
});
app.get("/dashboard", validateFirebaseIdToken, (request, response) => {
response.redirect("/dashboard/new");
});
In the /login POST route, I am receiving the idToken as expected (and showed in the logs). It seems though, that the response is unable to preserve/maintain the header property Authentication: Bearer <Firebase ID token> set beforehand.
In fact, I sent a GET request in Postman to /dashboard by getting the idToken printed by the logs and setting it in the header of the request like Authorization: Bearer <idToken> and it worked perfectly.
Here it says that redirects are in fact new HTTPS requests and therefore don't preserve the header set in the response. What should I do in this case?
You have to send the Authorization header with every request. HTTPS functions are stateless. They don't remember anything from a prior request. So, you shouldn't depend on redirect behavior to retain state. Instead, the client needs to figure out where to go next and make the next request itself.
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);
}
})
// ...
I'm building an app with Node.js + Express, and I'm trying to use JSON Web Tokens for authentication. Right now I am at the point where once a valid username/password is entered, the server responds by sending the client a JWT.
This is where I get lost.
How do I send that token along with further requests to the server?
How can I send it as a header?
How do I send that token along with further requests to the server?
You can append in your req URL as query parameter.
Eg:
http://localhost:8080/api/users?token=tokenValue
You can save it in cookies and when you req a URL, it will fetch up this cookie containing your token. Use document.cookie to save token in your cookie
How can I send it as a header?
Using JQuery
$.ajax({
type:"POST",
beforeSend: function (request)
{
request.setRequestHeader("Authority", authorizationToken);
},
url: "entities",
data: "",
success: function(msg) {
}
});
At Server Side, you can do:
var token = req.body.token || req.query.token || req.headers['x-access-token'];
For Cookie Parsing, you can use: Cookie-Parser
var app = express()
app.use(cookieParser())
app.get('/', function(req, res) {
console.log("Cookies: ", req.cookies)
})
Further Reading:
https://scotch.io/tutorials/authenticate-a-node-js-api-with-json-web-tokens
client can set the access token either as header or query parameter or in request body. Following is a way to send via header:
$.ajax({
url: 'foo/bar',
headers: { 'x-access-token': 'some value' },
data: {}
}).done(function(result){
//do something
});
Best practice is to save the access-token in browser local storage rather than in cookie. Once you obtain the token once logged it.
Server, best way to include a authentication middleware above all secured routes, where token is required.
auth.middleware:
'use strict';
module.exports = function(req,res,next){
const jwt = require('jsonwebtoken');
const config = require('../config/config');
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['x-access-token'];
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, config.secret, function(err, decoded) {
if (err) {
return res.status(401).json({ success: false, message: 'Failed to authenticate token.' });
} else {
// if everything is good, save to request for use in other routes
req.decoded = decoded;
next();
}
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
};
routes:
//no token required
app.post('/signup',users.create);
app.post('/login',users.authenticate);
const auth = require('../middleware/auth.middleware');
//token required for below routes
app.use(auth);
app.get('/info',index.getInfo);
first you need to set the json token in client by using http cookie (res.cookie("token","yourtoken")) or using session
when user sends a request you need to send the token to server.you can read cookie by using req.cookie.token and verify it in middleware or use session
I'm testing out express-jwt and jsonwebtokens. I've never used this before and would like some help!
I've got the basics setup done and I only have one protected route.
app.use('/api', expressJWT({secret: 'cat'}));
Unfortunatley, i'm not able to access '/api' because it gives me this error
UnauthorizedError: No authorization token was found
If I use POSTman and issue a GET request with the following header
Authorization -> Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImJhc2hpciIsImlhdCI6MTQ1MTQ0MjM4NywiZXhwIjoxNDUxNDQyNjg3fQ.CnaLvS_oCEy_mp_9MSAmTTylJqQMI2Qlq9V3emAmN3E
Everything works fine and I'm able to access the content in '/api'.
But my issue is in my express application, specifically when I try to redirect the user to a new protected route.
User logs in and I create a new jwt token and redirect the user to '/api'
router.post('/login', passport.authenticate('local'), function (req, res) {
myToken = jwt.sign({
username: req.body.username
}, 'cat', {expiresIn: 60*5});
res.redirect('/api');
});
In this route, I set the headers and render the page.
router.get('/api', function (req, res) {
res.render('index', {user: req.user});
});
Unfortunately, I get the following error
UnauthorizedError: No authorization token was found
My goal is to be able to redirect a user to a protected route.
From my understanding, since /api is a protected route, express-jwt should be setting my authorization headers. Even if I try to manually set my headers using a middleware I still get an error.
Any help is greatly appreciated!
Thanks!
try something like res.redirect('/api?token ' + myToken);
Then to receive query inputs change the expressJWT normal function with a custom function.
app.use('/api', expressJWT({
secret: 'cat',
credentialsRequired: false,
getToken: function fromHeaderOrQuerystring (req) {
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
return req.headers.authorization.split(' ')[1];
} else if (req.query && req.query.token) {
return req.query.token;
}
return null;
}
}));
This was mostly from reading the expressJWT docs and other stack answers.