I am using the latest version of Express (4.x) and Passport.js (0.13) in my Node.js application. I can get the current user object inside of a route by using req.user and working with the object, which works. However, for situations outside of routing, is there a method I can call or global object I can access which contains the same information?
The reason I want to do this is I have a Socket.io listener which waits for a message string to be sent. It then gets the currently logged in user, gets their ID and uses that for the database association. This takes place outside of a route obviously.
Passport.js uses session to deserialize the user and have it store at express.js req object. So to authenticate the user in Socket.io, you need to find the session using the cookie, lookup the session in the session store and finally get the user from the session.
You can use Socket.io middlewares to achieve this. Here is a pseudo-code to get you started:
var io = require('socket.io')();
io.use( (socket, next) => {
if (socket.request.headers.cookie) {
// find the session id in cookie
const sessionID = socket.request.headers.cookie['connect.sid']
// lookup the sessionID in session store
MongoStore.get(sessionID, (err, session) => {
// get the userID from the session
const userID = session.passport.user;
// Lookup user using the UserID
User.find(userID, (err, user) => {
//save the user in socket
socket.user = user;
next();
})
})
}
next(new Error('Authentication error'));
});
Related
I am making an API server where I can login and see some information. Whenever I log in I get a JWT token which contains my username. I want to store this username in a variable whenever I want to use it. This is the code I use to verify my token when I want to enter certain webpages.
import jwt from "jsonwebtoken";
function authenticateToken(req, res, next) {
const authHeader = req.header("access-token");
if (!authHeader) return res.sendStatus(401);
jwt.verify(authHeader, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
export default authenticateToken;
It checks if the header contains a JWT token. If so it verifies it. Then at the bottom the user gets retrieved, but I dont know how to get this value.
On my routes I secure them like this:
router.get("/", authenticateToken, getData);
within the getData method im displaying data from a mysql database. I want to add a check to see if my username is equal to the data in getData (since you can only see certain data).
In getData i get my data like this:
connection.query(
"SELECT * FROM data WHERE userid = ?", [user.username],
(err, rows) =>
On the [user.username] spot I want to retrieve the username within the JWT token. I want to do this using a method, but I cant seem to get it. How can I get my username?
You could try to change your code a little bit to pass the request to the getData function, as you store the authenticated user within the request.
Try something like this:
router.get("/", authenticateToken, async (req, res) => {
getData(req);
});
I am using Firebase Cloud Functions with Express and Firebase Hosting to serve my multi-page site. I have successfully implemented server-side cookies as explained here and as implemented below:
function verifyLogin(request, response, next) {
const sessionCookie = request.cookies.__session || '';
firebase.auth().verifySessionCookie(sessionCookie, true /** checkRevoked */ )
.then((decodedClaims) => {
//serve content for user
return next();
}).catch(error => {
// Session cookie is unavailable or invalid. Force user to log in.
console.log(error);
response.redirect('/login');
return;
});
}
app.get('/', verifyLogin, (request, response) => {
var page_title = "Home";
response.render('index', {
page_title,
});
});
I am using the Firebase Web SDK (JavaScript) to access the Firebase Cloud Firestore. In order to do this, I need to get the idToken on the client side for the currently-logged-in user, like so:
firebase.auth().onAuthStateChanged(firebaseUser => {
if (firebaseUser) {
firebase.auth().currentUser.getIdTokenResult()
.then((idTokenResult) => {
// Access the Firebase Cloud Firestore here
});
}
});
This seems redundant to me, since I already know which user is signed in via my server cookie, but how else can I get the idToken without another call to Firebase Auth?
Is there a way to extrapolate it from my session cookie somehow and pass it to my client from my cloud function as a variable?
Or is there another way to look at this philosophically that I am overlooking?
In the same way you can listen to user sign-in state changes using onAuthStateChanged(), you can also listen to user ID token changes using onIdTokenChanged(). You will only need to use a new token when the observer you attach shows that a new one appears (for example, when it's refreshed every hour by the SDK).
i was wandering what is the best way to avoid sending the user data on every request,
lets say i want to add product from user's account, i have to send the user. or i want to order something, i have to send the user.
i thought about something like this:
app.use(async (req, res, next) => {
if (!req.body.userId) {
return next();
}
const user = await enteties.User.findByPk(req.body.userId);
req.user = user;
next();
});
but it also requires me to send the user on evey request..
there must be a way to avoid sending the user data to the server on almost every request.
also, it will make all my requests of type "post" since i have to send the user, and even "get" requests are now become "posts", for sure this is not correct
If you implement your JWT token correctly you don't need to send the logged in user id.
JWT tokens contain a payload section that is basically any JSON data you want to set. This is basically your decentralized session stored in the user's machine. When creating a JWT token you'd normally do something like:
const jwt = require('jsonwebtoken');
const config = require('./config');
function generateToken(user) {
let payload = {
sub: user.id
};
return jwt.sign(payload, config.secret, {
algorithm: 'HS512', // choose algorithm appropriate for you
expiresIn: config.expires
})
}
That payload part allows you to send user identifying information. In the case above, the user id. To get that id from a request simply verify it:
app.use((req, res, next) => {
const token = req.get('Authorization');
jwt.verify(token, config.secret, (err, payload) => {
if (err) {
next(err);
}
else {
req.user = payload; // user.sub is the user id
next();
}
});
});
Or you can use a library such as express-jwt to do it for you:
const expressJwt = require('express-jwt');
const express = require('express');
const config = require('./config');
const app = express();
app.use(expressJwt({ secret: config.secret }); // use express-jwt like any
// middleware, you can even install
// it on specific routes.
Now in your controller/route you can simply extract the payload in the req.user object. Invalid tokens or requests without tokens will completely skip your handler and immediately return an error or unauthorized response:
app.get('/some/endpoint', (req, res) => {
console.log('user is', req.user.sub); // note: req.user is our payload
});
Additional tricks:
As I mentioned, the payload is basically user defined. If you need to keep track of other user information such as roles or permissions you can store them in the JWT token:
// Example payload
let payload = {
sub: user.id,
admin: user.role === 'admin',
gender: user.gender
};
This reduces the number of database requests needed to process the user. Making the authentication system completely decentralized. For example you may have a service that consumes this JWT token that is not connected to your user database but need to check if user is admin. With the right payload that service does not even need to have access to the user database.
Note however that the payload is not encrypted. It is just base64 encoded. This means that the information in the token can be easily read by anyone with access to it (normally the user but beware of 3rd party scripts). So ideally you shouldn't store dox-able information in the payload if you have 3rd party scripts on your website (then again, it is highly unusual these days for anyone to write the entire front-end from scratch without any libraries or frameworks)
Also note that the more you put in your payload the larger your token will be.
How can I tell when express creates a new session? I'm using a mongodb session store.
I'm having an issue with extra sessions being created, and want to narrow down on the problem by tracking which urls requests are triggering new sessions to be created.
This is what I ended up doing.
app.use(function(req, res, next) {
if (!req.session.returning) {
// session was just created
req.session.returning = true
} else {
// old session
}
next()
})
You can hook into the genid callback. By default express-session generates its own session IDs.
But you can generate the session ID yourself by specifying a genid callback. Within this callback you can log that a new session is being created (and then return a unique session ID).
Here is an example, adapted from the express-session README:
app.use(session({
genid: function(req) {
console.log('New session was created');
return genuuid(); // generate a UUID somehow and return it
},
secret: 'keyboard cat'
}));
The answer by Nate is fine. I'm answering because of 2 additions I'd like to make.
In case you also want the kind of sessionID that express-session would otherwise generate. You can do this:
const uid = require('uid-safe').sync;
//...
genid: function(req) {
console.log('New session was created');
// do what you want to do here!
return uid(24); // <-- the way express-session generates it
},
Be aware that you cannot use an async function for genid. Express-session calls this function without await, so that would get a promise instead of the uid.
I am considering using the Passport Library (http://passportjs.org/) for authentication in a Node project.
I am confused by the following passport session functions:
passport.serializeUser(function( user, done ) {
done( null, user.id );
});
passport.deserializeUser(function( id, done ) {
user.get( id, function ( err, user ) {
done( err, user );
});
});
I am wondering:
1) Do these get called for every request that needs to be authenticated? Or are they just called once when the session is first created?
2) How do I access the information that is in "user" from other parts of my script?
3) For requests that need to be authenticated, where do I put any additional logic. eg To check if an allowable user idletime value has not been reached.
Thanks (in advance) for your help
1) serializeUser is called when creating a session for the user (when authentication was successful). This is used to store some sort of identifying information (like a unique user-id) about the user in an Express session.
deserializeUser is called for every request, and takes that piece of identifying information from the session to somehow convert it back to a full user record by means of a database query, perhaps, but that's really up to you: instead of just storing the user id you could also store the entire user record in the session, but it depends on the type of user record and the session storage you're using if that's possible (for example, using express.cookieSession would limit the amount of data you can store in a session).
This is what storing the entire user record could look like:
passport.serializeUser(function(user, done) {
// Here, 'user' is the result of the function called by 'new LocalStrategy()'; when
// you call done() below, that result will be stored in the session.
done(null, user);
});
passport.deserializeUser(function(user, done) {
// Here, 'user' is what's stored in the session by serializeUser()
done(null, user);
});
2) Passport populates req.user which you can use in routes or middleware.
3) You could make a middleware to implement such checks. This might be a good starting point.