The problem is that even after expiration time that is 5 min for ID token and access token, the session is still validated for some reason I cannot understand. Can someone help me what should I Do?
let cognitoUser = userPool.getCurrentUser();
if (cognitoUser != null) {
await cognitoUser.getSession((err, session) => {
if (err) {
console.error(err.message);
return;
}
console.log('session validity: ' + session.isValid());
I tried waiting for 5 min or more to check if token has expired or not but it still says that it is valid whereas I was expecting it will go to refresh token function.
Ok, so I found what getSession does. It gets a token from your local storage, object storage or refresh token. Suppose it doesn't find the currentUserSession when you call getCurrenUser(). In that case, it will use the refresh token to get the session. It simply means that the already available session data was nullified and replaced with the new one you just got. The session will always stay valid whenever it uses a refresh token to get session data.
Related
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/
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.
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();
}
}
I'm making an OAuth2 call from my ASP.NET MVC web application to my Web API through JavaScript to authenticate my user and get a token. The Web API accesses a SQL Server database where the user's login is stored using Identity and the typical AspNetUsers tables. My API call returns a 20 min access token and a 2 week refresh token. The API and consuming apps are products that we are developing that our customers will register with. In other words, all the code is on our side.
I know I must refresh the access token before it expires by passing the API the refresh token. My question is...where do I store the access and refresh tokens on the client for use in my JavaScript to make subsequent API calls or to refresh the token? People online are saying that storing anything client-side is bad, cookies are unsecure, etc and without offering any solutions. Local storage? But of course these are Ajax calls in JavaScript that we are making to the API, so the tokens needs to exist somewhere on the client side! It's driving me crazy trying to figure this out. I know I need to at least use HTTPS.
I'd suggest you to create a table in database to store the refresh token and access token.
Table structure will look like below
ID,Access_Token,Refresh_Token,LastUpdated_Time
Whenever you're calling a API with access token , please check the current time and LastUpdated_Time of token , if it is more than one hour your token will become invalid, so you need to get another valid token using your refresh token.
In my application , I had 55 minutes lifespan of toke, after that time token gets invalid.
Code
if (dateTimeDiff > 55) {
var request = (HttpWebRequest) WebRequest.Create("https://www.googleapis.com/oauth2/v3/token");
var postData = "refresh_token=your refresh token";
postData += "&client_id=your client id";
postData += "&client_secret=your client secret";
postData += "&grant_type=refresh_token";
var data = Encoding.ASCII.GetBytes(postData);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
request.UseDefaultCredentials = true;
using(var stream = request.GetRequestStream()) {
stream.Write(data, 0, data.Length);
}
var response = (HttpWebResponse) request.GetResponse();
string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
}
The response will contain new access token, don't forget to update your table with LastUpdated_Time and new token.
The auth token is something that you can always obtain using the refresh token. So, have an interceptor that validates the incoming and outgoing requests and store the auth-token there. Coming to the refresh-token, in our application we were initially storing in cookies, later moved to local storage.
You can tried to store the token on the localStorage, what I am doing right now is(I am using typescript and react, but I think it will give a clue for your web app):
const msg = await login({ ...values, type });
let accessToken = msg.accessToken;
window.localStorage.setItem("x-access-token", accessToken);
I am using Meteor and its account system. My enrollment process involves a few steps on resetting the password.
Template['enrollment'].events({
'submit #reset-password-form': function (e, template) {
e.preventDefault();
let token = Session.get('_resetPasswordToken');
let user = Meteor.users.findOne({ "services.password.reset.token": token });
let password = $(e.target).find('input#password').val();
if (AutoForm.validateForm('reset-password-form')) {
resetPasswordAsync(token, password)
.then(() => {
return Meteor.promise('Orders.initialize', template.data);
})
// a few more `.then()s`
.catch((error) => {
Notify.warn(error.message);
Meteor.call('User._resetToken', user, token);
})
}
}
});
The reason for this is because if anything fails in the promise chain, then they will remain on the same page but have an "uninitialized" state.
I use a meteor method, because a user should not be able to change his/her services to change their token back.
Meteor.methods({
'User._resetToken': function (user, token) {
check(user, Meteor.users.simpleSchema());
check(token, String);
Meteor.users.update(user._id, {
"services.password.reset.token": token
});
}
});
I vaguely feel like this is insecure, but can't quite tell why. Is there any exploits where resetting the user token on a callback can be exploited?
Firstly don't provide the user object to the method, a user might be able to reset a different user's reset token.
Secondly, just because you've put it in a server method doesn't make it secure. A user from the console can type Meteor.call(user,token) and reset their token or any other user's token (assuming they know the other user's _id.
If the user has already reset their password the token is no longer necessary at all. It only needs to lead a short life.