JWT Token Verification to decode and Protect Routes in MEAN App - node.js

I am trying to authenticate users using JWT. I am assigning a token on login.
if (client) {
// Check if Client does exist, then compare password provided by Client
if (!req.body.password) {
res.json({ success: false, message: "No password provided" }); // Password was not provided
} else {
var validPassword = client.password === req.body.password; // Check if password matches password provided by Client
if (!validPassword) {
res.json({ success: false, message: {password: {message: "Incorrect Password"}} }); // Password does not match password in database
} else {
if (!client.active) {
res.json({ success: false, message: {active: {message: "Account is not activated"}} }); // Account is not activated
} else {
var token = jwt.sign(
{ username: client.username, email: client.email },
secret,
{ expiresIn: "24h" }
); // Logged in: Give Client token
res.json({
success: true,
message: "Client authenticated!",
token: token
}); // Return token in JSON object to controller
}
}
}
}
After login, I am checking the token in requests made my the user.
router.use(function(req, res, next) {
var token = req.body.token || req.body.query || req.headers['x-access-token']; // Check for token in body, URL, or headers
// Check if token is valid and not expired
if (token) {
// Function to verify token
jwt.verify(token, secret, (err, decoded) => {
if (err) {
res.json({ success: false, message: 'Token invalid' }); // Token has expired or is invalid
} else {
req.decoded = decoded; // Assign to req. variable to be able to use it in next() route ('/me' route)
next(); // Required to leave middleware
}
});
} else {
res.json({ success: false, message: 'No token provided' }); // Return error if no token was provided in the request
}
});
I am putting all the protected routes after the check token. Users can access the profile page /me
router.post('/me', function(req, res) {
res.send(req.decoded); // Return the token acquired from middleware
});
How can I check the token in req.body in Angular 11? I have tried to setToken using localStorage but it seems I am not doing it correctly.
localStorage.setItem('userToken', response.token);
It seems to be working fine in Postman when accessing the /me route by passing the token in body. It shows whether the token found or not. If found then it shows the result
{
"email": "example#gmail.com",
"iat": 1634704834,
"exp": 1634791234
}

Everything seems fine. I think, you just need to implement an interceptor on the frontend side. It will pick the auth token from the local storage, and attach it with all the requests.
Sample code
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '#angular/common/http';
import { Observable } from 'rxjs';
import { AuthService } from './service/auth.module';
#Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let loggedInUser = this.authService.currentUserValue;
token = JSON.parse(localStorage.getItem(user.token));
if (token) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
}
return next.handle(request);
}
}
And yeah, sending authentication token in the body params is not considered a good practice. Safe method is to use headers always for such sensitive information. More details can be found here

Related

JWT and nodejs returns invalid token as a response after signin

I'm using nodejs/express and express-jwt module.
this is the server code involved:
async function authenticate({ username, password }) {
const user = await User.findOne({ username });
if (user && bcrypt.compareSync(password, user.hash)) {
if(user.accountActive === true) {
const {hash, ...userWithoutHash} = user.toObject();
const token = jwt.sign({sub: user.id}, config.secret, { expiresIn: '1h' });
console.log(token)
return {
...userWithoutHash, token
};
} else {
throw 'Your account is pending approval.'
}
}
}
function jwt() {
const secret = config.secret;
return expressJwt({ secret, isRevoked }).unless({
path: [
// public routes that don't require authentication
'/users/authenticate',
'/users/register',
]
});
}
i am having {message: "Invalid Token"} message: "Invalid Token" whenever i tried to make a call to any api within the app
Here is the request header: the token is not added to the request headers
i add the authorization header with jwt token if available on the localstorage
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const currentUser = JSON.parse(localStorage.getItem('currentUser'));
if (currentUser && currentUser.token) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.token}`
}
});
}
return next.handle(request);
}
when i login the url logs in and sends the token to an authenticate post route
// routes
router.post('/authenticate', authenticate);
module.exports = router;
function authenticate(req, res, next) {
userService.authenticate(req.body)
.then(user => user ? res.json(user) : res.status(400).json({ message: 'username or password is incorrect' }))
.catch(err => next(err));
}
Here is an example of the authenticate login session request after signin
it seems like jwt token even though its valid it becomes invalid after signin.
to me it seems like a token issue, but i don't know where the problem at exactly?
N.B I finally found where the problem was , i did not add the jwt interceptor to my app.module
by adding the bellow code to the providers fixed it for me:
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
This mean that for the token i am providing a value, more than one value (or class) is going to be used.
angular-cli: 8.3.0
node: 10.16
express: 4.17.1
express-jwt: 5.3.1

Node.js JWT refresh token in express middleware

I'm trying to configure a token refresh method in my express middleware in wich the token is validate at every request to the api. I will check if the token expired and if so, I will sign a new token with new exp date. The problem is that I have to send the token again, but doing thatI lose the original request to send the token with the response and the api not continue to the destination endpoint.
How I can send back the new refreshed token and continue with the request?
My express middleware to check the token:
apiRouter.use(function(req, res, next) {
var token = req.body.token || req.query.token || req.headers['x-access-token'];
if (token) {
jwt.verify(token, app.get('superSecret'), function(err, decoded) {
if (err) {
//Here I can check if the received token in the request expired
if(err.name == "TokenExpiredError"){
var refreshedToken = jwt.sign({
success: true,
}, app.get('superSecret'), {
expiresIn: '5m'
});
//Here need to send the new token back to the client and continue with the request
//but if I use return res.... the request don't continue to next()
next();
}else if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
}
} else {
//If no error with the token, continue
next();
};
});
} else {
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
I dont' know if its the best aproach to this.
Thanks you.
You can not send a response to the client two times for single request, so better way will be sent an access token with the actual API response.
apiRouter.use(function(req, res, next) {
var token = req.body.token || req.query.token || req.headers['x-access-token'];
if (token) {
jwt.verify(token, app.get('superSecret'), function(err, decoded) {
if (err) {
//Here I can check if the received token in the request expired
if(err.name == "TokenExpiredError"){
var refreshedToken = jwt.sign({
success: true,
}, app.get('superSecret'), {
expiresIn: '5m'
});
request.apiToken = refreshedToken;
next();
}else if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
}
} else {
//If no error with the token, continue
request.apiToken = token;
next();
};
});
} else {
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
then when you send a response then send a response with the token, that you can get with request.apiToken.
but a better strategy is to provide a client refresh token and let the client make a request to get refreshed token.
You can read more about that here

How to correctly use the authentication for nodeJs API using JWT and Passport?

I am using JWT-simple for authenticating my express routes.
server side:
var jwt = require('jwt-simple');
var bcrypt = require('bcrypt');
var passport = require('passport');
require('../passport')(passport);
/* Create an Account */
router.post('/signup', function (req, res, next) {
var verifyCode = Math.random().toString(36).slice(-8);
var userData = {
name: req.body.name,
email: req.body.email,
phone: req.body.contact,
password: req.body.password,
verify_code: verifyCode,
status: 0
};
loginService.createUser(userData, function (err, data) {
if (err) {
res.status(500).json({error: true, data: {message: err.message}});
} else {
var token = jwt.encode(data, "secret");
res.json({success: true, data: {token: 'JWT ' + token}});
}
});
});
/* GET the info of an API using the jwt token data */
router.get('/info', passport.authenticate('jwt', {session: false}), function (req, res, next) {
var token = tokenRetrive.getToken(req.headers);
if (token) {
var decoded = jwt.decode(token, configVar.config.secret);
UserService.getContentUserById(decoded.id, function (err, user) {
if (err) {
res.status(500).json({error: true, data: {message: err.message}});
} else {
if (!user) {
res.send({success: false, msg: 'Authentication failed. User not found.'});
} else {
if (!user) {
return res.status(403).send({success: false, msg: 'Authentication failed. User not found.'});
} else {
res.json({success: true, data: user.toJSON()});
}
}
}
});
} else {
return res.status(403).send({success: false, msg: 'No token provided.'});
}
});
client side
var signup = function(user) {
return $q(function(resolve, reject) {
$http.post(API_ENDPOINT.url + '/signup', user).then(function(result) {
if (result.data.success) {
storeUserCredentials(result.data.data.token);
resolve(result.data);
} else {
reject(result.data.msg);
}
});
});
};
function storeUserCredentials(token) {
window.localStorage.setItem(TOKEN_KEY, token);
var loggedIn_user_Data = jwt_decode(token);
$http.defaults.headers.common.Authorization = token;
}
Using REST client (POSTMAN) when I pass the header info to the API I use
API : localhost:8080/info
Key
Authorization
Content-Type
Value
JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiYXR1bCIsImVtYWlsIjoidHJlZUB0cmVlLmNvbSIsInBob25lIjpudWxsLCJwYXNzZHJlc3MiOm51bGwsImNvdW50cnkiOm51bGwsInN0YXRlIjpudWxsLCJwaW5jb2RlIjpudWxsLCJvcmdfaWQiOjAsInJvbGVzIjpudWxsLCJjcmVhdGVfZGF0ZSI6IjIwMTctMDUtMThUMTk6NTE6MDYuMDAwWiIsImxhc3RfbG9naW4iOiIyMDE3LTA1LTE4VDE5OjUxOjA2LjAwMFoiLCJhdmF0YXJfdXJsIjpudWxsfQ.umxBRd2sazaADSDOW0e8rO5mKDpQYIK1hsaQMZriZFE
application/json
The above API gives me the data only if the correct token is passed and seems working fine.
However in client side I can get the token retrieve using jwt-decode, without the use of any secret in client side, what if the token is caught by middle man, How can the security be enhanced?
Is there something I am missing to have correct use of JWT for my node api routes?
Some places I see the Authorisation is passed as bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiYXR1bCIsImVtYWlsIjoidHJlZUB0cmVlLmNvbSIsInBob25lIjpudWxsLCJwYXNzd29yZCI6IiQyYSQxMCRIQVJPTy5PUEdYWFBvVktXOVhmYnZldk
When I try to use bearer I get error to get the info after authenticating.
What is this bearer and JWT being passed in value to header?
I am using passport-jwt
var JwtStrategy = require('passport-jwt').Strategy;
To use JWT tokens, you have to use SSL (https). Without it, you won't have protection at all.
JWT tokens are signed (check the site). So if someone (middle man) try to change it, it will be invalidated.
JWT and Bearer are basic the same thing. They are just the auth scheme for the authorization header.
The 'JWT' auth scheme is the default of the passport-jwt.
If you want to change it, you can use a different jwtFromRequest value.
See:
new Strategy({ ... jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('Bearer') ... }, verifyFunction)
Hope its clear.

Express Middleware jsonwebtoken authentication

My server has a registration api that provides a token after registration, and a middleware that authenticates a user's token. I need to register an account to get the token to do something else with my server. However, the middleware blocks my network request because I don't have a token yet.
So how can I create my account and token in this case? Get pass the middleware with some tricks?
Middleware:
// Middleware to verify token, it will be called everytime a request is sent to API
api.use((req, res, next)=> {
var token = req.headers.token
if (token) {
jwt.verify(token, secret, (err, decoded)=> {
if (err) {
res.status(403).send({ success: false, message: "Failed to authenticate user." })
} else {
req.decoded = decoded
next()
}
})
} else {
res.status(403).send({ success: false, message: "No Token Provided." })
}
})
Signin:
// Sign In with email API
api.post('/signInWithEmail', (req, res)=> {
User.findOne({
email: req.body.email
}).select(userFields).exec((err, user)=> {
if(err) {
throw err
}
if (!user) {
res.send({ message: "User doesn't exist"});
} else if (user) {
var validPassword = user.comparePassword(req.body.password);
if (!validPassword) {
res.send({ message: "Invalid Password"});
} else {
var token = createToken(user);
res.json({
success: true,
message: "Login Successfully!",
token: token
})
}
}
})
})
Make a function to check tokens and expose your routes such that whenever you need to call an authenticated route then you'll be checking the token first and then you'll expose the route.
Sample Code
Let's say this is my check token function
function checkToken(req, res, next) {
var x = req.token; //This is just an example, please send token via header
if (x === token)
{
next();
}
else
{
res.redirect(/unauthorized); //here do whatever you want to do
}
}
Now let's use the function for routes.
app.post('/protectedroute', checkToken, routename.functionname);
app.post('/notprotected', routename.functionname);
It's your call if you'd like to have separate routes for different codes or else you can just call specific code block via keeping them in function etc. on the main file i.e. app.js or server.js, whatever you have chosen.
What actually we are doing here is - we are making a middleware of our own to expose our routes through a channel of code blocks or functions.

Save and get express.js token from local storage

I am using Node with Express.js and trying to make an authentication with JWT, after the user logs in generate a token, and save it to localstorage, but then I don't know how to get the token for authentication.
This is the code I am using:
Login view:
$.ajax({
type: 'POST',
url: base_url + "login",
data: postData,
dataType: 'json',
success: function(data){
// console.log(data1);
// alert(data);
_(data);
if(data.success === false){
showError(data.msg);
}else{
showError(data.msg);
window.localStorage.setItem("authToken", data.token);
var token = window.localStorage.getItem('authToken');
if (token) {
$.ajaxSetup({
headers: {
'x-access-token': token
}
});
}
}
}
});
And this is the route authentication I am using to check before any of the routes is accessed:
router.use(function(req, res, next){
var token = req.headers['x-access-token'];
console.log(token);
if (token) {
// verifies secret and checks exp
jwt.verify(token, app.get('superSecret'), function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
}else{
// if everything is good, save to request for use in other routes
req.decoded = decoded;
next();
}
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
In console.log(token) I get an Undefined variable , it seems like I don't know how to access the token from route.
Thanks a lot.
"x-access-token" must be registered as an allowed header
response.setHeader("Access-Control-Allow-Headers", "x-access-token, mytoken");
Check this post :
How do CORS and Access-Control-Allow-Headers work?

Resources