Save and get express.js token from local storage - node.js

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?

Related

JWT Token Verification to decode and Protect Routes in MEAN App

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

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 exclude a router/api url from jsonwebtoken

I am using node.js express app. jsonwebtoken for authentication. I want to exlude some api url from the jsonwebtoken verification. below is what I have tried and my code
router.use('/authentication', mountAllRoutes(authenticationModule));
// route middleware to verify a token
router.use((req, res, next) => {
const r = req;
const token = req.body.token || req.query.token || req.headers.authorization;
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, (req.app.get('superSecret')), (err, decoded) => {
if (err) {
// res.json({ success: false, message: 'Failed to authenticate token.' });
res.status(401).send({
success: false,
message: 'Failed to authenticate token.'
});
} else {
// if everything is good, save to request for use in other routes
r.decoded = decoded;
next();
// console.log(decoded);
}
return {};
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
return {};
});
router.use('/test', mountAllRoutes(testModule));
router.use('/other', mountAllRoutes(otherModule));
router.use('/data', mountAllRoutes(dataModule));
Here I have placed routes above middleware which I dont want to protect. and I have placed after middleware which I want to protect. But it is protected which I placed above middleware. In authenticationModule, login and user registration api comes. so for user registration it gives response no token provided
Note: I have refrerred this link How-to-ignore-some-request-type-in-Jsonwebtoken
create separate route file for the API you want to exclued.
//Routes
var users = require('./routes/users');
var api = require('./routes/publicApi');
App.js:
// route middleware to verify a token
router.use((req, res, next) => {
const r = req;
const token = req.body.token || req.query.token || req.headers.authorization;
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, (req.app.get('superSecret')), (err, decoded) => {
if (err) {
// res.json({ success: false, message: 'Failed to authenticate token.' });
res.status(401).send({
success: false,
message: 'Failed to authenticate token.'
});
} else {
// if everything is good, save to request for use in other routes
r.decoded = decoded;
next();
// console.log(decoded);
}
return {};
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
return {};
});
app.use('/users', router);//will use Token Authentican
app.use('/publicApi', router);//Dont do this.

checking Jwt token on every request?

I am developing a android aplication with nodejs and postgreSQL, at the moment i just have the login and the register.
When i do a login and everything is fine the server send me a token, that token is stored on the device SharedPreference, now my confusion is, do i need to decode this token on every request, or do i need to do it just 1 time?
in this tutorial at the end, he decodes on every route the token, but i don't need to do that when i do for example a request to register.
What is the best way to implement this?
here is my server code:
//****************************************************Begin of login request **********************************/
router.post('/login', function (req, res, next) {
if (JSON.stringify(req.body) == "{}") {
return res.status(400).json({ Error: "Login request body is empty" });
}
if (!req.body.username || !req.body.password) {
return res.status(400).json({ Error: "Missing fields for login" });
}
// search a user to login
User.findOne({ where: { username: req.body.username } }) // searching a user with the same username and password sended in req.body
.then(function (user) {
if (user && user.validPassword(req.body.password)) {
//return res.status(200).json({ message: "loged in!" }); // username and password match
var payload = { user: user };
// create a token
var token = jwt.sign(payload, 'superSecret', {
expiresIn: 60 * 60 * 24
});
// return the information including token as JSON
res.json({
success: true,
message: 'Enjoy your token!',
token: token
});
}
else {
return res.status(401).json({ message: "Unauthorized" }); // if there is no user with specific fields send
}
}).catch(function (err) {
console.error(err.stack)
return res.status(500).json({ message: "server issues when trying to login!" }); // server problems
});
});
//****************************************************End of Login request **********************************/
//****************************************************Begin of register request******************************/
router.post('/register', function (req, res, next) {
if (JSON.stringify(req.body) == "{}") {
return res.status(400).json({ Error: "Register request body is empty" });
}
if (!req.body.email || !req.body.username || !req.body.password) {
return res.status(400).json({ Error: "Missing fields for registration" });
}
var password = User.generateHash(req.body.password);
User.create({
username: req.body.username,
email: req.body.email,
password: password
}).then(function () {
return res.status(200).json({ message: "user created" });
}).catch(function (err) {
return res.status(400).send({ message: err.message }); //
}).catch(function (err) {
return res.status(400).json({ message: "issues trying to connect to database" });
})
});
//****************************************************End of register request **********************************/
module.exports = router;
If you don't want to use JWT token check for all routes, you can skip those routes.
const url = require('url');
apiRoutes.use((req, res, next) => {
const path = url.parse(req.url).pathname;
console.log(path);
//No JWT token check
if (/^\/register/.test(path)) {
return next();
}
return jwtTokenValidate();
});
function jwtTokenValidate() {
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['x-access-token'];
// decode 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.'
});
}
}

node.js API authentication

I am working on an iOS application which communicates with a nodejs backend server REST API and I am thinking about API authentication.
I want that only the iOS application can communicate with the API.
On iOS application side, users are authenticated through Facebook login. They thus get a fb access_token and a fbid after authentication on the iOS app.
For the API authentication, I plan to make it this way:
When the user logs in into the iOS app, a call to /api/auth with his fb access_token and fbid is done;
If the user is new, I create a random api_token for this user, store it into Users DB, and send it back to the iOS app;
If the user is already in the DB, I refresh the fb access_token in my DB and the api_token and send it back to the iOS app;
For each API call, I give the api_token as a POST parameter and on server side I check if it is valid by fetching in the DB before executing the API call.
Am I missing something to be enough secured?
Any feedback or improvement will be very welcome.
Regards,
EDIT:
Another way would be the following:
On /api/auth I checked on FacebookB API (/me) if the fb access_token is still valid;
If not I refuse the authentication;
If yes I create and manage my api_token with JSON Web Tokens.
If anyone is interested, I finally implemented the second solution. Very simple and do the job!
config.js
module.exports = {
'secret': 'apisupersecrethere',
};
route.js
var config = require('./config');
app.set('api_secret', config.secret);
api = express.Router();
// function that checks the api_token
api.use(function(req, res, next) {
var token = req.headers['x-access-token'];
if (token) {
jwt.verify(token,app.get('api_secret'),function(err, decoded) {
if (err) {
return res.json({
success: false,
message: 'Failed to authenticate token.'
});
} else {
req.decoded = decoded;
next();
}
});
} else {
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
// route protected by the authentication
router.use('/users', api);
// authentication route
router.post('/auth', function(req, res) {
verifyFacebookUserAccessToken(req.body.access_token).
then(function(user) {
var token = jwt.sign(user, app.get('api_secret'), {
expiresIn: 1440*60 // expires in 24 hours
});
res.status(200).json({
success: true,
message: "Authentication success!",
token: token
});
}, function(error) {
res.status(401).json({
success: false,
message: error.message
});
}).
catch(function(error){
res.status(500).json({
success: false,
message: error.message
});
});
});
// Call facebook API to verify the token is valid
function verifyFacebookUserAccessToken(token) {
var deferred = Q.defer();
var path = 'https://graph.facebook.com/me?access_token=' + token;
request(path, function (error, response, body) {
var data = JSON.parse(body);
if (!error && response && response.statusCode && response.statusCode == 200) {
var user = {
facebookUserId: data.id,
username: data.username,
firstName: data.first_name,
lastName: data.last_name,
email: data.email
};
deferred.resolve(user);
}
else {
deferred.reject({
code: response.statusCode,
message: data.error.message
});
}
});
return deferred.promise;
}
Any feedback welcomed.
Regards

Resources