Related
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.
I am implementing JWT in one of my node apps. I am wondering, if there is any definite format/ structure in which a refresh token should be generated?
By definite format I mean whether a refresh token contain any claims like a JWT?
UPDATE
Let's assume a refresh token to be: fdb8fdbecf1d03ce5e6125c067733c0d51de209c (taken from Auth0). Now, what am I supposed to understand from this?
Is this a random unique string?
Is this an encrypted string which has claims inside?
Short answer
A refresh-token is just a random string.
All the user-related information including claims go in access-tokens
Explanation
You should keep something like this:
{
_id: [refreshTokenId],
value: 'fdb8fdbecf1d03ce5e6125c067733c0d51de209c',
userId: [userId],
expires: [some date],
createdByIp: [some ip],
createdAt: [some date],
replacedBy: [anotherRefreshTokenId],
revokedByIp: [some other ip],
revokedBy: [some other date],
}
Refresh tokens are random strings generated by the authentication server. They are generated after successful authentication (for example, if the username and password of the user are valid).
Their sole purpose is to remove the need to exchange user credentials repeatedly. They are different from access-tokens.
An access-token usually has information about the user (like name, claims). These are usually short-lived. JWT is one example.
To get a JWT the app has to verify the credentials.
To add additional security, and to stop bothering the user for username and password every 15 mins, we just create a signature on the server-side and forward it to the app.
Next time, whenever the app needs to create a JWT, it can just send the signature back to the server. This signature is your refresh token.
Refresh tokens are also supposed to be saved somewhere.
So you would probably create a table/collection in your database, linking the refresh-token values with userIds and ip_address.
This is how you could create a session management panel for the user. The user can then view all the devices (ip_addresses) for which we have registered a refreshtoken.
I have a MEAN stack app with a REST like api. I have two user types: user and admin. To sign the user in and keep the session i use jsonwebtoken jwt like this (simplified):
const jwt = require("jsonwebtoken");
//example user, normally compare pass, find user in db and return user
let user = { username: user.username, userType: user.userType };
const token = jwt.sign({ data: user }, secret, {
expiresIn: 604800 // 1 week
});
To protect my express routes from I do this:
in this example it is a "get user" route, the admin is ofc allowed to get information about any given user. The "normal" user is only allowed to get information about him/her -self, why i compare the requested username to the username decoded form the token.
let decodeToken = function (token) {
let decoded;
try {
decoded = jwt.verify(token, secret);
} catch (e) {
console.log(e);
}
return decoded;
}
// Get one user - admin full access, user self-access
router.get('/getUser/:username', (req, res, next) => {
let username = req.params.username;
if (req.headers.authorization) {
let token = req.headers.authorization.replace(/^Bearer\s/, '');
decoded = decodeToken(token);
if (decoded.data.userType == 'admin') {
//do something admin only
} else if (decoded.data.username == username) {
//do something user (self) only
} else{
res.json({ success: false, msg: 'not authorized' });
}
} else {
res.json({ success: false, msg: 'You are not logged in.' });
}
})
So my question is, how secure is this? Could someone manipulate the session token to swap the username to someone else's username? or even change the userType from user to admin?
My guess is. Only if they know the "secret" but is that enough security? the secret is after all just like a plain text password stored in the code. What is best practice?
the secret is after all just like a plain text password stored in the code.
That's right. If secret is not kept secret, then an attacker can forge user objects and sign them using that secret and verify would not be able to tell the difference.
Putting secrets in code risks the secret leaking. It could leak via your code repository, because a misconfigured server serves JS source files as static files, or because an attacker finds a way to exploit a child_process call to echo source files on the server. Your users' security shouldn't depend on noone making these kinds of common mistakes.
What is best practice?
Use a key management system (KMS) instead of rolling your own or embedding secrets in code. Wikipedia says
A key management system (KMS), also known as a cryptographic key management system (CKMS), is an integrated approach for generating, distributing and managing cryptographic keys for devices and applications.
Often, how you do this depends on your hosting. For example, both Google Cloud and AWS provide key management services.
https://www.npmjs.com/browse/keyword/kms might help you find something suitable to your stack.
This is extremely secure, especially if sent over HTTPS - then an attacker has no idea what your request payload looks like.
The only real danger is making sure you keep the SECRET safe, don't save on public git repos, lockdown access to any box on which the secret is stored. Use an encoded secret.
There are also other ways to harden your server. Consider using npm's popular helmet module.
This is secure if your data packets are sent over HTTPS. If you want to add another layer of security what you can do is you can first encrypt the user details with the help of iron which uses 'aes-256-cbc' for encryption and then use that encrypted text and generate a token through JWT. In this way if a user somehow gain access to your token and go the website of JWT. He won't be able to recognize what's inside this token.
Again this is just to add an extra layer of security and it makes technically infeasible for a person to extract information because it's very time consuming yet add some extra security to our application.
Also make sure that all of your secrets(secret keys) are private.
To answer your initial question "Could someone manipulate the session token to swap the username to someone else's username? or even change the userType from user to admin?"
JWT tokens are encrypted from the server-side and sent to the client in the form of a response. With that token, there are two things to keep in mind:
Token is encrypted with a private key that only the server knows about
The token contains, what is known as, a signature which validates the contents of the JWT payload (e.g. userType etc.)
For more information regarding the first point, please refer to the following answer.
For a better visual representation of JWT tokens and signatures, take a look at the following URL - https://jwt.io/
With this in mind, you must make sure that:
Your secret key is never exposed at any point in time to external services/clients. Ideally this key is not even hard-coded in your codebase (ask, should you want me to elaborate on this).
You don't have any logical flaws in your endpoints.
From a design perspective, I would much rather segregate any endpoints related to admin-space into their own endpoints however at a quick glance, your code seems to be fine. :-)
On a side-note that may assist you, if you don't have much experience in Web Application security, I would recommend checking out some automated scanners such as Acunetix, Burp (hybrid) and so on. Whilst not perfect by any means, they are quite capable of detecting a good amount of behavioral vulnerabilities (as in, ones a malicious actor would normally exploit).
Some potential vulnerabilities in the code above:
Variable username comes from the url, but usertype is asserted from the token in the router action. An audit log for example could be corrupted if it took the userid from the username variable, because that is not authentic and can be anything sent by the user.
Replay is an issue. As the token is valid for one week, it is impossible to revoke admin rights for example until the token expires. (A malicious user could replay the previous admin token.)
Similarly, you cannot terminate (force logout) any session until tokens expire. This is a common issue in such stateless designs.
Null reference after token decode as pointed out by others. In node.js that is more like a weakness than a vulnerability I'd say, I think it's not exploitable.
The secret on the server is extremely valuable and can be used to impersonate any user. It is very hard to protect such a secret in systems with high security requirements.
So contrary to another answer, this is far from being 'extremely secure', but can be reasonably secure for many purposes.
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
I would like to implement JWT-based authentication to our new REST API. But since the expiration is set in the token, is it possible to automatically prolong it? I don't want users to need to sign in after every X minutes if they were actively using the application in that period. That would be a huge UX fail.
But prolonging the expiration creates a new token (and the old one is still valid until it expires). And generating a new token after each request sounds silly to me. Sounds like a security issue when more than one token is valid at the same time. Of course I could invalidate the old used one using a blacklist but I would need to store the tokens. And one of the benefits of JWT is no storage.
I found how Auth0 solved it. They use not only JWT token but also a refresh token:
https://auth0.com/docs/tokens/refresh-tokens
But again, to implement this (without Auth0) I'd need to store refresh tokens and maintain their expiration. What is the real benefit then? Why not have only one token (not JWT) and keep the expiration on the server?
Are there other options? Is using JWT not suited for this scenario?
I work at Auth0 and I was involved in the design of the refresh token feature.
It all depends on the type of application and here is our recommended approach.
Web applications
A good pattern is to refresh the token before it expires.
Set the token expiration to one week and refresh the token every time the user opens the web application and every one hour. If a user doesn't open the application for more than a week, they will have to login again and this is acceptable web application UX.
To refresh the token, your API needs a new endpoint that receives a valid, not expired JWT and returns the same signed JWT with the new expiration field. Then the web application will store the token somewhere.
Mobile/Native applications
Most native applications do login once and only once.
The idea is that the refresh token never expires and it can be exchanged always for a valid JWT.
The problem with a token that never expires is that never means never. What do you do if you lose your phone? So, it needs to be identifiable by the user somehow and the application needs to provide a way to revoke access. We decided to use the device's name, e.g. "maryo's iPad". Then the user can go to the application and revoke access to "maryo's iPad".
Another approach is to revoke the refresh token on specific events. An interesting event is changing the password.
We believe that JWT is not useful for these use cases, so we use a random generated string and we store it on our side.
In the case where you handle the auth yourself (i.e don't use a provider like Auth0), the following may work:
Issue JWT token with relatively short expiry, say 15min.
Application checks token expiry date before any transaction requiring a token (token contains expiry date). If token has expired, then it first asks API to 'refresh' the token (this is done transparently to the UX).
API gets token refresh request, but first checks user database to see if a 'reauth' flag has been set against that user profile (token can contain user id). If the flag is present, then the token refresh is denied, otherwise a new token is issued.
Repeat.
The 'reauth' flag in the database backend would be set when, for example, the user has reset their password. The flag gets removed when the user logs in next time.
In addition, let's say you have a policy whereby a user must login at least once every 72hrs. In that case, your API token refresh logic would also check the user's last login date from the user database and deny/allow the token refresh on that basis.
Below are the steps to do revoke your JWT access token:
1) When you do login, send 2 tokens (Access token, Refresh token) in response to client .
2) Access token will have less expiry time and Refresh will have long expiry time .
3) Client (Front end) will store refresh token in his local storage and access token in cookies.
4) Client will use access token for calling apis. But when it expires, pick the refresh token from local storage and call auth server api to get the new token.
5) Your auth server will have an api exposed which will accept refresh token and checks for its validity and return a new access token.
6) Once refresh token is expired, User will be logged out.
Please let me know if you need more details , I can share the code (Java + Spring boot) as well.
I was tinkering around when moving our applications to HTML5 with RESTful apis in the backend. The solution that I came up with was:
Client is issued with a token with a session time of 30 mins (or whatever the usual server side session time) upon successful login.
A client-side timer is created to call a service to renew the token before its expiring time. The new token will replace the existing in future calls.
As you can see, this reduces the frequent refresh token requests. If user closes the browser/app before the renew token call is triggered, the previous token will expire in time and user will have to re-login.
A more complicated strategy can be implemented to cater for user inactivity (e.g. neglected an opened browser tab). In that case, the renew token call should include the expected expiring time which should not exceed the defined session time. The application will have to keep track of the last user interaction accordingly.
I don't like the idea of setting long expiration hence this approach may not work well with native applications requiring less frequent authentication.
An alternative solution for invalidating JWTs, without any additional secure storage on the backend, is to implement a new jwt_version integer column on the users table. If the user wishes to log out or expire existing tokens, they simply increment the jwt_version field.
When generating a new JWT, encode the jwt_version into the JWT payload, optionally incrementing the value beforehand if the new JWT should replace all others.
When validating the JWT, the jwt_version field is compared alongside the user_id and authorisation is granted only if it matches.
jwt-autorefresh
If you are using node (React / Redux / Universal JS) you can install npm i -S jwt-autorefresh.
This library schedules refresh of JWT tokens at a user calculated number of seconds prior to the access token expiring (based on the exp claim encoded in the token). It has an extensive test suite and checks for quite a few conditions to ensure any strange activity is accompanied by a descriptive message regarding misconfigurations from your environment.
Full example implementation
import autorefresh from 'jwt-autorefresh'
/** Events in your app that are triggered when your user becomes authorized or deauthorized. */
import { onAuthorize, onDeauthorize } from './events'
/** Your refresh token mechanism, returning a promise that resolves to the new access tokenFunction (library does not care about your method of persisting tokens) */
const refresh = () => {
const init = { method: 'POST'
, headers: { 'Content-Type': `application/x-www-form-urlencoded` }
, body: `refresh_token=${localStorage.refresh_token}&grant_type=refresh_token`
}
return fetch('/oauth/token', init)
.then(res => res.json())
.then(({ token_type, access_token, expires_in, refresh_token }) => {
localStorage.access_token = access_token
localStorage.refresh_token = refresh_token
return access_token
})
}
/** You supply a leadSeconds number or function that generates a number of seconds that the refresh should occur prior to the access token expiring */
const leadSeconds = () => {
/** Generate random additional seconds (up to 30 in this case) to append to the lead time to ensure multiple clients dont schedule simultaneous refresh */
const jitter = Math.floor(Math.random() * 30)
/** Schedule autorefresh to occur 60 to 90 seconds prior to token expiration */
return 60 + jitter
}
let start = autorefresh({ refresh, leadSeconds })
let cancel = () => {}
onAuthorize(access_token => {
cancel()
cancel = start(access_token)
})
onDeauthorize(() => cancel())
disclaimer: I am the maintainer
Today, lots of people opt for doing session management with JWTs without being aware of what they are giving up for the sake of perceived simplicity. My answer elaborates on the 2nd part of the questions:
What is the real benefit then? Why not have only one token (not JWT) and keep the expiration on the server?
Are there other options? Is using JWT not suited for this scenario?
JWTs are capable of supporting basic session management with some limitations. Being self-describing tokens, they don't require any state on the server-side. This makes them appealing. For instance, if the service doesn't have a persistence layer, it doesn't need to bring one in just for session management.
However, statelessness is also the leading cause of their shortcomings. Since they are only issued once with fixed content and expiration, you can't do things you would like to with a typical session management setup.
Namely, you can't invalidate them on-demand. This means you can't implement a secure logout as there is no way to expire already issued tokens. You also can't implement idle timeout for the same reason. One solution is to keep a blacklist, but that introduces state.
I wrote a post explaining these drawbacks in more detail. To be clear, you can work around these by adding more complexity (sliding sessions, refresh tokens, etc.)
As for other options, if your clients only interact with your service via a browser, I strongly recommend using a cookie-based session management solution. I also compiled a list authentication methods currently widely used on the web.
Good question- and there is wealth of information in the question itself.
The article Refresh Tokens: When to Use Them and How They Interact with JWTs gives a good idea for this scenario. Some points are:-
Refresh tokens carry the information necessary to get a new access
token.
Refresh tokens can also expire but are rather long-lived.
Refresh tokens are usually subject to strict storage requirements to
ensure they are not leaked.
They can also be blacklisted by the authorization server.
Also take a look at auth0/angular-jwt angularjs
For Web API. read Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin
I actually implemented this in PHP using the Guzzle client to make a client library for the api, but the concept should work for other platforms.
Basically, I issue two tokens, a short (5 minute) one and a long one that expires after a week. The client library uses middleware to attempt one refresh of the short token if it receives a 401 response to some request. It will then try the original request again and if it was able to refresh gets the correct response, transparently to the user. If it failed, it will just send the 401 up to the user.
If the short token is expired, but still authentic and the long token is valid and authentic, it will refresh the short token using a special endpoint on the service that the long token authenticates (this is the only thing it can be used for). It will then use the short token to get a new long token, thereby extending it another week every time it refreshes the short token.
This approach also allows us to revoke access within at most 5 minutes, which is acceptable for our use without having to store a blacklist of tokens.
Late edit: Re-reading this months after it was fresh in my head, I should point out that you can revoke access when refreshing the short token because it gives an opportunity for more expensive calls (e.g. call to the database to see if the user has been banned) without paying for it on every single call to your service.
I solved this problem by adding a variable in the token data:
softexp - I set this to 5 mins (300 seconds)
I set expiresIn option to my desired time before the user will be forced to login again. Mine is set to 30 minutes. This must be greater than the value of softexp.
When my client side app sends request to the server API (where token is required, eg. customer list page), the server checks whether the token submitted is still valid or not based on its original expiration (expiresIn) value. If it's not valid, server will respond with a status particular for this error, eg. INVALID_TOKEN.
If the token is still valid based on expiredIn value, but it already exceeded the softexp value, the server will respond with a separate status for this error, eg. EXPIRED_TOKEN:
(Math.floor(Date.now() / 1000) > decoded.softexp)
On the client side, if it received EXPIRED_TOKEN response, it should renew the token automatically by sending a renewal request to the server. This is transparent to the user and automatically being taken care of the client app.
The renewal method in the server must check if the token is still valid:
jwt.verify(token, secret, (err, decoded) => {})
The server will refuse to renew tokens if it failed the above method.
How about this approach:
For every client request, the server compares the expirationTime of the token with (currentTime - lastAccessTime)
If expirationTime < (currentTime - lastAccessedTime), it changes the last lastAccessedTime to currentTime.
In case of inactivity on the browser for a time duration exceeding expirationTime or in case the browser window was closed and the expirationTime > (currentTime - lastAccessedTime), and then the server can expire the token and ask the user to login again.
We don't require additional end point for refreshing the token in this case.
Would appreciate any feedack.
Ref - Refresh Expired JWT Example
Another alternative is that once the JWT has expired, the user/system will make a call to
another url suppose /refreshtoken. Also along with this request the expired JWT should be passed. The Server will then return a new JWT which can be used by the user/system.
The idea of JWT is good, you put what you need in JWT and go stateless.
Two problems:
Lousy JWT standardization.
JWT is impossible to invalidate or if created fast-expiring it forces the user to log in frequently.
The solution to 1. Use custom JSON:
{"userId": "12345", "role": "regular_user"}
Encrypt it with a symmetric (AES) algorithm (it is faster than signing with an asymmetric one) and put it in a fast-expiring cookie. I would still call it JWT since it is JSON and used as a token in a Web application. Now the server checks if the cookie is present and its value can be decrypted.
The solution to 2. Use refresh token:
Take userId as 12345, encrypt it, and put it in the long-expiring cookie. No need to create a special field for the refresh token in DB.
Now every time an access token (JWT) cookie is expired server checks the refresh token cookie, decrypts, takes the value, and looks for the user in DB. In case the user is found, generate a new access token, otherwise (or if the refresh token is also expired) force the user to log in.
The simplest alternative is to use a refresh token as an access token, i.e. do not use JWT at all.
The advantage of using JWT is that during its expiration time server does not hit DB. Even if we put an access token in the cookie with an expiration time of only 2 min, for a busy application like eBay it will results in thousands of DB hits per second avoided.
I know this is an old question, but I use a hybrid of both session and token authentication. My app is a combination of micro-services so I need to use token-based authentication so that every micro-service doesn't need access to a centralized database for authentication. I issue 2 JWTs to my user (signed by different secrets):
A standard JWT, used to authenticate requests. This token expires after 15 minutes.
A JWT that acts as a refresh token that is placed in a secure cookie. Only one endpoint (actually it is its own microservice) accepts this token, and it is the JWT refresh endpoint. It must be accompanied by a CSRF token in the post body to prevent CRSF on that endpoint. The JWT refresh endpoint stores a session in the database (the id of the session and the user are encoded into the refresh JWT). This allows the user, or an admin, to invalidate a refresh token as the token must both validate and match the session for that user.
This works just fine but is much more complicated than just using session-based auth with cookies and a CSRF token. So if you don't have micro-services then session-based auth is probably the way to go.
If you are using AWS Amplify & Cognito this will do the magic for you:
Use Auth.currentSession() to get the current valid token or get new if the current has expired. Amplify will handle it
As a fallback, use some interval job to refresh tokens on demand every x minutes, maybe 10 min. This is required when you have a long-running process like uploading a very large video which will take more than an hour (maybe due to a slow network) then your token will expire during the upload and amplify will not update automatically for you. In this case, this strategy will work. Keep updating your tokens at some interval.
How to refresh on demand is not mentioned in docs so here it is.
import { Auth } from 'aws-amplify';
try {
const cognitoUser = await Auth.currentAuthenticatedUser();
const currentSession = await Auth.currentSession();
cognitoUser.refreshSession(currentSession.refreshToken, (err, session) => {
console.log('session', err, session);
const { idToken, refreshToken, accessToken } = session;
// do whatever you want to do now :)
});
} catch (e) {
console.log('Unable to refresh Token', e);
}
Origin: https://github.com/aws-amplify/amplify-js/issues/2560
services.Configure(Configuration.GetSection("ApplicationSettings"));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddDbContext<AuthenticationContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection")));
services.AddDefaultIdentity<ApplicationUser>()
.AddEntityFrameworkStores<AuthenticationContext>();
services.Configure<IdentityOptions>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 4;
}
);
services.AddCors();
//Jwt Authentication
var key = Encoding.UTF8.GetBytes(Configuration["ApplicationSettings:JWT_Secret"].ToString());
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x=> {
x.RequireHttpsMetadata = false;
x.SaveToken = false;
x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
ClockSkew = TimeSpan.Zero
};
});
}