want to allow only the post owner to delete - node.js

If a hacker knows the rest api url and another person's userId, I want to prevent it from being deleted by sending it as a parameter.
I want to store the jwt token in the database and check it before writing. However, after searching, it is said that it is inefficient because it has to be searched every time. Is there any other way?? Short live jwt also exists, but I can't use it either because I have to apply it to the app, not the web. (You can use it, but it's not UX-wise)
If there is another better way, please let me know

A JWT encodes all the information which the server needs (in so-called claims, for example, the user id in the sub claim and the expiry time in the exp claim). And it contains a signature which the server can validate, and which cannot be forged unless someone has the private key.
The server validates and decodes the token with code like the following:
const jwt = require("jsonwebtoken");
const signingCertificate = `-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----`;
var claims = jwt.verify(token, signingCertificate);
if (claims.exp && claims.exp <= new Date().getTime() / 1000)
throw "expired";
var user = claims.sub;
All this happens on the server, without any token being looked up in a database. And you can then check whether the post to be deleted really belongs to user. (This check involves a database access, because you must look up the user to whom the post belongs. But deletion of a post anyway is a database access.)
You don't need a user parameter, because the user is always taken from the token. So even if A knows B's userId and postId, it can only send its own token:
POST delete?postId=475
Authorization: Bearer (A's token)
and with that it cannot delete B's post 475.

Related

Is a good idea pass decoded jwt data via express req parameters?

In my NodeJs application im using jwt to manage the user session, inside a jwt token i store user_role and user_id. This is my route:
routes.post('/manga/post', Authorize("Scan"), MangaMiddleware.valid_manga_store, MangaController.store);
In the middleware Authorize("Scan") I verify the jwt token with "jwt.verify", if its valid i going to check if there is a active user with the token id and if his permission allow him to access this route, if so i use next()
In MangaController.store i going to save a new manga, and i need to save in the document the user_id who made the request.
That's my point, i already decoded the token in Authorize middleware but the decoded data do not persist out of the middleware. To access the user_id from MangaController i have to verify the token again.
I think i should avoid verify the same token twice, so in the middleware Authorize after verifying i was saving the user_id (encrypted) inside req.auth and after use it in the controller, i was setting req.auth = null. This way the user_id is stored in req.auth for a short period of time.
req.auth = user_id //after encrypting
My friend told me this is a bad idea storing decoded data inside req parameters, but i don't think it is this bad.
In a nutshell. Do i need to verify the token twice? Is there another way to retrieve this data? It is that bad storing decoded data in req parameters? I do appreciate your time and help.
Verifying and decoding JWT are two different things. When you verify, it's checking for its integrity, ie making sure it has not been tampered with, while decoding JWT means converting from base64 to readable format (UTF-8?). So it doesn't have to be verified twice.
Assuming you sent your token in headers as "token":"base64encodedJwt", then after successful verification, whenever you need user_id, you can then simply decode the JWT. Use some JWT decode library.
let token = req.get('token') || req.headers['token'];
let payload = decodeJWT(token);
let userId = payload.user_id;
If you are not storing it in req object, then you will have to decode it everytime you need it. So req.auth = userId should be fine.

Logout JWT with nestJS

I'm using JWT passaport to login module:
async validateUser(userEmail: string, userPassword: string) {
const user = await this.userService.findByEmail(userEmail);
if (user && user.password === userPassword) {
const { id, name, email } = user;
return { id: id, name, email };
}else {
throw new UnauthorizedException({
error: 'Incorrect username or password'
});
}
}
async login(user: any) {
const payload = { email: user.email, sub: user.id };
return {
access_token: this.jwtService.sign(payload),
};
}
This part is running.
My question is: how do the logout? I read about creating a blacklist and adding the token to it, but how do I get the user's access token?
Something you should know about token-based authentication is that it is stateless. This means that even the server does not keep track of which users are authenticated, like with session-based authentication. As such, you do not need to do anything on the server side to "log out" a user. You simply need to delete the t\JWT token on the client. If you make a request to the server app, without a valid JWT token, it will be treated as the user is not logged in.
Generally when a logout request would be sent the Authorization header should be present, so you can grab the token from there. Then you can save the token to the database's restrict list table.
When user click to "Log out" btn, you should sent a request which is attached Authorization header with bearer token. In the backend side, you need to extract header and push the token to the blacklist token (as your solution). Basically, you only need remove token in client side, it's so easy to do but in the worst case when the token was stolen by hacker, your token still valid. Using blacklist token is more secure but it can be lead to performance issue and scalable. What is the best solution? it's depend on you.
Read the Nestjs Execution context get the token from the request header and verify this token from JWT.
everything defines in the NESTJS link
//here we check the token from the header is valid or not expired
const tokenVarify = await this.jwtService.verify(token);
my idea is make whitelist every generate new token and remove it when user logout. so u need to check on guard also every user with token access its exist on whitelist or note
You must be refresh expire token to 1ms with:
https://webera.blog/how-to-implement-refresh-tokens-jwt-in-nestjs-b8093c5642a9
Actually there is a workaround for this, but not so straightforward!
The idea is to keep track of the tokens for logged out users (use some sort of black-list) and query provided token against that black-list.
To sum it up, you can follow these 4 steps:
Set a reasonable expiration time on tokens
Delete the stored token from client side upon log out
Have DB of no longer active tokens that still have some time to live
Query provided token against The Blacklist on every authorized request
For detailed explanations, you can check this post: medium
A guide in how to do the implementation is in this youtube video
Code with Vlad
, and there's as well the github source nestjs-jwts. I followed this video and implemented myself as well..

How to destroy JWT Tokens on logout?

I am using jwt plugin and strategy in hapijs.
I am able to create jwt token while login user and authenticate other API using the same token through 'jwt' strategy.
I am setting the token in request.state.USER_SESSION as a cookie where USER_SESSION is a token name. Also, I am not saving these token in the database.
But how can I destroy jwt token at the time of logout?
Please suggest a way.
The JWT is stored on browser, so remove the token deleting the cookie at client side
If you need also to invalidate the token from server side before its expiration time, for example account deleted/blocked/suspended, password changed, permissions changed, user logged out by admin, take a look at Invalidating JSON Web Tokens for some commons techniques like creating a blacklist or rotating tokens
You cannot manually expire a token after it has been created. Thus, you cannot log out with JWT on the server-side as you do with sessions.
JWT is stateless, meaning that you should store everything you need in the payload and skip performing a DB query on every request. But if you plan to have a strict log out functionality, that cannot wait for the token auto-expiration, even though you have cleaned the token from the client-side, then you might need to neglect the stateless logic and do some queries. so what's a solution?
Set a reasonable expiration time on tokens
Delete the stored token from client-side upon log out
Query provided token against The Blacklist on every authorized request
Blacklist
“Blacklist” of all the tokens that are valid no more and have not expired yet. You can use a DB that has a TTL option on documents which would be set to the amount of time left until the token is expired.
Redis
Redis is a good option for blacklist, which will allow fast in-memory access to the list. Then, in the middleware of some kind that runs on every authorized request, you should check if the provided token is in The Blacklist. If it is you should throw an unauthorized error. And if it is not, let it go and the JWT verification will handle it and identify if it is expired or still active.
For more information, see How to log out when using JWT. by Arpy Vanyan(credit and reference)
On Logout from the Client Side, the easiest way is to remove the token from the storage of browser.
But, What if you want to destroy the token on the Node server -
The problem with JWT package is that it doesn't provide any method or way to destroy the token.
So in order to destroy the token on the serverside you may use jwt-redis package instead of JWT
This library (jwt-redis) completely repeats the entire functionality of the library jsonwebtoken, with one important addition. Jwt-redis allows you to store the token label in redis to verify validity. The absence of a token label in redis makes the token not valid. To destroy the token in jwt-redis, there is a destroy method
it works in this way :
1) Install jwt-redis from npm
2) To Create -
var redis = require('redis');
var JWTR = require('jwt-redis').default;
var redisClient = redis.createClient();
var jwtr = new JWTR(redisClient);
jwtr.sign(payload, secret)
.then((token)=>{
// your code
})
.catch((error)=>{
// error handling
});
3) To verify -
jwtr.verify(token, secret);
4) To Destroy -
jwtr.destroy(token)
Note : you can provide expiresIn during signin of token in the same as it is provided in JWT.
If you just want to remove the token, it will be simple as removing it from the front end application, In you case clear the cookies that stores the token
On the other hand if you mean to invalidate the token, there is couple of ways to do it, below are some ways
(1) If all the token ever generated is stored in backend, It will be just simple as clearing that storage, if tokens have been mapped to users you can just clear tokens for a particular user.
(2) You can add a date field like "invalidate_before" along with user which should be updated at a event of changing password, logout from all devices etc.
Simply update the invalidate_before to currentTime() on such events.
Every time a new token is created, add the created time in token payload,
to validate the token on incoming request just check if the created time in payload is greater than invalidate_before time for that user in db
(3) When you create a new user, create a secret for just that user, then you can sign every user token with that specific secret, and just like in (2) events like changing password, logout from all devices etc, Should create a new secret.
This way also you can invalidate by checking the token signature.
overhead with (2) and (3) is that, validation will be a 2 step process and it involves db reading
EDIT: For (3) you may use a salt instead (final secret will be common secret + salt for particular user), So that you hava a way to invalidate either a single user's token by changing salt or the all user's token by changing common secret
You can add "issue time" to token and maintain "last logout time" for each user on the server. When you check token validity, also check "issue time" be after "last logout time".
While other answers provide detailed solutions for various setups, this might help someone who is just looking for a general answer.
There are three general options, pick one or more:
On the client side, delete the cookie from the browser using javascript.
On the server side, set the cookie value to an empty string or something useless (for example "deleted"), and set the cookie expiration time to a time in the past.
On the server side, update the refreshtoken stored in your database. Use this option to log out the user from all devices where they are logged in (their refreshtokens will become invalid and they have to log in again).
OK so I tried something that I wanna share I think it's a really easy and effective method so basically instead of destroying your token or blacklist it we can simply append a random value to it in the middle in a random index or even in the end of it like a random number (or a random hashed number) to make it harder for anyone to reverse it and obtain the previously valid token, Doing so makes this token invalid so the user won't go anywhere and from the front-end you can redirect the user to login again (or even from the back-end however I prefer if the front-end did it) so the user logs out they get redirected to the login page and it's all good, Here's my code. first of all I have an auth middleware that if the token(password & username) is OK it appends the token to req.token so whenever I call this middleware the user's token will be save to req.token
router.post('/logout', auth, async(req, res) => {
try{
let randomNumberToAppend = toString(Math.floor((Math.random() * 1000) + 1));
let randomIndex = Math.floor((Math.random() * 10) + 1);
let hashedRandomNumberToAppend = await bcrypt.hash(randomNumberToAppend, 10);
// now just concat the hashed random number to the end of the token
req.token = req.token + hashedRandomNumberToAppend;
return res.status(200).json('logout');
}catch(err){
return res.status(500).json(err.message);
}
});
right now it will concat the the hashed random number to the end of the token which means it's no longer valid so the user will have to login again as they will be redirected to the login page

REST | JWT | Auth: How To Verify Authorities For Secure Resources

Token-Based Authentication: JWT? Check. GET /items/:id... How?
This is a small question, but probably a big answer, as I'm a bit new to this...
Once I have provided a client a JWT -- and they wish to obtain a resource -- what does the logic-flow of verifying Client look like?
In other words, I have a JWT payload such as...
{
...
"sub": user.id
...
}
... and Client needs to access item 998 at /api/items/:id...
My current approach looks something like the following.
// ItemsController.lang | 'GET /api/items/:id'
var userId = jwt.decode(token).sub;
var isValid = checkUserIdInDatabase(userId);
var secureResource = ORM.findOne({ user: userId, id: request.itemId });
response.send(secureResource);
Along with this, when Client signs up/in, I provide them a response which looks like this...
{ user: { id: 998, email: 'no#username.com', preferences: [...] }, jwt: token }
Should I ever be sending id & email if I'm issuing a JWT?
Given that I should have middleware to check if my jwt.sub's [userId] value exists in the database, should I use this userId as part of my query, or should Client be sending Server the userId as request.body.userId since it obtains it upon sign in/up?
Is it a no-no to assign a userId to payload.sub?
Do I need to generate a new JWT with a new lifespan upon every request and send it to the client?
What are all my security blunders, what is best practice, and what would you do?
Every tutorial I look at shows a nice & clean high-level flow stating [simply] that 'if the JWT is verified, the resource is sent to the client'.
Can you please provide me with some direction on how all this token-based authentication stuff is supposed to work at the low-level -- namely, in order to request secure resources?
PreThanks,
Cody
This is solely my point of view and i am no expert, here it goes:
1) I think you should not send user ID in your response as there is not a lot of scenarios where i see that would be necessary for client to know its user id, you easily assign this to your token payload and use a middle ware like express-jwt
to do the decoding and giving the user id for you.
2) Do not rely on sensitive data sent by client, if you use express-jwt then it would assign user id with every request object eg: req.user.userId
3) you can assign user id and other small user session data in your payload, your client would require your secret key and to be able to decode that information , always have a strong secret key.
4) I think you should have token life span expire after a day or even less, there are scenarios where you may need to go longer i think 1 week should be maximum amount of time you should have your token life span. now you can always refresh your token and have your application check after some interval of time if its token is valid and request for refresh or new one after expiration .
5) may be these:
Have a strong secret key.
Use cookies to store secret key for web applications
Keep your token life span short as possible.
Use HTTPS protocol for APIs.
Write a middle-ware or use already existing middle-wares for user authentication. eg: express-jwt
jwt tutorial tutorial2 ,using storm-path and using passport
Hopefully this helps.

Is it possible to revoke a token with Nancy.Authentication.Token?

I've looked through the source code and tests, but don't see a way to revoke a token. I need to cover scenarios where I disable access for a user and need that occur immediately.
Given that the token is stored in keyChain.bin, it might be possible to deserialize the collections, detokenize all tokens, remove the desired on, the serialize the collection again. This sounds elaborate. Are there any other methods I could use?
Update
Potentially I can keep a separate list of user ids and the token that they have been issued, then match the token with the keyChain collection.
Update 2
After playing with the keyChain file, things are little more confusing. After creating a new user, I issue a token:
var newServiceIdentity = siteSecurityManager.CreateServiceUser(user.UserId, user.Password)
.GetServiceIdentity();
string token = _tokenizer.Tokenize(newServiceIdentity, this.Context);
newServiceIdentity.Token = token;
siteSecurityManager.RegisterToken(newServiceIdentity);
var fileStore = new FileSystemTokenKeyStore(_rootPathProvider);
var allKeys = fileStore.Retrieve() as Dictionary<DateTime, byte[]>;
return new { Token = token };
By default, Nancy will store each token in a binary file so that should your server need to be bounced, the user sessions will survive. With a different browser session, I connect with the new users credentials and gain access to the site. When I add another user, I would expect that the allKeys count would incremented to reflect my admin session as well as the new user that is connected with a different browser. I see a count of one, and the key matches the admin token.
My login method does indeed issue a token for each user that connects with correct credentials. That logic is:
var userServiceIdentity = ServiceIdentityMapper.ValidateUser(user.UserId, user.Password);
if (userServiceIdentity == null)
{
return HttpStatusCode.Unauthorized;
}
else
{
string token = _tokenizer.Tokenize(userServiceIdentity, this.Context);
return new { Token = token };
}
I store the token and return it with each Ajax call. The implication here is that I do have the tokens recorded, otherwise authentication would fail. But if the keyChain.bin is not updated, then I can't pursue the idea of registering the token and user in a separate store, then purging that token to revoke access.
As explained to me by Jeff, the code's author, the keyChain.bin stores only the key that is used to generate the token. This is so that the relevant information is only stored on the client, and a simple comparison of the client token is used to avoid querying a back-end source. Jeff's complete explanation is here
A possible solution would be to keep a separate list of black listed tokens / users. Perhaps this is best dictated by business practices. There are indeed times where you should lock a user out of immediately. This could be accomplished by issuing a purge for all tokens, and force a login for all legitimate users. A minor inconvenience for some scenarios, and unacceptable for others.

Resources