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}`)
});
Related
I am trying to build a MERN stack Food Tracker application for a school project, I'm very new to the MERN stack, and I've come across a problem with making my server's Express routes private that I haven't been able to find a solution for through searching.
I've set up login functionality using Auth0. My routes and models are stored in separate files and my checkJwt middleware is stored in a separate file that I'm importing into my route files. I am also using cors. I've tested that everything is setup correctly for authentication by sending a request from my frontend to a route that was written directly into my server.js file, and I've also verified that my routes respond to my frontend when they are left public, but when I try to make my routes private, I get a 401 Unauthorized error.
checkJwt file:
const jwksRsa = require("jwks-rsa");
require("dotenv").config();
const audience = process.env.AUTH0_AUDIENCE;
const issuer = process.env.AUTH0_ISSUER;
module.exports = checkJwt = jwt({
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `${issuer}.well-known/jwks.json`,
}),
audience: audience,
issuer: issuer,
algorithms: ["RS256"],
});
Server.js Setup:
app.use(cors({ origin: appOrigin }));
app.options("*", cors());
const profile = require("./backend/routes/profile");
//Test Route
app.get("/api/messages", checkJwt, (req, res) => {
res.send({
msg: "Testing authentication.",
});
});
// External route
app.use("/routes/profile", profile);
profile.js - Route file:
//Import checkJwt
const checkJwt = require("../../config/checkjwt");
//Import model
const Profile = require("../models/Profile");
//Router
router.post("/update/:user_id", checkJwt, (req, res, next) => {
//Code to update user information in database//
});
And, just in case it might help, I'm using axios to make the request on the frontend. Here is that code:
const onSubmit = async () => {
try {
const token = await getAccessTokenSilently();
const response = await axios.post(
`${apiUrl}routes/profile/update/${sub}`,
{
data: qs.stringify({
username: username,
}),
headers: {
Authorization: `Bearer ${token}`,
},
}
);
setMessage(response.data);
} catch (error) {
setMessage(error.message);
}
};
I've tried rewriting the request, turning it into a Promise, but because the request works when the route is left public, and I'm able to make requests of private routes that are directly written into my server.js, I suspect the issue is somewhere in how I'm accessing those external routes. Any help is greatly appreciated.
*Edit: I realized this morning that there was another big difference between my test route and problem route that I hadn't considered. Test route is a GET route and problem route is POST. When I changed one of my GET routes in the profile.js route file to include the checkJwt, it worked fine. So the issue is that I can't POST. Looking into this issue now, but there isn't much out there on how to specifically setup POST routes or how they would be different than GET routes.
I looked at this tutorial from Auth0 website, which is using a Passport strategy: https://auth0.com/blog/create-a-simple-and-secure-node-express-app/#Accessing-Guarded-Routes
But I thought passport was the same as the checkJwt...just using different middleware to accomplish the same thing. Am I mistaken?
I originally followed this tutorial to set everything up: https://auth0.com/blog/complete-guide-to-react-user-authentication/
I also looked at this Auth0 Quickstart regarding permissions and scopes: https://auth0.com/docs/quickstart/backend/nodejs/01-authorization
It kind of made it sound like not adding scopes or permissions and setting it up like the private without scopes would allow full access to the routes, but now I'm back to wondering if I need to set up those permissions for post...but there isn't much documentation on how that works.
Can anyone point me in the direction of a better tutorial for securing and accessing my POST routes?
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.
I'm using cookie-parser, all the tutorial talk about how to set cookie and the time it expiries but no where teach us how to get the value of these cookie
For people that stumble across this question, this is how I did it:
You need to install the express cookie-parser middleware as it's no longer packaged with express.
npm install --save cookie-parser
Then set it up as such:
const cookieParser = require("cookie-parser");
const app = express();
app.use(cookieParser());
Then you can access the cookies from
req.cookies
Hope that help.
First note that Cookies are sent to client with a server request and STORED ON THE CLIENT SIDE. Every time the user loads the website back, this cookie is sent with the request.
So you can access the cookie in client side (Eg. in your client side Java script) by using
document.cookie
you can test this in the client side by opening the console of the browser (F12) and type
console.log(document.cookie);
you can access the cookie from the server (in your case, expressjs) side by using
req.cookies
Best practice is to check in the client side whether it stored correctly. Keep in mind that not all the browsers are allowing to store cookies without user permission.
As per your comment, your code should be something like
var express = require('express');
var app = express();
var username ='username';
app.get('/', function(req, res){
res.cookie('user', username, {maxAge: 10800}).send('cookie set');
});
app.listen(3000);
hope this will help you
const app = require('express')();
app.use('/', (req, res) => {
var cookie = getcookie(req);
console.log(cookie);
});
function getcookie(req) {
var cookie = req.headers.cookie;
// user=someone; session=QyhYzXhkTZawIb5qSl3KKyPVN (this is my cookie i get)
return cookie.split('; ');
}
output
['user=someone', 'session=QyhYzXhkTZawIb5qSl3KKyPVN']
Just want to add that we shouldn't be using modules to do trivial stuff. Modules are very convenient and fast forward development, but keep us from learning by creating infrastructural code.
I'm a professor not a boss so I value more programmers knowledge/skill development than to write code in lesser time without learning anything...
Back to business...
Unless you need signed cookies, or anything more complex, it's perfectly possible to write your own middleware to parse cookies and add the values to the rest of the pipeline as the module does.
app.use((req, res, next) => {
const { headers: { cookie } } = req;
if (cookie) {
const values = cookie.split(';').reduce((res, item) => {
const data = item.trim().split('=');
return { ...res, [data[0]]: data[1] };
}, {});
res.locals.cookie = values;
}
else res.locals.cookie = {};
next();
});
Anywhere you need to read the cookie it's available via res.locals.cookie, conveniently formatted as an object.
You could even add a custom cryptography strategy here to make sure no one is reading your cookie.
Just remember middlewares are ordered, so this one has to be added before any other middleware or route that uses the cookie.
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'm using Passport.js to authenticate with Google via OAuth (I'm using the passport-google-oauth strategy). It works fine, but I'm currently redirecting the user to "/", and I'd like to send them to "/" plus the current hash tag. I can send the hash value in a query string parameter, but I can't seem to set that value to the callbackURL property of the object that I'm passing to authenticate.
Can someone provide an example or explanation as to the correct way to do this? I'm not beholden to using the query string, it just seemed the most straight-forward route, but I'm open to using a session variable or something else, if that would be easier or better practice.
Thank you.
You can achieve this effect by storing the return url in the session.
// server
var app, express;
express = require('express');
app = express();
app.configure(function() {
app.use(express.cookieSession({secret: 'shh'}));
});
app.get('/auth/twitter', function(req, res, next) {
// to return to '/#/returnHash', request this url:
// http://example.com/auth/twitter?return_url=%2F%23%2FreturnHash
// on the client you can get the hash value like this:
// encodeURIComponent("/"+window.location.hash)
req.session.return_url = req.query.return_url;
next();
}, passport.authenticate('twitter'));
app.get('/auth/twitter/callback', passport.authenticate('twitter', {
failureRedirect: '/login'
}), function(req, res) {
var url = req.session.return_url;
delete req.session.return_url;
// redirects to /#/returnHash
res.redirect(url);
});