I want to make a call every X amount of minutes from the client side to see if the JWT is still valid. I'm not sure how to do this in nodeJS. If I'm already authorized, how can i check if I'm still authorized.
An elegant solution to handle token expiration is when you set the token(in LocalStorage or store(redux), or both) is also to have an Async function that runs exactly when the token expires. Something like this:
const logUserOut = token =>{
setTimeout(()=> MyLogoutFunction(), token.expiresIn)
}
This way you make sure that the user won't be logged when the token is no longer valid.
You can have your client side decode the JWT and check an expiry field and compare it with system time.
eg.
isExpired: (token) => {
if (token && jwt.decode(token)) {
const expiry = jwt.decode(token).exp;
const now = new Date();
return now.getTime() > expiry * 1000;
}
return false;
you can use npm install jsonwebtoken or some other npm package on the client side to do this
Create and endpoint that verifies the token is valid. You can use the the jsonwebtoken package.
import jwt from 'jsonwebtoken';
const verifyToken = (req, res) => {
const token = req.headers.authorization;
jwt.verify(token, SECRET_KEY, (err, decoded) => {
if (err) {
return res.status(401).send();
}
// can do something with the decoded data
})
}
router.post('/verify-token', verifyToken);
I found a better option with promise to check if my token is valid
jwt.verify(token,key,(err,result)=>{
if(err){
if(err.name == "TokenExpiredError"){
console.log("Expired") //This case is when token expired
}
else{
console.log(err.name) //Any other case
}
}
else{
//Here code for your promise using 'result' when token is Valid
}
})
EDIT: this code is OK if you don't use the JWT for security reason, only if you use it to public stuff.
since this request is show to user if it valid or not, and if not why.
I'm using it only to understand if object is still available for other reason. (and it's OK to be public)
Related
any idea why this test code always prints fail, I designed this code to figure out what is wrong with my authentication server for my school project and I wrote this to always verify but it never does
var refreshToken = jwt.sign({username: "test",}, "secret");
const tokenARR = refreshToken.split('.');
const signiture = tokenARR[2];
jwt.verify(signiture, "secret", (err, user) => {
if (err) {
console.log("fail");
}
else {
console.log("TEST");
}
})
Yes, the 3rd part of a JWT token is signature. But the jwt.verify function verify and decode the token at the same time.
So it must be jwt.verify(refreshToken) instead of jwt.verify(signiture)
So I have my app working properly regarding JWT. I can log in with a certain user, log out, perform operations with such user, etc.
However, I'm not sure how to handle the expiration of the token. I am using Angular and the localStorage in order to store the token.
Is there a way of removing the localStorage with Angular if the token is expired?
I have the following code:
io.on('connection', async (socket) => {
let token = socket.handshake.auth.token;
if (token) {
try {
let userTkn = jwt.verify(token, config.secret);
//A bunch of code that does stuff
} catch (err) {
//Expiration and other errors handling
}
I have a suspicion about the relative security of some authentication middleware code I came across in a course im enrolled in.
So I used postman to send a request to a protected route(see route code below) and found that I was able retrieve an order for one user with a token generated for another user.
const protected = asyncHandler(async (req, res, next) => {
let token;
if (
req.headers.authorization &&
req.headers.authorization.startsWith("Bearer")
) {
try {
token = req.headers.authorization.split(" ")[1];
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = await User.findById(decoded.id).select("-password");
next();
} catch (error) {
console.error(error);
res.status(401);
throw new Error("Not authorized, token failed");
}
}
if (!token) {
res.status(401);
throw new Error("Not authorized, No token found");
}
});
export protected
It seems evident to me that this middleware code will only verify if a user from decoded token exists in the DB and but will not limit access to resources based on the user/token.
import {addOrderItems, getOrderbyId} from "../controllers/orderController.js";
import { protected } from "../middleware/authMiddleware.js";
const router = express.Router();
router.route("/").post(protected, addOrderItems);
router.route("/:id").get(protected, getOrderbyId);
//:id is the order id
However, when testing another protected route for updating a user's profile info, I receive an error when using wrong token.
Was hoping for some clarification
jwt.verify will only verify that the given token is generated by the server or not. It doesn't care which user send this token.
For your protected middleware, it just check if the request is authorized. If so, the request will pass to the controller.
As for the updating route. It probably be something like this:
// route
router.route("/:userId", protected, updateController)
const updateController = (req, res) => {
const user = req.user; // this is the one generated by protected middleware
const reqUserId = req.params.userId; // this is the one send by request
if (user.id !== reqUserId) {
// if two ids are not the same, it means someone is trying
// to update the profile with the wrong token
res.status(401);
}
// update profile in database
}
I'm currently using Vue in the frontend and Express in the backend, and using Express's JSONWebToken plugin to handle authentication. But right now, I have no idea how to store the jwt safely in the cookie, and how can I get the User object from it. I found lots of blog posts, tutorials, etc, but none of them meets my needs. Thanks a lot for this!!
Basically the JWT is kind of safe in a httpOnly cookie. There are still some real issues with that. To fix the issues with a simple accessToken, you need to create a additional refreshToken (take a look at that article
You store the refreshToken in a cookie and the accessToken in a simple cache.
First you need to verify the sent jwt. You do that with the verify method from the jwt package.
If it was successfull you get a payload back from the verify method. In the payload is the user and you can parse it to your object.
This is a basic javascript implementation:
export const isAuth = ({ context }, next) => {
const authorization = context.req.headers['authorization'];
if (!authorization) {
throw new Error('not authenticated!');
}
try {
const token = authorization.split(' ')[1];
const payload = verify(token, process.env.ACCESS_TOKEN_SECRET!);
context.payload = payload;
} catch (err) {
console.log(err);
throw new Error('not authenticated!');
}
return next();
};
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().