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)
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/
So i have been reading about the difference between ID token and access token is for authorization. I don't know how to implement the ID token, which should be a JWT.
The access token i have been implementing looks like this:
const accountCredentials = {
username: account.username,
accountId: account.accountId,
grant_type: "password"
}
const token = jwt.sign(accountCredentials, config.JWT_KEY, { expiresIn: "1h" })
so i wonder, what is an ID token supposed to contain and how to implement it as described by the openID connect specification. I'm sorry, this might sound stupid but i really don't understand the documentation.
An ID token is a token which allows your app to identify the user. It is always a JWT, and it's contents are described in this section of the OIDC docs: https://openid.net/specs/openid-connect-core-1_0.html#IDToken So to have a valid ID token, you need a JWT which contains at least these claims: iss,
sub, aud, exp, iat. You can check in the spec, what each of these claims should contain. Additionally, the ID token must be at least signed (it can also be encrypted).
In your code you will have something like that:
const accountCredentials = {
iss: '...',
sub: account.username,
aud: '...',
iat: now()
}
const token = jwt.sign(accountCredentials, config.JWT_KEY, { expiresIn: "1h" })
Note that OpenID Connect spec only specifies authorization code, implicit and hybrid flows. Using password grant type is not part of the OIDC standard.
Using https://github.com/netlify/gotrue-js to interface with netlify's authentication service (called "Identity") how frequently is it necessary to do the following:
const user = auth.currentUser();
const jwt = user.jwt();
jwt
.then(response => console.log("This is a JWT token", response))
.catch(error => {
console.log("Error fetching JWT token", error);
throw error;
});
Will the resulting JWT be valid forever? For the duration of the user's logged-in session? Or does it expire after a given amount of time?
Generally JWTs contain can (optionally) contain an exp (expiration) claim, that contains the time when it will expire.
I don't have experience with GoTrue, but according to their documentation you can configure the expiration, and it's set to a default value of 3600 seconds.
As the library also works with refresh tokens, you won't have to re-authenticate again after the token expires but use the refresh token to get a new access token.
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 creating a custom token on backend server while login and below is the code:
UserSchema.methods.generateAuthToken = function() {
var user = this;
var access = "auth";
return firebaseAdmin.default
.auth()
.createCustomToken(user._id.toHexString())
.then(function(token) {
user.tokens = user.tokens.concat([{ access, token }]);
return user.save().then(() => {
return token;
});
});
};
and storing this token inside mongodb and sending it back to client via header named "x-auth" and then stored it inside cookies.
On client side, using this token to signin like below:
axios({
url: "/api/users/login",
method: "POST",
data: {
email: data.email,
password: data.password
}
}).then(res => {
Cookies.set("x-auth", res.headers["x-auth"], { expires: 7 });
return firebase
.auth()
.signInWithCustomToken(res.headers["x-auth"])
.then(response => {
console.log("CA", response);
response
.getIdToken()
.then(idToken => {
Cookies.set("idToken", idToken, { expires: 7 });
})
.catch(e => {});
dispatch(
login({
token: Cookies.get("x-auth")
})
);
});
});
Now in order to call an api to fetch all users, I'm sending these tokens, custom token and idToken back to server:
const authenticate = (req, res, next) => {
const token = req.header("x-auth");
const idToken = req.header("idToken");
User.findByToken(token, idToken)
.then(user => {
if (!user) {
return Promise.reject({
message: "no user found with the associated token!"
});
}
req.user = user;
req.token = token;
next();
})
.catch(e => {
res.status(401).send(setErrorMessage(e.message));
});
};
And findByToken is as below:
UserSchema.statics.findByToken = function(token, idToken) {
var User = this;
return firebaseAdmin.default
.auth()
.verifyIdToken(idToken)
.then(function(decodedToken) {
var uid = decodedToken.uid;
return User.findOne({
_id: uid,
"tokens.access": "auth",
"tokens.token": token
});
})
.catch(function(e) {
return Promise.reject(e);
});
};
Why do I have to send both these tokens for authorization, is there anything wrong with the concept here.
https://firebase.google.com/docs/auth/admin/verify-id-tokens
Warning: The ID token verification methods included in the Firebase Admin SDKs are meant to verify ID tokens that come from the client SDKs, not the custom tokens that you create with the Admin SDKs. See Auth tokens for more information.
Please clarify if I can verify the custom token instead of idToken to retrieve userId and match it up with DB, instead of using different tokens for one purpose or I'm doing something wrong here and custom token should not be stored inside DB, and there is some other approach to it.
And now after sometime when I try to fetch all users, it says:
Firebase ID token has expired. Get a fresh token from your client app and try again (auth/id-token-expired). See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.
When working with tokens, you need to differentiate each token and what it is used for, also each token has related properties.
Custom Tokens: These tokens will be generated in the server (as you are doing) and are used in the client side to authenticate the client. These tokens expire after one hour.
Session ID Token: (I'm calling it "Session ID Token" just to differentiate it) When the client autheticates with any of the authentication providers, the SDK will exchange the information for an ID token used for the session. The same applies for custom tokens, where the SDK exchanges the custom token for an ID Token. ID Tokens are also short live and will expire after one hour. When getting the ID Token, the SDK also receives a refresh Token which is used for refreshing the session ID Token. The ID token is used when doing authenticated requests to Firebase.
ID Token for verification: To get this token, you will need to call the function "getIDToken" (for Web). This function will return an ID Token that can be used only to verify requests coming from the client to your server. Similar to the other tokens, this one expires after one hour. When calling the getIDToken, the function will request a new one if the current is expired.
To verify the ID Token, you must use the mentioned token. If you try to use the session ID Token or the Custom token, you will get errors. The same applies if you try to use an expired ID Token.
All the calls and tasks to refresh the ID Tokens are handled by the SDKs behind the scenes.
It is not a good practice to store these tokens as they will expire after one hour, is better if you call the appropriate functions to get the latets tokens and pass the correct ID Token to be verified.
Lastly, you can find in these links the properties used for generating the custom token and the ID Token for verification.