This is my Library function file named verifyToken.js
require('dotenv').config();
const CognitoExpress = require("cognito-express");
//Initializing CognitoExpress constructor
const cognitoExpress = new CognitoExpress({
cognitoUserPoolId: process.env.USER_POOL_ID, // User Pool id (created via Console->Cognito Manage User Pool)
region: process.env.REGION, //User Pool Region
tokenUse: "id", //Possible Values: access | id
tokenExpiration: 3600000 //Up to default expiration of 1 hour (3600000 ms)
});
//Our middleware that authenticates all APIs under our 'authenticatedRoute' Router
module.exports = function(){
this.verifyToken=(function(token, req, res) {
//I'm passing in the id token in header under key Token
let TokenFromClient = token;
//Fail if token not present in header.
if (!TokenFromClient) return "Token missing from header";
cognitoExpress.validate(TokenFromClient, function(err, response) {
//If API is not authenticated, Return 401 with error message.
if (err) {
return res.send({success:false, msg:"Token Authenication Error", Error:err});
}
//Else API has been authenticated. Proceed.
return res.send({success:true, msg:"Token Authenication result", result: response});
});
});
}
I am calling this function from controller
var verify_tokenController = {};
//Verify AWS token recieved through client header by calling verifyToken Library function
verify_tokenController.verify = function(req, res) {
//Import verifyToken Library function
require('../library/verifyToken')();
var auth = verifyToken(req.header('Token'), req, res);
console.log(auth);
};
module.exports = verify_tokenController;
But I am always receiving undefined at console. How to return err or response back to the calling controller. Both of the return variable are JSON object type.
It is becuase cognitoExpress.validate is an async operation. it will run and the callback of this function will run. But by the time this all happens. Your following line
console.log(auth); gets hit already and you see undefined becuase you are actually not returning anything from verifyToken method at that time.
This how javascript and async operations work.
You can either use the callback approach but that's not what I would recommend. So I'm modifying your code with promises and see if it works for you.
Your verifyToken.js should look as follow:
require('dotenv').config();
const CognitoExpress = require("cognito-express");
//Initializing CognitoExpress constructor
const cognitoExpress = new CognitoExpress({
cognitoUserPoolId: process.env.USER_POOL_ID, // User Pool id (created via Console->Cognito Manage User Pool)
region: process.env.REGION, //User Pool Region
tokenUse: "id", //Possible Values: access | id
tokenExpiration: 3600000 //Up to default expiration of 1 hour (3600000 ms)
});
//Our middleware that authenticates all APIs under our 'authenticatedRoute' Router
module.exports = function(){
this.verifyToken=(function(token, req, res) {
return new Promise(function(resolve, reject) {
//I'm passing in the id token in header under key Token
let TokenFromClient = token;
//Fail if token not present in header.
if (!TokenFromClient) return "Token missing from header";
cognitoExpress.validate(TokenFromClient, function(err, response) {
//If API is not authenticated, Return 401 with error message.
if (err) {
reject(err);
} else {
//Else API has been authenticated. Proceed.
resolve(response);
}
});
});
});
}
Your controller should look like as follow:
var verify_tokenController = {};
//Verify AWS token recieved through client header by calling verifyToken Library function
verify_tokenController.verify = function(req, res) {
//Import verifyToken Library function
require('../library/verifyToken')();
verifyToken(req.header('Token'), req, res)
.then(function(response){
console.log(resposne, 'response')
res.send({success:true, msg:"Token Authenication result", result: response});
})
.catch(function(err) {
res.send({success:false, msg:"Token Authenication Error", Error:err});
});
};
module.exports = verify_tokenController;
Try the above solution.
Related
I am creating a MERN application and I have a problem, in the back end I have the protected routes through a function called verifytoken that looks for the token as a header, on the other hand, in the front I receive a token that I keep in the local storage, how can I send the local storage as a header when calling that API?
I attach my code
frontend function that receives the token and stores it in localstorage
`import axios from "axios";
const jwtString = 'jwtlibraryyt'
export const loginUser = async (userObj) => {
const response = await axios.post(`http://localhost:3001/login`, userObj);
const { user, token} = response.data;
const {_id, ...userStored} = user;
localStorage.setItem(jwtString, token );
return response;
}`
frontend function that tries to call the protected route and send the local storage as a header
`const Books=()=> {
const [books, setBooks] = useState([]);
useEffect(() => {
getBooks();
},[])
async function getBooks(){
if(!localStorage.getItem("jwtlibraryyt")){
}
else{
const response = await axios.get('http://localhost:3001/books',{ headers: { token:localStorage.getItem('jwtlibraryyt') }});
setBooks(response.data);
}
}`
protected backend path
`router.get('/books', UserController.verifyToken,BookController.allBooks)`
verifytoken function found in the backend
`UserMethods.verifyToken = async (req, res, next) => {
let token = req.headers["token"];
if (!token) return res.status(403).json({ message: "Dame un token" });
try {
const decoded = jwt.verify(token, SECRET='elbicho');
req.currentUserId = decoded._id;
const user = await Users.findById(req.currentUserId, { password: 0 });
if (!user) return res.status(404).json({ message: "No se encontro el usuario" });
next();
} catch (error) {
return res.status(401).json({ message: "Usted no tiene autorizacion" });
}
};`
I am working on an Express App with MongoDB and trying to utilize FeathersJS for all my services. Here I'm running a test try to get an error message from the server to the client, but I have an issue with the response from the error handler. My req headers have the correct application/json stuff, so I assumed the Error Handler should send valid json back.
I know I'm not using the next callback in my function, but when I try to do that it gives the same error, so I'm thinking it has to do with the Error Handler. Any direction here would be greatly appreciated!
The first error log is on the server, which is correct.
Bucket Services
error >>>>> Bucket validation failed
Possibly Unhandled Rejection: Bucket validation failed, Promise { <rejected> 'Bucket validation failed' }
>>>>>> Error: Unexpected token < in JSON at position 0
at convert (/Users/jaruesink/Documents/Projects/Buckets/node_modules/feathers-rest/node_modules/feathers-errors/lib/index.js:365:79)
at toError (/Users/jaruesink/Documents/Projects/Buckets/node_modules/feathers-rest/lib/client/base.js:24:37)
at process._tickCallback (internal/process/next_tick.js:103:7)
my create function within the BucketService class:
create({
amount,
isFund = false,
name,
type,
userID: owner
}, params, next) {
const new_bucket = new Bucket({ name, amount, type, isFund, owner });
return new_bucket.save((error) => {
console.log('error >>>>>', error.message);
if (error) { return Promise.reject(error.message); }
return Promise.resolve(new_bucket);
});
}
my router file:
const feathers = require('feathers');
const errorHandler = require('feathers-errors/handler');
const rest = require('feathers-rest');
const router = feathers();
const LoginService = require('../services/login_service');
const UserService = require('../services/user_service');
const BucketService = require('../services/bucket_service');
// Enable REST services
router.configure(rest());
router.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
router.use('/login', new LoginService());
router.use('/user', new UserService());
router.use('/bucket', new BucketService());
// Set up error handling
router.use(errorHandler());
module.exports = router;
I figured it out, the key was to correctly pass through a callback (next) function as the third parameter to handle errors. FeathersJS handles the Promise Rejections for you on errors. Then in my test I needed to convert the Feathers-Error to JSON before I could get the message.
I changed my test to:
it('can validate an incorrect bucket', (done) => {
const invalid_bucket = {
name: 'Invalid Bucket',
};
bucket_service.create(invalid_bucket, {}, (error) => {
error = error.toJSON();
assert(error.message.length > 0);
done();
});
});
and my create function to:
create({
amount,
isFund = false,
name,
type,
userID: owner
}, params, next) {
const new_bucket = new Bucket({ name, amount, type, isFund, owner });
return new_bucket.save()
.then(created_bucket => Promise.resolve(created_bucket))
.catch(next);
}
Up, running and ready for action!
When GET method is used, the below output comes but never completes loading... from POSTMAN Why?
Successfully connected to MongoDB instance!
MongoDB returned the following documents:
[ { _id: ObjectID { _bsontype: 'ObjectID', id: [Object] },
name: 'Apple',
price: 2.5 },
{ _id: ObjectID { _bsontype: 'ObjectID', id: [Object] },
name: 'Pear',
price: 3 },
{ _id: ObjectID { _bsontype: 'ObjectID', id: [Object] },
name: 'Orange',
price: 3 } ]
When POST method is used, the below error occurred. Why?
/Users/json/Dev/restful_api/api.js:21
database.insert('OrderBase', resourceName, resource, function(err, resource) {
^
TypeError: database.insert is not a function
at insertResource (/Users/json/Dev/restful_api/api.js:21:12)
at insertProduct (/Users/json/Dev/restful_api/api.js:34:3)
at IncomingMessage.<anonymous> (/Users/json/Dev/restful_api/api.js:66:9)
at emitNone (events.js:86:13)
at IncomingMessage.emit (events.js:185:7)
at endReadableNT (_stream_readable.js:974:12)
at _combinedTickCallback (internal/process/next_tick.js:74:11)
at process._tickCallback (internal/process/next_tick.js:98:9)
Can anyone explain? I am new to NodeJs. Thanks a lot!
var http = require('http');
var database = require('./database');
var url = require('url');
// Generic find methods (GET)
function findAllResources(resourceName, req, res) {
database.find('OrderBase', resourceName, {}, function (err, resources) {
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify(resources));
});
};
var findResourceById = function (resourceName, id, req, res) {
database.find('OrderBase', resourceName, {'_id': id}, function(err, resource) {
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify(resource));
});
};
// Generic insert/update methods (POST, PUT)
var insertResource = function (resourceName, resource, req, res) {
database.insert('OrderBase', resourceName, resource, function(err, resource) {
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify(resource));
});
};
// Product methods
var findAllProducts = function (req, res) {
findAllResources('Products', req, res);
};
var findProductById = function (id, req, res) {
findResourceById('Products', id, req, res);
};
var insertProduct = function (product, req, res) {
insertResource('OrderBase', 'Product', product, function (err, result) {
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify(result));
});
};
var server = http.createServer(function (req, res) {
// Break down the incoming URL into its components
var parsedURL = url.parse(req.url, true);
// Determine a response based on the URL
switch (parsedURL.pathname) {
case '/api/products':
if (req.method === 'GET') {
// Find and return the product with the given id
if (parsedURL.query.id) {
findProductById(id, req, res);
}
// There is no id specified, return all products
else {
findAllProducts(req, res);
}
}
else if (req.method === 'POST') {
//Extract the data stored in the POST body
var body = '';
req.on('data', function (dataChunk) {
body += dataChunk;
});
req.on('end', function () {
// Done pulling data from the POST body.
// Turn it into JSON and proceed to store it in the database.
var postJSON = JSON.parse(body);
insertProduct(postJSON, req, res);
});
}
break;
default:
res.end('You shall not pass!');
}
});
server.listen(8080);
console.log('Up, running and ready for action!');
database file
// Our primary interface for the MongoDB instance
var MongoClient = require('mongodb').MongoClient;
// Used in order to verify correct return values
var assert = require('assert');
/**
*
* #param databaseName - name of the database we are connecting to
* #param callBack - callback to execute when connection finishes
*/
var connect = function (databaseName, callback) {
// URL to the MongoDB instance we are connecting to
var url = 'mongodb://localhost:27017/' + databaseName;
// Connect to our MongoDB instance, retrieve the selected
// database, and execute a callback on it.
MongoClient.connect(url, function (error, database) {
// Make sure that no error was thrown
assert.equal(null, error);
console.log("Successfully connected to MongoDB instance!");
callback(database);
});
};
/**
* Executes the find() method of the target collection in the
* target database, optionally with a query.
* #param databaseName - name of the database
* #param collectionName - name of the collection
* #param query - optional query parameters for find()
*/
exports.find = function (databaseName, collectionName, query) {
connect(databaseName, function (database) {
// The collection we want to find documents from
var collection = database.collection(collectionName);
// Search the given collection in the given database for
// all documents which match the criteria, convert them to
// an array, and finally execute a callback on them.
collection.find(query).toArray(
// Callback method
function (err, documents) {
// Make sure nothing went wrong
assert.equal(err, null);
// Print all the documents that we found, if any
console.log("MongoDB returned the following documents:");
console.dir(documents);
// Close the database connection to free resources
database.close();
})
})
};
The situation that I am specifically referring to involves the use of json web tokens (jwt). For example how would I automatically add middleware to only return a new json web token if that web token is expired without duplicating code? The sample code below will show what I mean.
var express = require("express");
var jwt = require('jsonwebtoken');
var router = express.Router();
router.use(function(req,res,next){
var token = req.body.token || req.query.token;
if (token) {
jwt.verify(token,"secretTingz",{algorithms:["RS256"]},function(err,decoded){
if (err) {
if (token.expired) {
// HOW DO I GET THIS PIECE OF CODE TO RUN FOR EVERY VALID ROUTE THAT NEEDS A NEW TOKEN WITHOUT DUPLICATING CODE?
var token = jwt.sign({user:"MilnerJenkins"},cert,{algorithm:"RS256",expiresInMinutes:1});
req.token = token;
next();
}else{
return res.json({message:"Failed to authenticate token"});
}
}else{
req.decoded = decoded;
next();
}
})
}else{
return res.status(403).send({
message: "No token!"
});
}
});
apiRoutes.get("/stuff",function(req,res){
var token;
if (req.token) {
token = req.token;
};
res.json({message: "Dope API son!",token:token});
});
apiRoutes.get("/users",function(req,res){
var token;
if (req.token) {
token = req.token;
};
User.find({},function(err,users){
res.json({users: users, token: token});
});
});
As you can see code is being duplicated in both routes with this block:
var token;
if (req.token) {
token = req.token;
};
What can I do with express middleware to prevent this duplication?
The only way you could do this with a "middleware" is by having a handler at the end of your middlware chain that would send the response. That would mean all your handlers would have to pass the request and response down the chain. It would essentially be the last handler before your 404 handler. I do not recommend attempting that. Alternatively you can create a custom response method for your express app.
var app = require('express')();
app.response.myJsonRes = function(obj) {
if (this.req.token) {
obj.token = this.req.token;
}
this.json(obj);
};
app.get('/myRoute', function(req, res) {
// get data somehow
res.myJsonRes(data);
});
You coulld even change the standard json method.
var prevJson = app.response.json;
app.response.json = function(obj) {
if (this.req.token) {
obj.token = this.req.token;
}
prevJson.call(this, obj);
};
I am trying to set up an authentication via token for my web app. I am using nodejs for the back end and the jwt-simple module to encode/decode the tokens.
I manage to create and handle the tokens from the server to the client. However I struggle to handle the token from the client to the server.
For example, I have a page called profile for which I handle the requests the following way:
app.get('/profile', [bodyParser(), jwtauth], function(req, res) {
res.render('profile.ejs', {
user : req.user // get the user out of session and pass to template
});
});
Where jwtauth is the following:
var jwt = require('jwt-simple');
var User = require('../server/models/user');
module.exports = function(req, res, next) {
var token = (req.user && req.user.access_token) || (req.body && req.body.access_token) || (req.query && req.query.access_token) || req.headers['x-access-token'];
if (!token)
return next('invalid or no token');
try {
var decoded = jwt.decode(token, app.get('jwtTokenSecret'));
if (decoded.exp <= Date.now())
return res.end('Access token has expired', 400);
User.findOne({ _id: decoded.id }, function(err, user) {
req.user = user;
});
return next();
} catch (err) {
return next('couldn\'t decode token');
}
};
On the client side I attached the token once the user is logged in the following way:
$(document).ready(function() {
var token = __token;
if (token) {
$.ajaxSetup({
headers: {
'x-access-token': token
}
});
}
});
But if I try to get the url '/profile' in my browser there is no 'x-access-token' in the headers and I get the 'invalid or no token' message.
How can I set the token on the client side so that it is attached to every request to my server?
Many thanks
What are you seeing when you console.log(req.headers) in your jwtauth middleware? Are you deifning $.ajaxSetup more than once? See this post