How to deal with a TokenExpiredError: jwt expired? - javascript

I have a project with NodeJS with jwt for managing user authentication. After a period of time, my app stops working and my server prints the following:
return done(new TokenExpiredError('jwt expired', new Date(payload.exp * 1000)));
^
TokenExpiredError: jwt expired
Now, the person who was working in this project before me had this code for managing the tokens:
static generateToken(username, type, id) {
return jwt.sign(
{
username,
type,
id
},
API_SECRET,
{
expiresIn: '7d'
}
);
}
static generateRefreshToken(username, type, id) {
return jwt.sign(
{
username,
type,
id
},
API_SECRET_REFRESH,
{
expiresIn: '30d'
}
);
}
My guess is that the issue is the expiresIn: '7d' (since I'm quite new with jwt). I also know what we can omit the expiresIn field to make the token not expire at all. My question is: Is it safe to omit said field or there's another way to tackle this kind of error message? Since every time that message pops up, I have th delete the entire browsing history of my browser (or run my project in incognito mode) in order for it to start working again.

You should not create a token that does not expire. So, you should keep the expiresIn config.
That being said, you don't have to use refresh token strategy. There are a lot of ways to handle this. For example, you can do the following:
Create /check-token endpoint that will check if the current token is still valid.
Create token when user logs in. Token will be valid for 7 days for example.
Each time user opens your application call the /check-token endpoint. If current token is not valid, logout the user. If current token is valid, generate new token that will be valid for another 7 days and continue to authenticate the user with new token.

Yes, this is one of the most common problems, it is possible to solve it with a refresh token, what this function does is that when the token expires, a request is made from the front to obtain a new one, here is a link with an example of implementation in node js:
Link: https://www.bezkoder.com/jwt-refresh-token-node-js/

Related

firebase provider authentication refresh flow

I am trying to use firebase authentication to get a google access token in order to call the youtube API.
I am able to initially get the access token like this:
const provider = firebase.auth.GoogleAuthProvider();
cosnt scopes = []; // ...scopes
scopes.forEach(scope => provider.addScope(scope));
firebase.auth().signInWithPopUp(provider)
.then(userCredentials=> {
const oauthCredentials = userCredentials.credentials;
// using credentials for API calls
axios.get("some-google-api-path", { params: { access_token: oauthCredentials.accessToken } }); // and so on...
});
This works fine until the access token expires.
As far as I can tell, it seems like firebase automatically refreshes the session, but I can find a way to get the new access token.
I tried:
firebase.auth().onAuthStateChange(user => {
// could not find the access token on the user object
});
And since that failed, I tried to do it manually using:
const token = firebase.auth.GoogleAuthProvider.credential(
oauthCredentials.idToken,
oauthCredentials.accessToken
);
const authResult = await firebase.auth().signInWithCredential(token);
The issue is that authResult will only contain the idToken or the accessToken, but not both, depends on what I give the firebase.auth.GoogleAuthProvider.credential function.
Am I missing something?
Is there another/better way to do it?
There are two tokens in play here:
The refresh token, which is only available after the user actively signs in. Since it doesn't change, you can cache this token - which is what the Firebase SDKs actually also do.
The ID token, which is available at any moment from the current user. This token expires in an hour, and is auto-refreshed by the Firebase SDKs.
So if you want to retain access to the refresh token, you will have to store (typically in local storage) yourself when the user first actively signs in.
So apparently firebase doesn't refresh tokens of other providers for you (not event google) according to this (thank you Frank van Puffelen!)
So what I did instead, is authenticate manually to google (since I use react, I used react-google-login), and got all tokens from there.
Then, once the session is initiated/refreshed, I create a firebase session using:
const token = firebase.auth.GoogleAuthProvider.credential(
oauthCredentials.idToken,
oauthCredentials.accessToken
);
const authResult = await firebase.auth().signInWithCredential(token);
I hope that this helps anyone, and I will accept another answer if firebase ever changes this.

What does it mean for a jwt token to expire

I'm implementing jwt token for user verification purposes. I sign a jwt token whenever a user sign-ins and store that token in my database. Then whenever the user wants to access their personal information, they send me their token and I check if such token exists in the database, if it does, I allow access (please reccommend if you know better ways for user verification). The thing is, I want my token to expire every 24 hours, which means every 24 hours I want the token in my database to become an empty string, however I did some experiments:
const jwt = require('jsonwebtoken')
var token = jwt.sign({d:'tt'}, 'prvateKey', {expiresIn: 1000});
setTimeout(()=>{
console.log(token)
}, 3000)
the code above console logs the actual token instead of an empty string, null, or something like that.
So is my code somewhat wrong or does expiration mean something different than what I think?
jwt.sign returns the token as a string. This will not automatically become empty on expiry, it's just a string. However, the token will become invalid. You can check token validity using the jwt.verify method. jwt.verify will error if the token is not valid.
The expiresIn option is a convenience to set the token exp claim. This is what the jwt spec says about this claim:
The "exp" (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing.
https://www.rfc-editor.org/rfc/rfc7519#section-4.1.4
The jsonwebtoken package documentation also says:
expiresIn: expressed in seconds or a string describing a time span zeit/ms.
https://www.npmjs.com/package/jsonwebtoken#token-expiration-exp-claim
With the jsonwebtoken package, an expired token actually translates in the verify() function throwing an exception with a TokenExpiredError name when passing it the token for processing.
var jwt = require("jsonwebtoken")
var data = {
"data": "some data"
}
var secret = "secret"
var token = jwt.sign({ data }, secret, { expiresIn: 1 })
setTimeout(function () {
try {
var payload = jwt.verify(token, secret)
} catch (error) {
console.log(error.name) // output: TokenExpiredError
}
}, 1000)

AWS Cognito - Use Refresh Token immediately after login

I want to force-refresh the AWS cognito token in the client, so that as soon as a user logs in the app immediately uses the refresh token to get a new access token (with longer exp time). I've read about initiateAuth and cognitoUser.refreshSession() methods, but I'm not sure which one I need to use? I'm trying the below method and I am getting a new token back but it's always just valid for 1 more hour (at least that is what I see when I check the exp time on the token). I want to get a token that is valid for 30 days (which is what I have configured in cognito). Any help will be greatly appreciated!
const getTokens = function(session) {
return {
accessToken: session.getAccessToken(),
idToken: session.getIdToken(),
refreshToken: session.getRefreshToken()
};
cognitoUser.refreshSession(refreshToken, (err, session) => {
if (err) {
console.log(err);
} else {
const tokens = getTokens(session);
console.log(tokens);
localStorage.setItem("sessionTokens", JSON.stringify(tokens));
}
}
The ID and Access token in Cognito are valid for 1 hour and this is not configurable. You mentioned you have configured the tokens to last for 30 days, this is the validity/expiry time of your refresh tokens.
Calling certain methods on the client side SDKs (Amplify or identity SDK) will automatically check the validity and expiry time of the tokens and refresh them if needed.
This has now changed as can be seen here:
https://aws.amazon.com/about-aws/whats-new/2020/08/amazon-cognito-user-pools-supports-customization-of-token-expiration/
Cognito recently added options to configure the token validity.
Refresh tokens can have a TTL from 60 minutes to 365 days.
ID tokens and Access tokens can have a TTL from 5 minutes to 1 day
just look in the details of your user pool app client, the new fields are in there for easy configuration.

Best way to set timer in React/Redux for sending refresh auth token

I'm using the access refresh jwt authentication flow and want the client to send a refresh token to get a new access token every 10 minutes after it received the access token. I also want to make sure that if the user closes their laptop for an hour and comes back to it that a new access token request is also sent. What is the best way to implement this behavior in React/redux, where the user will always have a valid access token and seamlessly keeps their "session" going?
To achieve that you can conditionnally check the expiration of the token before each request instead of setting a timer.
This will depend on the way you communicate with the server but the idea is to store client side the jwt token, his expiration and the refresh token (and his expiration too if needed) then use some sort of middleware before each request that need the auth :
const authClientMiddleware = (done) => {
if (new Date(localStorage.getItem('expiration')) <= new Date()) {
getNewToken(localStorage.getItem('refreshToken')).then(() => done()).catch(() => logout());
} else {
done();
}
}

Skype Web SDK - Maintain a user session

I am using Skype Web SDK to get a user's contact list in the following manner.
Skype.initialize({
apiKey: 'a42fcebd-5b43-4b89-a065-74450fb91255',
}, function (api) {
var Application = api.application;
var client = new Application();
client.signInManager.signIn({
username: sip,
password: pwd
})
This works fine when I provide the username(sip) and password. However, when I reload the page, I have to provide the credentials again because the app re-initializes. Is there a way to maintain the user's sessions for a while after the initial login so that the page refreshes wouldn't need ask for credentials again?
I have looked through the samples and docuementation that Microsoft has and couldn't find a way. I've also tried to store the client object in the localStorage after the initialization and sign in, but when I tried to reuse the object from localStorage to get the contact list, it did not work.
http://officedev.github.io/skype-docs/Skype/WebSDK/model/api/interfaces/jcafe.signinmanager.html#signin last example explains that you can store oauth token and use it as unexpired token.
To connect to an existing app's event channel, specify id of that app:
sm.signIn({
username: "user1#company.com",
password: "password1",
id: "273867-234235-45346345634-345"
});
To sign in to Skype for Business Online using OAuth while handling the
logic of retrieving OAuth tokens yourself:
sm.signIn({
client_id: '123-456',
origins: [ 'https://webdir.online.lync.com/AutoDiscover/AutoDiscoverservice.svc/root' ],
cors: true,
get_oauth_token: function(resource) {
// Return a valid unexpired token for the specified resource if you already have one.
// Else, return a promise and resolve it once you have obtained a token.
return 'Bearer eyJ0e...';
}
});

Categories