On my client-side I'am saving a token in the user's cookies :
this.socket.on('server:authentication', function(tokenObject){
if(tokenObject){
sessionStorage.setItem("token", tokenObject);
this.setState({ submitted: true});
}
}.bind(this));
Then I want to read that token on the server side, but when I do :
app.use(cookieParser());
app.use(function(req, res, next){
const { token } = req.cookies;
console.log(token);
...
});
I get "undefined" on the console. Same thing when I try console.log(req.cookies.token). How can I achieve that?
You need to set the cookie in your client-side code, you're currently setting is in sessionStorage, which is a different beast.
this.socket.on('server:authentication', function(tokenObject){
if(tokenObject){
document.cookie = 'token=' + tokenObject;
sessionStorage.setItem("token", tokenObject);
this.setState({ submitted: true});
}
}.bind(this));
See How to set cookie and get cookie with JavaScript
Related
I wanted to use my login data (username, ID) and store them in other tables so that I could access the other data stored in those tables. I am using Node.js and express for my server, and for the session I am using the express-session module.
Here is the app.js and the session middleware and its default options
import session from 'express-session';
app.use(session({
name:'test',
secret: "thisismysecrctekeyfhrgfgrfrty84fwir767",
saveUninitialized: true,
resave: false
}));
//some other stuff
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use((_, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
app.use((req,res,next) => {
console.log('req.session',req.session);
next();
});
After this I use router for all my routes,
Here is the login route where I save my username into the session
router.post('/login', (req, res, next) => {
// checks if email exists
User.findOne({ where : { //
name: req.body.name, //
}})
.then(dbUser => {
if (!dbUser) {
return res.status(404).json({message: "user not found"});
} else {
// password hash
bcrypt.compare(req.body.password, dbUser.password, (err, compareRes) => {
if (err) { // error while comparing
res.status(502).json({message: "error while checking user password"});
} else if (compareRes) {
// password match
const token = jwt.sign({ name: req.body.name }, 'secret', { expiresIn: '1h' });
const sessName= req.body.name;
req.session.name=sessName;
res.status(200).json({message: "user logged in", "token": token});
} else { // password doesnt match
res.status(401).json({message: "invalid credentials"});
};
});
};
})
.catch(err => {
console.log('error', err);
});
});
After the user enters the Login info it goes from the login route to the Auth route
router.get('/private', (req, res, next) => {
const authHeader = req.get("Authorization");
if (!authHeader) {
return res.status(401).json({ message: 'not authenticated' });
};
const token = authHeader.split(' ')[1];
let decodedToken;
try {
decodedToken = jwt.verify(token, 'secret');
} catch (err) {
return res.status(500).json({ message: err.message || 'could not decode the token' });
};
if (!decodedToken) {
res.status(401).json({ message: 'unauthorized' });
} else {
res.status(200).json({ message: `here is your resource ${req.session.name}` });
};
});
If I console log the session it shows the username on the terminal, for other routes when I store some data onto the session and access it from other routes it works fine, but when I store the username from the login route it doesn't show up when I try to access it.
router.get('/session', (req,res,next)=>{
if (req.session.name) {
res.json({name: req.session.name})
} else {
res.json({name: null});
}
})
I am new to Node.js so it would be helpful if anyone could answer this.
After
const sessName= req.body.name;
req.session.name=sessName;
the name gets saved in the session from the login route, but it shows me a null value when I access it from other routes. Plus is it normal for the session to end and the username to disappear after I restart my server.
Since both /login and /private look like they are being made by code, the problem is probably caused by that code not capturing the session cookie, retaining it and making sure it is sent with all subsequent requests on behalf of this same user.
That session cookie is the key to making express-session work. Upon first engagement with a client, the express-session middleware sees that there is no session cookie attached to this request. It creates a new one with an encrypted unique key in it, adds that key to the internal session store and returns that cookie with the http response.
For the session to work, the client that receives that cookie must retain it and send it with all future http requests from this client (until it expires). Since these http requests to /login and /private are being made programmatically, your code has to be set up properly to retain and send this cookie. It will depend upon what http library you are using to make these http requests for how exactly you go about retaining and resending the session cookie.
If these are made from Javascript running in a browser in a web page, then the browser will retain the cookie for you and you will just need appropriate settings on the http requests to make sure the cookie is sent. If you are doing cross-origin requests, then things get a little more complicated.
If these are made from your own code in your own app, then the precise steps to include the cookie will depend upon what http library you're using. Many http libraries have something often referred to as a cookie jar that can be associated with a given request/response to help maintain and send cookies.
Now that you've shown your client code, that code is using fetch() to make these calls. That's the http request library your client is using.
Because it appears this may be a cross-origin request, to be sure that cookies are sent with requests made by fetch(), you can add the credentials: "include" option like this:
fetch(`${API_URL}/private`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
credentials: "include",
}).then(...)
If fetch() doesn't send credentials (which include cookies), then your session cookie won't be sent and your server will think it has to create a new session on each new request so your session will never be maintained from one request to the next.
I am new to express and I am trying to create session when user logs in to my app.I am using passport for authentication. To login user I am creating basic strategy and on success of basic strategy I am creating a JWT token which I am storing at client side in cookie and I use JWT strategy for subsequent requests.
But I notice that express-session is creating one session when I am logging in which has Basic header and another session for subsequent calls which has JWT header.
Here is my code where I am saving session on login
signin = function(req, res, next) {
passport.authenticate('basic', function(err, user, info) {
if (err) { return next(err) }
if (!user) {
return res.status(401).json({ error: 'message' });
}
var token = jwt.encode({ user: user}, config.db.secret);
res.cookie('token', token, { maxAge: 900000, httpOnly: true, path: '\/', Secure: true });
res.status(200).json({ success: true, firstName: user.firstName });
delete user.password;
req.session.user = user;
req.session.save();
})(req, res, next);
};
But when I debug this code it shows one sessionID in req.sessionID and it show different sessionID in req.sessionID in the following code which as JWT authentication
listProducts = function(req, res) {
debugger;
//here req.session.user is undefined which I have saved at login. and sessionID is also different
res.json({ demo: 'response' });
};
I am expecting it to be same sessionID throughout the life cycle till user logs out of my app.
Why is this exactly happening? What is the solution to it?
You are sending the response before saving the session.
Try saving the session, then sending the response instead.
express-session modifies res.end to make it perform express-session specific tasks introducing the sequential coupling you were victim of: https://github.com/expressjs/session/blob/master/index.js#L249
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 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);
I wrote a JS script for a webserver that includes authentication using the passport and the digest strategy. I am not using sessions, but I have tried using sessions and it does not change the results. The browser requests the "/login" route and displays a built-in login dialog. Authentication works fine, but I can't get the user to "logout." The problem seems to be that the browser remembers the login credentials and resends them automatically. The end result is that the user must close the browser completely to log out, but that is a problem for this application. I know that there must be a way to tell the browser not to do this, but I haven't figured it out.
I figured out a hack to get the browser to display the login dialog again; force the authentication function to return a false. However, I haven't figured out a way to do this per-session. Right now, if one person logs out, everyone gets logged out. It's not a workable solution.
Can anyone tell me what I'm doing wrong here? One thing I'm wondering is whether I'm returning the proper response to the browser when it POSTs to the /logout route (see end). I return res.json(""), but maybe there's a different response I should send to tell the browser to forget the credentials for the session?
My code follows. Any insight is greatly appreciated. Thank you in advance.
T
var passport = require('passport'),
DigestStrategy = require('passport-http').DigestStrategy;
var express = require('express');
var app = express();
app.configure(function () {
app.use(
"/", //the URL throught which you want to access to you static content
express.static('./www') //where your static content is located in your filesystem
);
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.session({ secret: 'keep moving forward' }));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
});
app.listen(80); //the port you want to use
/**
* CORS support.
*/
app.all('*', function(req, res, next){
if (!req.get('Origin')) return next();
// use "*" here to accept any origin
// For specific domain, do similar: http://localhost'
// Use an array for multiple domains, like [http://localhost', 'http://example.com' ]
res.set('Access-Control-Allow-Origin', '*' );
res.set('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.set('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Authorization');
next();
});
//
// Configure passport authentication
//
// Used to force the browser to display the login screen again.
var forceLogin = false;
passport.use(new DigestStrategy({ qop: 'auth' },
function(username, done ) {
if ( !forceLogin )
{
return done(null, username, "nimda");
}
else
{
//
// Forces the browser to request the user name again by returning a failure to its last request.
//
console.log ( "forcing user to log in" );
forceLogin = false;
return done(null, false);
}
));
passport.serializeUser(function(user, done) {
console.log( "serialize user " + user.toString() );
done(null, user.toString());
});
passport.deserializeUser(function(id, done) {
console.log( "deserialize user " + id.toString() );
done(null, id);
});
app.post('/login', passport.authenticate('digest', { session: true }),
function(req, res) {
console.log( "/login");
res.header('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0');
res.json({ id: req.user.id, username: req.user.username });
});
app.post('/logout', function(req, res){
req.logOut();
// NOTE: Same results as req.logout
//
// req.session.destroy(function (err) {
// res.redirect('/');
// });
res.redirect("/");
// flag to force a login
forceLogin = true;
console.log( "logout");
// Is this the proper return to the browser?
return res.json("");
});
You could try the following
req.session.destroy()
req.logout()
res.redirect('/')
Saving the new state of the session is important if you're using some form a session store for persistent sessions (eg. Redis). I tried the above code with a similar setup and it seems to work for me
Server needs somehow notify web browser that he need to refresh login info (MD5 digests).
For that you can send 401 error code and usually browser will show login popup message.
By the way, all these tricks with sessions are useless because browser already have all required info for automatic logging in.
So you can try next code:
req.logout();
res.send("logged out", 401);
A bit late to the party, but I found this thread on Google while searching for answer and nothing worked for me. Finally, I was able to solve it so thought I could post solution here for anyone that might read this in the future.
So basically, I use node.js, express, passport(local) and rethinkdb as my storage, and I was having problem with req.logout(); not logging me out.
My setup:
var express = require( 'express' );
var passport = require( 'passport' );
var session = require( 'express-session' );
var RDBStore = require( 'express-session-rethinkdb' )( session );
I tried a lot of stuff and nothing was working, so finally I decided to manually do it, by removing session record from database myself.
Here is the code I used:
app.get( '/logout', function ( req, res, next ) {
if ( req.isUnauthenticated() ) {
// you are not even logged in, wtf
res.redirect( '/' );
return;
}
var sessionCookie = req.cookies['connect.sid'];
if ( ! sessionCookie ) {
// nothing to do here
res.redirect( '/' );
return;
}
var sessionId = sessionCookie.split( '.' )[0].replace( 's:', '' );
thinky.r.db( 'test' ).table( 'session' ).get( sessionId ).delete().run().then( function( result ) {
if ( ! result.deleted ) {
// we did not manage to find session for this user
res.redirect( '/' );
return;
}
req.logout();
res.redirect( '/' );
return;
});
});
So I hope this helps someone :)
res.redirect('/') will end the response, you can not call subsequently write(), end(), send(), json() ...etc
Just like this:
app.post('/logout', function(req, res){
req.logOut();
res.redirect("/");
});
deserializeUser should return a user instead of id:
passport.deserializeUser(function(id, done) {
findUser(id, function(err, user) {
if(err) {
done(err)
} else {
done(null, user);
}
}
});
I have taken a short discussion, I realize that digest username/password store on browser (not on server), so req.logout not help, there no way to clear it from server. You just close browser and open again that mean logout.
You can use a variable in cookie or session to mark the session as logout, but I think we should not do not, because username/password still in browser.
It's digest!