Keeping Session data on backend only using JWT - node.js

I'm using JWT on my application and I define my claims like so:
const claims = {
iss: process.env.DOMAIN,
scope: "game",
email: user.get("email")
};
I verify the token like so:
nJwt.verify(socket.handshake.query.token, app.get("jwt.secret"), (err, decoded) => {
if (err) return;
console.log(decoded.body.email); // doge#doge.com
});
However, it forces me to add session data into claims object. Instead, I want to use the token as a session identifier and keep session values on backend only, such as:
nJwt.verify(socket.handshake.query.token, app.get("jwt.secret"), (err, decoded) => {
if (err) return;
// Since we verified token, read token's session data from backend
});
Is there any example about doing this?
Ps. I use Redis so I can SET/GET values via Redis, but I feel like it's bad practice since I'll be developing whole session thing myself.

To uniquely identify your token, you could take the whole tokenstring as identifier, though this doesn't make much sense since its too long and unhandy.
Instead, jwt has already standardized a claim for this purpose: jti.
So generate your token something like this:
var claims = {
iss: process.env.DOMAIN,
jti: yourGenerateUniqueIdFunction();
};
After JWT verification, you have the jti value.
In the Backend you could model your Session K/V Store like this:
[jti].[keyName] -> [value]
Accessing K/V something like this:
Session.get('3452345.email') //doge#doge.com
Another way how to achieve what you want is just enabling the default session mechanism in express (https://github.com/expressjs/session),
then using https://github.com/tj/connect-redis to store the session in your redis store. In this way you could just easily read and write to the req.session and it will be backed to your configured redis store.

Related

How to verify a hashed secret without knowing the salt?

I store the API-Keys as hashes in a database.
...
async function createToken(userId:number) {
...
const salt=await bcrypt.genSalt(15)
const hash=await bcrypt.hash(token, salt)
await db.store({userId,hash})
}
async function verifyToken(token:string){
const userId= //I don't know the UserID since only the token is sent as HTTP header
const hashes= db.get(userId)
for(const hash of hashes) {
if(await bcrypt.compare(token, hash)) {
return true
}
}
return false
}
How do I verify the token validity without knowing the userID?
The only option I see is to loop through all DB records, and try if the produced hash for that record matches. But this results potentially in thousands of hashes checked before I find the right one.
I may reduce this to only the tokens belonging to a specific UserID. However, this would require my users to also send there userID which I don't want them to do.
Seems like you are using bcrypt hashes as token to authenticate the users, but that's not the actual purpose of such hashes. If you need to use a token, why not use something like JWT. It can hold the user information, and can be verified without a single db call.

JWT store at server database

Hello I have a query regarding JWT and authentication. In authentication, does the server have to store the JWT as well and have it related to the client?
When the client log in, it receives the JWT token and store it in localStorae with the "auth" key:
For example, this is a client's basic code
const user{
email:user_email,
password:user_password
//other field
}
axios
.post("http://localhost:4000/api/login", user)
.then(function (response) {
if (response.data.success === false) {
// password or user does not exist!
} else {
localStorage.setItem("auth", response.data.token);
setTimeout(() => {
history.push("/");
}, 3000);
}
})
.catch(function (error) {
console.log(error);
});
};
Then in our Routes, we can check if there is a token and if so, we can access certain urls that are only accessible when logged in:
const PrivateRoute = (props) => {
const token = localStorage.getItem("auth");
return <>{token ? <Route {...props} /> : <Redirect to="/login" />}</>;
};
But my question comes from: when the client sends a new request (once logged in) doing a POST, GET, DELETE, does the HTTPS packet data need to be in JSON format with the corresponding token? But if so, the server must have the JWT stored to confirm that the user is registered and is who they say they are. So what is the difference with a session?
How do you check if the server doesn't have the JWT stored in its database that the client is who it claims to be?
For example, in django, must there be a field that is jwt?
class User(models.Model):
email = models.EmailField(max_length=254)
token = models.BigIntegerField(default=None)
#Otros campos
A jwt typically contains a set of so called claims, one if them oftentimes is the sub claim which denotes the subject for whom this token was issued. Depending on the particular issuer, this claim may also be named differently. Another typically claim is exp which denotes a timestamp, when this token expires.
Furthermore a JWT should be signed by a trusted party (the issuer). This can be your own server or any other trusted party (for instance sign-in with Google or something like that). If you are able to verify that signature, you are safe to assume the token wasn't tampered with.
So if you find a sub claim in a token whichs signature you can verify, you can easily identify the user. If you furthermore check the expiration, you can reject tokens which are not valid anymore.
But if you don't store the token at the backend, you won't be able to revoke such a token prematurely and it will be valid until it expires. That may, or may be not, a problem, depending on your usecase.

Storing an array in a cookie in nodeJS

I was wondering if it is possible to store an array in a cookie in nodeJS. For example I can set a cookie using the following code res.cookie('Name', "John", { maxAge: 900000});. My question is it possible to store and array of names instead of one name. For example res.cookie('Name', ["john","mark"], { maxAge: 900000});
No, you can't. But you can do something better: store the array in a Json Web Token (JWT).
A JWT is a token in which you can store information in JSON format. You can do it this way:
var jwt = require('jsonwebtoken');
var token = jwt.sign({ Name: ["john","mark"] }, 'YOUR-SECRET-KEY');
Now you have your array of names inside the token variable (and encrypted with your secret key). You can put this token in a cookie:
res.cookie('jwt', token, { maxAge: 900000});
And the user can't edit the cookie information without knowing the secret key you used on the server to create the token.
When you want to decode the information from the token on the user's cookies, you just have to do:
var decoded = jwt.verify(req.cookies.jwt, 'YOUR-SECRET-KEY');
console.log(decoded.Name)
//output: ["john","mark"]
Here is the npm package. Surely you can just encrypt your cookies with any other method, but the pro point of using JWT is that you can store the information like a simple JSON. And then send the token on the cookie.

Convert NodeJS asynchronous code to Spring Project Reactor

I have below NodeJS code:
// req and resp are http request, response objects
var uri = req.getURI()
var pageView = new PageView(uri)
var token = req.token
if (token) {
UserRepository.findByToken(token, function(notFound, user){
if(notFound) { // means user not found by specified token
var newUser = new User('John Doe')
user.foo = 'some value'
processUser(newUser, pageView)
} else { // user found by token
user.foo = 'some value'
processUser(user, pageView)
}
})
} else { // token does not exist
token = new Token('some value')
resp.setToken(token)
var newUser = new User('John Doe')
user.foo = 'some value'
processUser(newUser, pageView)
}
processUser(user, pageView) {
PageViewRepositiry.save(pageView, function(error, savedPageView){
if(error) {
throw 'error'
}
user.pageViews.push(savedPageView)
// save the modified savedUser
UserRepository.save(user , function(error, savedUser){
})
})
}
It uses Repository pattern as abstraction over database layer (same as the Repository pattern in Spring applications).
Basically it finds user by incoming token (from http req object). If user is found then updates user entity and adds the saved pageView entity and saves the modified user. If user is not found by token then it creates a new User, updates the user with saved pageView, saves the user.
How the same code will be written in Spring Project Reactor (Flux) ?
Is it possible to solve this problem without using block()? Ideally I would like a solution that does not use block().
First of all, you have some logic to generate a token if a token isn't present. For example:
private Mono<String> getToken(String token) {
return Mono
.just(token)
.switchIfEmpty(Mono.just("some token"));
}
In this case, it's a bit overkill to use switchIfEmpty for this, but I assume your process to generate a token is a bit more complex, otherwise you could have worked with Optional<String> in stead (eg. token.orElse("some token")).
Additionally, we also have some logic to either find the user by its token, or create a new user if there is no user by the given token:
private Mono<User> findUserByToken(String token) {
return userRepository
.findByToken(token)
.switchIfEmpty(userRepository.save(new User("John Doe", token)));
}
Now that we have these methods, we can create a PageView and use these methods along the way. The reason I start with creating a PageView is because that's the first "constant" in the entire token, regardless of whether there is a token/user found:
return Mono
.just(new PageView(uri))
.flatMap(pageViewRepository::save)
.flatMap(pageView -> getToken(token)
.flatMap(this::findUserByToken)
.doOnNext(user -> user.setFoo("foo"))
.doOnNext(user -> user.getPageView().add(pageView)))
.flatMap(userRepository::save)
.map(User::getToken);
Now, since you need the token to add to the response, and I figured out that the token is part of the User object somehow (otherwise UserRepository.findByToken() wouldn't work?), it would be easier to just use User::getToken at the end to retrieve the token to pass to the response.
Be aware though, the repository pattern does work properly with Spring, but there is only reactive support for MongoDB, Cassandra, Couchbase and Redis. Other than that there's also reactive support for PostgreSQL through rdbc, but I don't think Spring data has support for that.

How is authentication token used to access user data

I am trying to understand how token based authentication strategy uses the token (e.g.: JWT) to access the data specific to a user. All I see explained in search results is the part when the user supplies the username and password and then the token is created and verified with each subsequent call.
Le't say I have a Node.JS service with two MongoDB collections: Users and UserMessages. Each entry from UserMessages contains a UserID and a Message.
If an authenticated user wants to see all the messages pertaining to that user, how do I (programatically) know how to filter the correct messages? How do I make the correlation between the token and the user identity (e.g.: UserID or anything else that helps identify the user) that I need for querying the message collection in the next step?
I just wish to understand the concept. I couldn't find anywhere a clear example where the user identity is obtained in code. Example:
apiRoutes.use(function(req, res, next) {
var token = req.body.token || req.param('token')
|| req.headers['x-access-token'];
if (token) {
jwt.verify(token, app.get('superSecret'), function(err, decoded) {
if (err) {
return res.json({ success: false, message:
'Failed to authenticate token.' });
} else {
req.decoded = decoded;
next();
}
});
}
});
The JWT contains a payload.
Typically, you put the user's ID in this payload. You can put anything you like in there, but since the token is sent on every request you want to keep the payload small. Also, because the JWT is base-64 encoded, anyone can decode the token and see the contents of the payload.
However, because only your server knows the "secret" for the JWT, no one can alter the payload or otherwise create a phony JWT... your server will consider such tokens invalid.
The above code seems to be decoding the payload and storing it on the req object. When it decodes the JWT it is also verifying the signature, so if the decode is successful you can trust the data in the payload. Now you can use the user ID from the payload to get the data you desire.

Resources