I am using JWT in my node.js app. Everything with token works fine. I can get a token when I have logged in.
Here's how I check user auth.:
const jwt = require('jsonwebtoken')
try{
const token = req.headers.authorization
const decoded = jwt.verify(token, 'secret')
req.userData = decoded
next()
}
catch(err){
return res.send("Auth error")
}
I can access protected routes if I change token value with that token I've got after log in.
But I want to save the token (on user's side???), and each time when the user tries to access protected routes (from frontend), send token as req.headers.authorization, so that the token can be verified, and the user can access the route.
So, how to save and later send the token, that has been generated after user's log in each time when protected routes are linked to?
Thank you.
(I am not using any javascript frontend frameworks)
If it's a token for authentication, you can use a httpOnly cookie to prevent XSS attacks, which is a risk with local storage.
To save a JWT in a cookie in express:
const accessToken = jwt.sign();
return res
.status(201)
.cookie("accessToken", accessToken, {
httpOnly: true,
maxAge: (1000*60*5), // 5m
secure: true,
signed: true
})
.end();
When getting from requests:
const { accessToken } = req.signedCookies;
Inside your app.js:
const express = require("express");
const cookieParser = require("cookie-parser");
const app = express();
app.use(cookieParser("secret"));
With httpOnly cookies your requests automatically send the cookie along with the request (only if the cookie belongs to the same domain). Just make sure your CORS and http client are properly configured to handle the cookie
Common approach to save it to a local storage. Just keep in mind local storage size and other limitations in different browsers.
Related
I'm Developing a full-stack web application with Node js and express js as the backend and using Next Js for the front end and JWT for the authentication, I'm setting the JWT token with the cookie in the browser. The cookie is set successfully in the browser, but whenever I try to get the cookie from the Express server to check whether the user is valid or not, the cookie does not receive from the browser. I tried several ways to get the cookie from the browser but it doesn't respond. but it is receiving the cookie token from the Postman/thunder client.
note: The frontend port is 3000 and the backend port is 5000.
You can suggest another way for the user authentication with next js with external express server.
res.cookie("userToken", token, {
expires: new Date(Date.now() + 9000000000),
})
and the JWT verify code is here
const jwt = require("jsonwebtoken");
const jwtToken = (req, res, next) => {
try {
const userToken = req.cookies?.userToken;
if (userToken) {
const verify = jwt.verify(userToken, process.env.JWT_SECRET);
req.user = verify;
}
next(); // for getting the api result back to client
} catch (error) {
next(error);
}
};
module.exports = jwtToken;
The scenario:
I have a page in my app which users seem to be leaving open and returning to well after their session / CSFR have expired
When they try and submit data from that page and the session is rejected, I have a feature to open up a log-in modal
On submitting that, there's first a request to the server to issue a new CSRF token
On receiving that, it then uses that new token, and sends it with their user credentials to generate a new session
They can then continue with what they were doing before
The problem is that at step 4, the request fails due to an invalid CSRF token.
On the server side, the two routes look like this (using express, csurf):
const csrf = require("csurf");
const csrfProtection = csrf({ cookie: true });
const passport = require("passport");
//Issue new ajax csrf login token
app.get("/ajaxLoginToken", csrfProtection, function (req, res) {
const csrfToken = req.csrfToken();
res.json({ csrfToken });
console.log("New csrf token issued", csrfToken);
});
//AJAX login
app.post("/ajaxLogin", csrfProtection, function (req, res, next) {
passport.authenticate("local", async function (err, user, info) {
//This point is never reached due to csrf mismatch
On the browser, step 3 looks something like this:
async requestNewToken() {
let response = await fetch("/ajaxLoginToken", {
method: "GET",
});
let responseBody = await response.json();
//Update csrf token
document.querySelector('meta[name="csrf-token"]').setAttribute("content", responseBody.csrfToken);
document.cookie = `_csrf=${responseBody.csrfToken}`;
Then to send step 4, it adds the csrf token to the request before sending
async login({email, password}) {
let response = await fetch(request.url, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"X-CSRF-Token": document.querySelector('meta[name="csrf-token"]').getAttribute("content")
},
body: JSON.stringify({email, password}),
//this always fails due to csrf token
When I try debugging, the new token is being received on the log in request, but it's rejected. Any help would be amazing - I've been bashing my head at this for days.
Hard to say without a minimal running example, but I guess you didn't include the CSRF cookie. By calling csurf with csurf({ cookie: true }) you decided to maintain the CSRF Token on client side in form of a cookie, by default called _csrf. You already did it. This mechanisms is called Double Submit Cookie.
Up to now, you have to send the CSRF token twice with every request: As an Header or a request param, depends on how you configured it, see csurf - value for the defaults, AND in form of the cookie. As far as I can see, you never send the cookie in your AJAX request. You send the CSRF Token as Header X-CSRF-Token but you have to include the cookie as well.
Just a guess, but could it be possible that you have to configure your fetch call by using the credentials option? By default, fetch doesn't include cookies. You have to incude them by using credentials: "include". Be aware, you can use credentials: "include" only if the Access-Control-Allow-Origin header is configured, wildcards are not possible when using include.
To sum it up, I guess you never send the cookie in your AJAX call and you have to configure it.
I have been pulling my hair out the last couple of days. No matter how many google searches I make I can't find an answer so I come here as a last resort in hope of some help.
I am creating a full stack application on the MEAN stack. My login works. It is verifying that the email and password is matching a user's email and password in my database. Now I want to send a JWT token to my client so I can keep the user signed inn. I understand how JWT works and I have generated one already
const ACCESS_TOKEN_SECRET = "a random string";
const payload = {"username": login_result[0].username};
const accessToken = jwt.sign(payload, ACCESS_TOKEN_SECRET);
This is where my problems start. I want to store the token in a cookie, to prevent XSS attacks. I have tried all kinds of ways to store the JWT in a cookie. Most forums wrote that I should do
res.cookie('access_token', accessToken);
As I understand, this should automaticly store the JWT in a cookie, on my client, under the name "access_token". However this does not work. When this line is in the code, nothing is happening. By "nothing is happening" I mean that the client code does not execute.
If a password/email is innvalid, I return an error code.
if(res.msg == "403"){
this.showLogErrorMsg = true;
}
This part works.
The else statement looks like this
else{
console.log("Valid username and password");
window.location.href = "http://localhost:4200/profile";
}
Meaning, if the log in is wrong, it will print an error (and it does), if the log in is correct, they are suppose to get redirected. This does not work. It did work when I did
ret.json({"access_token":accessToken});
but does not work with
res.cookie('access_token', accessToken);
This is what I don't understand. It does not store the access_token in cookies and it does not execute the code inside the else statement. There are no error messages on my server or client. Further more, every guide or tutorial says to use the res.cookie, since I am using Express as my webserver.
I have even tried adding the following options:
res.cookie('access_token', accessToken,{domain: domain, path: '/',httpOnly:false,
secure:false,sameSite:false});
The solution is to either, somehow make the res.cookies work, which I really want to do due to it being "what everybody else is using" and it seems really chill. Or, send the JWT token as res.json and then save the token to a cookie in angular. However, does this open you up for XSS? And if not, how can I save something to a cookie in angular?
Thank you guys in advance.
This is a basic express app that sets, unset, and displays cookies.
set GET /login
unset GET /logout
display GET /
This is not dependent on the client (angular)
# main.js
const express = require('express')
const cookieParser = require('cookie-parser')
const app = express()
app.use(cookieParser())
const port = 3000
const accessToken = 'XXXXXXXXXXXXXXXXXXXXX';
app.get('/', (req, res) => {
res.json(req.cookies)
});
app.get('/login', (req, res) => {
res.cookie('access_token', accessToken, { path: '/', httpOnly: true, sameSite: "strict" })
res.send('"access_token" cookies was set !')
});
app.get('/logout', (req, res) => {
res.cookie('access_token', accessToken, { maxAge: 0 })
res.send('"access_token" cookies was unset !')
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
});
I am creating login module.
User will enter Username and Password.
If user validate successfully then Server will return JWT token.
I will use the JWT token to validate the different API call in React js.
Now my concern is that I found some article regarding this then I found that We can use http only cookie. How can we implement httponly cookie method to store JWT ? Is it safe?
HttpOnly cookies are safe in that they are protected from browser access via the Document.cookie API, and therefore are protected from things like XSS attacks.
When your user is successfully validated, the server should generate a jwt token and return it as a cookie to your client like so:
return res.cookie('token', token, {
expires: new Date(Date.now() + expiration), // time until expiration
secure: false, // set to true if you're using https
httpOnly: true,
});
The cookie will be accessible via incoming http requests from your client. You can check the jwt value of the cookie with an authorizing middleware function to protect your API endpoints:
const verifyToken = async (req, res, next) => {
const token = req.cookies.token || '';
try {
if (!token) {
return res.status(401).json('You need to Login')
}
const decrypt = await jwt.verify(token, process.env.JWT_SECRET);
req.user = {
id: decrypt.id,
firstname: decrypt.firstname,
};
next();
} catch (err) {
return res.status(500).json(err.toString());
}
};
Reference for more details: https://dev.to/mr_cea/remaining-stateless-jwt-cookies-in-node-js-3lle
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.