Logout with AdonisJS using JWT token - node.js

I have a login method working well that generates a JWT token to the user for authentication on AdonisJS. But how I could block this token in the future if the user click on "Logout" button or even if I want to block it manually by myself?
I know I could just delete it from the client side, but the problem is that the token would still being active (in the case someone else steal this token somehow they would still have access to the API, for example).
Any idea how to fix that? Thanks

you should use Revoking Tokens in AdonisJs
The jwt and api schemes expose methods to revoke tokens using the auth interface.
For jwt, refresh tokens are only revoked, since actual tokens are
never saved in the database
revokeTokens(tokens, delete = false)
The following method will revoke tokens by setting a flag in the tokens table:
const refreshToken = '' // get it from user
await auth
.authenticator('jwt')
.revokeTokens([refreshToken])
If true is passed as the 2nd argument, instead of setting the is_revoked database flag, the relevant row will be deleted from the database:
const refreshToken = '' // get it from user
await auth
.authenticator('jwt')
.revokeTokens([refreshToken], true)
To revoke all tokens, call revokeTokens without any arguments:
await auth
.authenticator('jwt')
.revokeTokens()
When revoking the api token for the currently loggedin user, you can access the value from the request header:
// for currently loggedin user
const apiToken = auth.getAuthHeader()
await auth
.authenticator('api')
.revokeTokens([apiToken])
revokeTokensForUser(user, tokens, delete = false)
This method works the same as the revokeTokens method, but instead you can specify the user yourself:
const user = await User.find(1)
await auth
.authenticator('jwt')
.revokeTokensForUser(user)

Related

How to use Shopify Node API to retrieve an access token from a session with Amazon AWS API Gateway

I have a custom function that validates the Shopify session token and returns the decoded JWT
{
iss: The shop's admin domain.
dest: The shop's domain.
aud: The API key of the receiving app.
sub: The user that the session token is intended for.
exp: When the session token expires.
nbf: When the session token activates.
iat: When the session token was issued.
jti: A secure random UUID.
sid: A unique session ID per user and app.
}
Then, I initialize the context with Shopify.Context.initialize({...})
How can I get the access token using:
const session = await Shopify.Utils.loadCurrentSession(req, res)
Right now, I'm saving the access token in a database during the initial install since I don't know how to use the function above with AWS API Gateway.
What should be req and res knowing that a Lambda handler has event and context as parameters?
I feel req = event. But I have no idea what res should be in this case.

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..

JWT Revoke Refresh Token

I'm building a Node.js API service. I'm issuing a JWT access token that expires every 5 minutes, a refresh token that expires in a month, and /fetch-access route to get a new access token when called from the SPA/client.
Now I'm using a database to store a list of recently revoked refresh tokens that an administrative user can populate by some administrative action (i.e. "suspend account", "change permissions", etc.).
My assumptions were that I would produce the same JWT token string value, store that in the database and query the database for that value when the /fetch-access route is called. However, what I realize now is that; at the time that the administrative user is revoking the JWT, I have no way to replicate the same JWT string value at that point in time.
To illustrate my point;
const jwt = require('jsonwebtoken')
let refresh_token_on_login = jwt.sign({user: 'bob'}, 'secret123', {expiresIn: '7d'})
let refresh_token_on_revoke = jwt.sign({user: 'bob'}, 'secret123', {expiresIn: '7d'})
console.log(refresh_token_on_login)
//eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0Ijp0cnVlLCJpYXQiOjE2MDA4Mzg2NzUsImV4cCI6MTYwMTQ0MzQ3NX0.X2zBWVr5t3olb5GsPebHULh-j1-iiuyjJmb98jzlZ2Q
console.log(refresh_token_on_revoke)
//eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0Ijp0cnVlLCJpYXQiOjE2MDA4Mzg3MTMsImV4cCI6MTYwMTQ0MUxM30.4LDBJv4qrLLzOieDtyXu8vWqJ1EY75vShpUWiX7jepQgg
In order for the administrator to invalidate refresh token that was issued to the user at a prior time, the revoke key cannot be the actual token string, but some other value that gets saved somewhere in the database at the time of login (i.e. UserModel = { last_issued_refresh: 'xyz'}). That same value needs to be embedded in the refresh token itself to be used in checking the black-listed tokens upon /fetch-access requests.
Are my assumptions correct? Is this the only way to do this, or am I missing some key point in how the refresh token gets revoked (by a user other than the client user - such as admin)

node js JWT get current user

I work on app with an authentication using Node JS, JWT and Sequelize for the API and I'm using React JS / redux for the front part. I'm successfully implemented the login/logout/register parts for the application, but now I need to access to the current_user logged in.
I put a JWT in the localStorage, but I want to have access to the user ID, user email, user name and more informations about my user currently logged in.
Should I use Cookies ? LocalStorage ? Or should I create a currentUser method in my API ?
I'm a bit lost with this, if someone could help me find some usefull resources or advices !
Thank !
If you put that information in the payload of the JWT, then you can get it without decoding on the server or needing the secret, and can therefore put the token in LocalStorage for use whenever. By the spec, a JWT is <headerINfo>.<payloadInfo>.<signature>. So on the client, you can just do:
// given a payload object of { username: 'bob', userid: 1, email: 'bob#example.com' }
const tokenParts = token.split('.');
const encodedPayload = tokenParts[1];
const rawPayload = atob(encodedPayload);
const user = JSON.parse(rawPayload);
console.log(user.username); // outputs 'bob'
Obviously, this info is available to any client that has access to the Token, so you only want to put stuff in the payload that that's OK for.
Storing the token in LocalStorage is fine. If you need to fetch the user details, create an endpoint in your API such as getUser. You can then use jwt.decode(accessToken, JWT SECRET HERE) and return the decoded value (which will be your user) assuming the accessToken is valid.
You can make a middleware if you haven't already that will ensure that user info is always available to those routes that require it:
const auth = jwt({
secret: JWT_SECRET,
userProperty: 'payload',
algorithms: ['HS256']
});
module.exports = auth;
Then you should have req.payload with user details. Alternatively you can check the Authorization property in your headers depending on how you set up your app.
When logging in, server should send token and user data, you can store that data in Redux store. Then simply request data from there. When user reloads page, send API request with JWT token and server should return user data, that you will again put in Redux store.

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

Resources