Refresh token how to handle POST routing - node.js

I am new to JWT and tokens for user verification and login. I used the following extensions for Node JS (NPM)
var jwt = require('jsonwebtoken');
const cookieParser = require('cookie-parser')
require('dotenv').config();
// Express ..
I already have a login that checks in MongoDB (Node JS as server) the user, checks email and password and then sets a cookie with access token and refresh token.
My login code is like
//create the access token with the shorter lifespan
let accessToken = jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET, {
algorithm: "HS256",
expiresIn: process.env.ACCESS_TOKEN_LIFE
})
//create the refresh token with the longer lifespan
let refreshToken = jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET, {
algorithm: "HS256",
expiresIn: process.env.REFRESH_TOKEN_LIFE
})
//send the access token to the client inside a cookie
res.cookie("_login", accessToken, {secure: true, httpOnly: true})
res.send()
and here is the part for refresh token post
exports.refresh = function (req, res, next){
console.log("Test");
let accessToken = req.cookies._login
if (!accessToken){
return res.status(403).send()
}
let payload
try{
payload = jwt.verify(accessToken, process.env.ACCESS_TOKEN_SECRET)
}
catch(e){
return res.status(401).send()
}
//retrieve the refresh token from the users array
let refreshToken = payload.email.refreshToken
//verify the refresh token
try{
jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET)
}
catch(e){
return res.status(401).send()
}
let newToken = jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET,
{
algorithm: "HS256",
expiresIn: process.env.ACCESS_TOKEN_LIFE
})
res.cookie("_login", newToken, {secure: true, httpOnly: true})
res.send()
My question now is, since I see in so many tutorial and guides that they also work with refresh token via POST, how do I process that with the user?
Would the client send an AJAX or post to middleware for check on access token
-> If Access token is expired
--> code automatically takes refresh token and issues a new access token and gives an OK?
or the client sends to middleware where access token is checked
-> Access token is expired (result to the user)
-> client make now post request to /refresh-token result = new access and refresh token
-> And again post request to original middleware with new post request?
What is the procedure here, I can't find any workaround how this is handled.

Keep in mind that my answer is based on my experience. Feel free for anyone to edit if I happen to make a mistake in my ways.
So, in order to handle refresh token, I use this way:
When a user successfully logs in, the JWT (containing user's auth) and the refresh token (containing the user's refresh token) will be placed in the user's cookies (same like you).
The user will do his/her things inside your web application, until he/she closes the browser without signing out.
Remember that JWT always have expiration date - this expiration date will be kept in mind.
In every request, you're going to send that specific JWT (that contains the user's auth) and the refresh token to a middleware where you are going to make a POST request. If the JWT has expired, pick the refresh token and call your /refresh-token-result to get a new token. Else, just don't do anything with the refresh token and proceed with your request.
Ensure that your /refresh-token-result accepts a request token. The endpoint will check for its validity and will return a new access token.
If the refresh token had expired, log out the user. This is for security reasons and this is important!
Oh, and when a user logs out, ensure that both of your user's token and your user's refresh token is revoked properly, usually by changing the cookie value and the expiresIn attribute. For me, I usually change both cookie values to loggedOut and I'll set the expiresIn to five seconds.
Alternatively, if you're using React (an additional answer), you can do it like this:
If a user accesses your website, and the JWT expiration date is close to expiry, you can simply use a useEffect() hook in order to renew your access token.
TL;DR: Your second way is already good.
EDIT: Sample pseudocode to help you. Don't copy paste this right away, it most likely wouldn't work, but it should give you the general idea of how things work.
// middleware.js
const { token, refreshToken } = req.cookies;
// 1. If the token has not expired, call 'next()'
// assume 'isExpired' returns boolean: true or false depending on the state of your token.
if (!token.isExpired()) {
return next();
}
// 2. If the token has expired AND the refreshToken has not expired, issue a new token, THEN call 'next()'
if (token.isExpired() && !refreshToken.isExpired()) {
await issueToken();
return next();
}
// 3. Else, logout the user. I'll keep this one short.
await logoutUser();
res.status(401).json({
status: 'fail',
message: 'Your access has expired! Please log in again!',
});
And this is your controller.
// controller.js
const getAllComments = async (req, res, next) => {
const comments = await Comment.find();
res.status(200).json({
status: 'success',
data: comments,
});
}
And, this is what your route should look like.
// this import might be unresolved - keep in mind!
const middleware = require('./middleware');
const getAllComments = require('./controllers');
router.get('/api/v1/comments/', middleware,
checkToken, getAllComments); // assume checkToken is your function to check for a token's validity.
Keep in mind I did not include error handling to keep this example short.

Related

using refresh token to get new access token react and node js

I've built a JWT API for user authentication. I want to use it with react.
my app has these routes:
Node js API Routes:
http://localhost:8080/api/auth/signin -> this route accepts username and password from react and sends back an access token to react which it will save it inside localstorage(I'm planning to use memory instead) and cookie containing refresh token with httpOnly enabled.
http://localhost:8080/api/auth/signup -> this route will add a new user to database based on user input(doesn't relate to authentication process)
http://localhost:8080/api/auth/refreshtoken -> this route will return a new access token based on the sent refresh token from client which was saved inside cookies. and then client will replace it with the expired access token.
http://localhost:8080/api/test/user -> this will check if user is signed in with the access token from client and will send the user data back
React Client Routes:
http://localhost:3000/login -> a route for sending user information for logging in
http://localhost:3000/register -> a route for creating a new user
http://localhost:3000/user -> a protected route which needs user to be logged in to view data.
when I log in to my website I can see the data inside the user route for 20 seconds before the access token expiry. when the access token is expired and I do a new request the server will response with 401 unauthorized code. so this way I can make sure that the access token is expired and I need to use refresh token. I've done that like this:
const API_URL = "http://localhost:8080/api/test/";
const getUserBoard = () => {
return axios.get(API_URL + "user", { headers: authHeader() })
.catch((error) => {
if(error.response.status === 401) {
// if error response status was 401 then request a new token
authService.refreshToken();
window.location.reload();
}else if(error.response.status === 403) {
authService.logout();
window.location.href = "/login";
}
});
};
code to get new access token with refresh token:
const refreshToken = () => {
axios({
url: API_URL + "refreshtoken",
method: "POST",
withCredentials: true
})
.then((response) => {
if(response.data.accessToken) {
const user = JSON.parse(localStorage.getItem("user"));
user.accessToken = response.data.accessToken;
localStorage.setItem("user", JSON.stringify(user));
}
})
.catch((error) => {
console.log("server: " + JSON.stringify(error.response));
})
}
code to set header for receiving data from server using access token:
export default function authHeader() {
const user = JSON.parse(localStorage.getItem('user'));
if (user && user.accessToken) {
// for Node.js Express back-end
return { 'x-access-token': user.accessToken };
} else {
return {};
}
}
the first part for getting new access token on 401 error works good but my refresh token also expires after 40 second. if I send the expired refresh token it won't return a new access token. and because of that again I will receive a 401 error which again will cause a 403 error and here I'll get stuck in an infinite reload loop.
any idea? how can I control tokens expiry?
You're issuing tokens in your node.js app, right? So that is where you should adjust the expiration time of the token. The code which issue tokens should have an option to set the expiration time.
Remember that once the refresh token is expired you should log in again. You can implement something which is called a rolling refresh token. So whenever you call the /api/auth/refreshtoken endpoint you can also issue a new refresh token, with a new expiration time and return it in a cookie.

Recognizing first user login (Node.js/JWT)

I've developed an authentication system who consists on Node.js/Express and JWT.
I need to recognize the first time a user logs on and show him a message relevant only on that one time.
How can I do it?
This is the auth middleware i'm using to verify the user:
const auth = (req, res, next) => {
const token = req.header("x-auth-token")
if (!token) return res.status(401).json({ msg : "No token, authorization failed" })
try {
const decoded = jwt.verify(token, config.get("jwtSecret"))
req.user = decoded
next()
}
catch {
res.status(400).json({ msg : "Token is not valid" })
}
}
For this approach, you should let User to login with Basic Authentication for the first time (Email/Username + Password). After you validate that the user is the valid user on the backend, then you should store his _id in JWT and send him that JWT. On each next request, that user should send that JWT, and you can use your JWT authentication (but for initial login, you should go with Basic Authentication).

force expire jwt token in adonis js

Is there a way to force expire jwt token (not refresh token) in adonis-js.
I am creating a token on login and setting its time to expire for 10 mins. When I log out I want to force expire that token before 10 mins.
Try these things for your token.
The tokens can be expired. But you cannot do it on demand.
Set a reasonable expiration time on tokens.
Delete the stored token from the 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.
I did it this way:
async login({ request, auth }) {
const { email, password } = request.all();
const user = await auth.validate(email, password, true);
const { name, admin, confirmed } = user;
const token = await auth.generate(user, false, { expiresIn: '10m' })
return { token, user }
}

Node.js express, request jwt token - how to properly get a new valid token if expired in request

On successful login I store the returned JWT token in Session.
Then in my search route I'm accesing the api with the jwt token from the session and set in the header like this:
router.post('/search', (req, res) => {
var getToken = req.session.APIToken;
var auth = 'Bearer '+getToken;
request.get({
headers: {
"authorization": auth
},
url: "localhost/abc/search?name=peter"
}, (error, response, body) => {
if(error) {
return console.dir(error);
}
var jsonBody = JSON.parse(body);
if(jsonBody.status === 200) {
console.log('Request successful!');
}
if(jsonBody.success === false) {
/// ??? BUT what if the JWT token is expired! How do I properly refresh or get a new valid jwt token here? ///
console.log('Token expired!');
}
});
});
BUT what if the JWT token is expired! How do I properly refresh or get a new valid jwt token here?
I suppose with a callback function using the email and password saved in session? But how do I do this exactly or what is the best way to do this?
Edit: I have two apps ... one frontend app ... and one separated API app. So when logging in ... I login in via the frontend app but get the JWT token from the API app. The login session from the frontend app is 360s ... the JWT token is valid for the 60s .... so in case the JWT token expired when the user does a search request ... I want to automatically generate a new token that finishes the request.
I think you are trying to implement auth0 with JWT token. The best practice is to always return 2 tokens when login:
access_token (Using JWT): The benefit of JWT is that the server doesn't need to query back to database to verify and get information about the user who is calling the API
refresh_token: This is useful for the user to regenerate the token when the access_token is expired.
The the client-side application, you should always save the 2 tokens. Before calling any API, you should check if the JWT is expired. It can be done easily using jwt.decode(). The expired information is usually stored in the exp or iat key. If It's expired, you need to call the API to regenerate the token before calling the actual API.
You can find an example here: https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/
Hope this helps.

Save Token in local Storage using node

I'm using JWT ("jsonwebtoken": "^5.4.0") with express 4 and jade.
I'm able to create the right Token, but How can i Pass this token in each call?
Where I have to store this token ? in headers or in localStorage?
For now I'm using CURL with Postman, and Set token in header in
x-access-token
Have I Do create a middleware that retrieve a token from Database and use this in each call?
thanks
You do not need to save and check the token from the database. This token such a mechanism can be decoded with only your-server, and if it was done that the token is valid. The code that you want to do should look like.
var cookieParser = require('cookie-parser')
app.use(cookieParser())
app.get('/login', function(req, res, next) {
var user = {name:'test'}; //!! find the user and check user from db then
var token = jwt.sign(user, 'secret', {
expiresInMinutes: 1440
});
res.cookie('auth',token);
res.send('ok');
});
app.use(function(req, res, next) {
var token = req.cookies.auth;
// decode token
if (token) {
jwt.verify(token, 'secret', function(err, token_data) {
if (err) {
return res.status(403).send('Error');
} else {
req.user_data = token_data;
next();
}
});
} else {
return res.status(403).send('No token');
}
});
Here you can find very nice article : https://scotch.io/tutorials/authenticate-a-node-js-api-with-json-web-tokens
I would recommend checking this out if you want local storage: https://www.npmjs.com/package/node-localstorage
But, with that said, you guys and girls wouldn't believe how long it took me to find res.cookie('auth' token) from the above answer. I scoured Google for hours, Passport docs, Express docs, GraphQL and authentication/authorization docs in an effort to find out how to get the token to the API in a stateless manner.
I already built JWT token security and secured my GraphQL resolvers with it, but then, I opted to use EJS along with graphql-request (approx same as Apollo Client), so I needed to find a way to pass the token to my middleware without using a server side session.
Storing a JWT token in cookies is fine especially if you take extra precautions such as signing the cookie, and I recall there are also options you can include that keep the cookie secure, so that other sites cannot see it if the "browser" allows access to cookies. If a cookie is signed with your server secret, the data inside the cookie simply cannot be altered and still be valid. The risk is always still someone leaking their token/cookie, and if that bothers you, do research into refresh tokens. However, API tokens are generally and should be kept tightly secret and safe. Your biggest annoyance will more likely be the requirement to maintain a blacklist of JWTs that expire a year from now if you set expiry to 1y.
I am just including my findings here because this question is actually a rare resource it seems...
Here is my Express middleware for authentication:
// AUTHENTICATION
app.use(async (req) => {
try {
const token = req.headers.authorization || req.cookies.auth
const { person } = await jwt.verify(token, SECRET)
req.person = person
return req.next()
} catch (e) {
return req.next()
}
})
You can see I am setting the token from the header with cookie as fallback. This supports my needs fine and allows me to use really any client with stateless security.
My logged in user is available as req.person in my views and GraphQL resolvers. If req.person is not set, the user is treated as not-logged-in.
I am using return req.next() which is important to note because calling next() without parameters is treated as "clean go-to next middleware and/or proceed to process request". If you include any string or object parameter, it will throw an error that can bubble down to your error handling middleware. You can try it yourself. Put return next('You are not authenticated.') in the catch block and you will see it halt the request before your route.
I use return next() because I handle authorization in the routes and in my resolvers. It allows more flexibility such as facilitating register and login mutations to be accessed by non-authenticated users.
Here is my GraphQL endpoint (I am using Apollo Server):
app.use('/graphql', bodyParser.json(), graphqlExpress((req) => {
const context = {
person: req.person
}
return {
schema,
context,
rootValue: null
}
}))
In my GraphQL resolvers, the third parameter of every query has context.person populated with req.person which comes from the above Authentication middleware.
That is really all a person needs to know.
Here is how I am using the NPM package called graphql-request:
https://www.npmjs.com/package/graphql-request
app.get('/allpeople', async (req, res) => {
try {
const client = new GraphQLClient(GRAPHQL_ENDPOINT, {
headers: { Authorization: req.headers.authorization || req.cookies.auth }
})
const query = `query allPeople($serialNumber: String!) {
allPeople(serialNumber: $serialNumber) {
id
created
status
email
}
}`
const variables = {
serialNumber: req.person
}
const response = await client.request(query, variables)
res.render('allpeople/list', { people: response.allPeople })
} catch (e) {
throw [`allPeople`, `${JSON.stringify(error, null, 2)}`]
}
})
I include this code because there are no "more advanced" example usages of graphql-request, and I like it so far. It is very concise and could easily be swapped out for Apollo Client if you venture into React.js. My examples here are also very relevant for anyone researching createNetworkInterface and new ApolloClient().

Resources