managing user sessions for multiple devices node js using JWT - node.js

I am making an application in which I am using JWT for maintaining sessions. When any new user registers then I provide a JWT token to user and store it in my database as well as in users browser. When user log out, then i delete that token from browser and my database.
But I want that if user is logged in from multiple devices then it it will log out from one device, it does not logout from other devices as well. How do I achieve this?

First, JWT are not supposed to be able to "log out", but to automatically expire, that is why you are supposed to set short expiresIn times.
It is because with JWT, sessions are handled by the client, it is not the server's responsibility to log out users, it is the user who just throw away the JWT.
In your case, I suppose you check if the JWT exist in your DB before allowing the user, and as such, you just need to search and delete the others JWT associated to that account.
But if you want to make things clean, embrace JWT logic: just set short life time, and wait for them to expire.

Just use an array of tokens in database. Each device will have it's own token in the array of tokens (each token was added to db when user first logged in from a new device) and when the user logs out from that device, only the associated token from the tokens array gets deleted. Here is an example of User schema:
var userSchema = new mongoose.Schema(
{
name: {
type: String,
require: true,
trim: true
},
.
.
.
tokens: [
{
token: {
type: String,
required: true
}
}
],
{
timestamps: true
}
);

Related

Why store email-verification-token/secret-code in database and not array? NodeJs, Express, Typescript

I'm building an application with registration and login, so I need to verify the email after registration. After creating the user I want to send an activation mail with a jwt in get request. After successfully confirming the email and token is not expired the user is logged in.
My question is why, shouldn't I store the jwt local in an array and on the verification
request check if the token is valid and afterwards delete it from the array and set user registration status true?
Do I get any benefits if i store the token in a certain table in the database and also delete it after successful verification? Or shouldn't I delete it at all?
const token = jwt.sign(
{
email: loadedUser.email,
userId: loadedUser._id.toString(),
},
"somesupersecretkey",
{ expiresIn: "1h" }
);
res.status(200).json({ token: token, userId: loadedUser._id.toString() });
No need to store the token in a certain table in the database and you can also set its timeout after successful verification...

How is this actually done?

I am doing a project with authentication using passport js with the Strategies (Google, Facebook, Local). I am working on the google part now but there is a small problem. When users register locally I save their data to the db (email,username,hashedPassword) but now when they register/signin through google I do not have access to their password (obviously). It is required in the user schema and I do not want to create a new user schema.
Could I save the password as the user id I received from google? I really want to know the correct path I should take.
You could add another field to your schema like provider that specifies the type of authentication that was used. Then you can use the mongoose required validator to make password only required if provider is 'local.' Something like this:
const userSchema= new Schema({
firstName: String,
lastName: String,
email: String,
username: String,
provider: {
type: String,
enum: ['local', 'google', 'facebook']
},
password: {
type: String,
required: function() {
return this.provider === 'local';
}
}
});
When you go use an auth provider, they usually will give you some sort of "token", maybe on your client or on a redirect on your server (depends on your implementation).
Then you have to validate that token with the provider (how to do this depends on the documentation of each provider), because you can not plain trust that a given token is valid.
Once you have validated with the provider that the token is valid, is common that with that token you have access to some info of the user, like email, name, phone number, etc (depends on how you configure your integration) and maybe store which provider they use (fb, google, apple, etc)
Then you can save this information in your schema, and then generate a session for this user (it may be a JWT or a session stored on the db)
Now, when a user sign in, you will do the same validation process of the given token with the user sent, then you can provide a new session
In summary, the key is that instead of using user/pwd to validate against your local database to authenticate a user, they will provide a token, and you have to validate that token against the provider they selected

Generate API keys with JWT and regenerate the same key when needed

I recently found my self in need of developing a public API for my application. I developed my application with node.js and mongodb. After some research, I decided to use jwt for generating API keys for users and authentication. For authenticating jwt tokens, they come with the benefit of not needing to store them in a database, as they can be decoded and verified without knowing the exact generated token.
But I see that lots of applications show the users their API keys in the application dashboard, so I need to actually store the token in order to show them to the users later on. I know that storing tokens is a bad idea and in case of a database breach, it could let the hackers impersonate others with the API keys.
So long story short I am trying to find a way to not to store the exact tokens, but store only the payload in the database and every time users request their API keys I just generate the same one with a SECRET and pass it to them. I currently find that if on the signing token step, I pass the same payload with the same iat (issued at) every time, the generated token will be the same every time. So by saving the iat with the payload data in the database, I can generate the exact token every time.
Here is my mongoose schema:
var KeySchema = new mongoose.Schema({
name: { type: String, default: 'API Key' },
active: { type: Boolean, default: true },
iat: { type: Date, required: true },
project: { type: mongoose.Schema.Types.ObjectId, ref: 'Project', required: true },
});
And this is how I generate the same token with jsonwebtoken npm package:
import jwt from 'jsonwebtoken';
jwt.sign({ project, iat: CONST_TIMESTAMP }, config.secrets.session);
Now my questions are:
Does this approach is a good approach or is there a better way to achieve this?
Is there any good practice for generating API keys without storing them?
Does this even necessary (considering if there ever be any database beach, all of the data is already stolen)?
Is there any method other than using jwt to achieve this?
JWT is a standard approach for the given objective and for you I would recommend to use asymmetric signing key.
The advantage of this would be the consumers or clients will need to trust the issuer and there are multiple ways in which the public part of the key can be distributed.
So this does away with the requirement of key regeneration , also you can use the standard techniques for securing the private part of the signing key.

Should I verify a JWT token information from a Database?

I'm currently trying to build a REST API using express, node, and MongoDB. Now for authentication, I'm using JWT.
Here is the code for checking JWT token
const token = req.headers['authorization'];
if (token){
const tokens = token.split(' ');
const key = tokens[1];
jwt.verify(key, config.jwtKey, (err, authData) => {
if (err){
res.status(403).json({
success: false,
message: "Authentication2 failed"
});
}
// User authenticated
// Do something
next();
});
} else {
res.status(403).json({
success: false,
message: "Authentication failed"
});
}
Now, this code is working perfectly.
For making the JWT, here is the code
........
........
const token = jwt.sign(
{
email: user[0]._email,
userId: user[0]._id
}, config.jwtKey,{ expiresIn: "1d" });
........
........
Now my question is should I also verify the user by checking the existence of the user's information in the database to make it more secure?
For example, searching the email and userId to my database.
I'm using node, express, MongoDB, Mongoose, and JWT for this project.
It's an old question but I want to leave an answer:
Yes! Everytime your client make an api request, backend should verify both the validity of the token and the presence somewhere in your backend (for example a db table).
Consider always the most dangerous scenario: bank account
What happens if someone steal your device?
You should be able to invalidate the token from another device and change the password.
The advantage of using a token is that the server can
verify it quickly without calling out to an external data store like MongoDB.
But if you're going to add a business login to your API authentication like a blacklist/whitelist of revoked tokens then you have to use a store to verify the token and user details, (will be slower than not doing a remote call for each token but you have to do it with low latency).
For low latency you have to use DB like Redis, Dynamodb would probably be fine and more secure without major latency between your DB and your API server.
Is not required to verify the signature with DB, and you can settle for JWT algorithm
Verifying (the signature of) the token using the selected algorithm is enough to ensure that this user exists (or existed) on the system because it was the system who generated the token in the first place.
But there are cases when that's not enough, for example, blacklists as mentioned in Roy G's answer, or if the users' claims have been changed or completely deleted from the system but they are still using an old token (not expired yet), they could still have access to the system, so checking against DB would prevent that access.
Setting a small expiry date in combination with refresh tokens is generally a good practice to prevent those kinds of leaks.

Passport Authentication - Any?

I am writing a nodejs application with passport.js-based authentication. It lets users message other users, where only authenticated users are allowed to retriever messages either sent by them or with them as the receiver. I am planning to implement multiple identity providers, such as facebook, google, and maybe local authentication as well.
The user schema i set up using mongoose looks like sort of like this:
var userSchema = new mongoose.Schema({
googleId: String,
facebookId: String,
email: { type: String, required: true },
}, {
strict: false
})
module.exports = mongoose.Model('User', userSchema)
Now the approach I had in mind was this:
A user is presented a sign in page
On this page they are presented a choice of identity providers
They get redirected to authorization page, granting access to the requested scopes, getting redirected to my specified callback URL
There is either already a user with the according ID or a new one is created.
Now when they try to receive the message, I want to authenticate them again in order to grant authorization to obtain the message. How they authenticate really does not matter, as long as it is any of the strategies I configured; there is however no such thing as app.get('/messages', passport.authenticate('any'), done), so how would I approach this?
One option is to pass the strategies you want as an array into passport.authenticate([Strategies]). The link below shows that nicely.
passport.js with multiple authentication providers?
Another example from the author of passport:
https://github.com/jaredhanson/passport-http/blob/master/examples/multi/app.js

Resources