I have a aplication where the user can take some pictures and send to the database, just as simple as that.
Everytime the user login he get a token, if everything fine with the token(he doesn't need to login).
I followed this tutorial to do the jwt authentication, now i want to check on every request except(/login / register) that token and decode it to get the user info ( i am just saving the username, its unique so its fine).
So imagine i am routing to /flower?flowerName (random route) so in this route i want to create a register and save in my database some data, but before that as i said, i should enter a middleware that checks the permission.
This is my middleware:
var jwt = require('jsonwebtoken');
var jwtConfig = require('../config/jwt');
module.exports = function(req, res, next) {
console.log("entered");
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['x-access-token'];
console.log(req.headers['x-access-token']);
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token,jwtConfig.secret, function (err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
console.log("HEREE");
// if everything is good, save to request for use in other routes
req.decoded = decoded;
console.log(req.decoded);
next();
}
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
}
my problem is, how can i get the userID for my middleware and then save it in my next route? can i pass it trough the next? like next(userID)????
How can i get the parameter then.
this is where i save the register:
var express = require('express');
var User = require('../models').User;
var Foto = require('../models').Foto;
var router = express.Router();
var jwt = require('jsonwebtoken');
var fs = require('fs');
var fsPath = require('fs-path');
module.exports = {
sendPicture: function (req, res,next) {
var bitmap = new Buffer(req.body.base64, 'base64');
var dummyDate = "25/04/14-15:54:23";
var lat = req.params.lat;
var lon = req.params.lon;
var alt = req.params.alt;
var path = __dirname + "/../public/images/" + req.params.flowerName + "/example3.png";
var fotoPath = ""
var userId = 1;
console.log(lat);
console.log(lon);
console.log(alt);
console.log(req.query.token);
fsPath.writeFile(path, bitmap, function (err) {
if (err) {
console.log(err.stack);
return err;
}
Foto.create({
image: path,
userId: userId
}).then(function () {
return res.status(200).json({ message: "foto created" });
}).catch(function(err){
console.log(err.stack);
})
});
}
}
You should be able to pass any state variables through the whole chain through res.locals, i.e.
res.locals.decoded = decoded;
next();
you can find the details on res.locals here
Related
Hi I have been struggling significantly with making a login using react front end, npm express, npm mssql and npm jsonwebtokens.
I understand the logic behind this but cannot implement properly into my application. I have included the code in which I am getting stuck on and which is causing the issues.
This is my login route within my server.js (main npm packages used are mssql, express, jsonwebtokens,bodyparser)
This route is used when the logging form is submit (front end not important within this question).
This then checks if the database contains that email and password. If it does it prompts user "login successful" if not it prompts user "bad creds". If login is successful it assign a jwt token to it.
app.post("/login", async (req, response) => {
try {
await sql.connect(config);
var request = new sql.Request();
var Email = req.body.email;
var Password = req.body.password;
console.log({ Email, Password });
request.input("Email", sql.VarChar, Email);
request.input("Password", sql.VarChar, Password);
var queryString =
"SELECT * FROM TestLogin WHERE email = #Email AND password = #Password";
//"SELECT * FROM RegisteredUsers WHERE email = #Email AND Password = HASHBYTES('SHA2_512', #Password + 'skrrt')";
const result = await request.query(queryString);
if (result.recordsets[0].length > 0) {
console.info("/login: login successful..");
console.log(req.body);
jwt.sign({ Email }, "secretkey", { expiresIn: "30m" }, (err, token) =>
res.cookie("auth", token).json({ token })
);
} else {
console.info("/login: bad creds");
response.status(400).send("Incorrect email and/or Password!");
}
} catch (err) {
console.log("Err: ", err);
response.status(500).send("Check api console.log for the error");
}
});
// Verify Token
function verifyToken(req, res, next) {
// Get auth header value
const bearerHeader = req.headers["authorization"];
// Check if bearer is undefined
if (typeof bearerHeader !== "undefined") {
// Split at the space
const bearer = bearerHeader.split(" ");
// Get token from array
const bearerToken = bearer[1];
// verify the token and store it
jwt.verify(bearerToken, "secret", function(err, decodedToken) {
if (err) {
console.info("token did not work");
return res.status(403).send("Error");
}
// Set the token
req.token = bearerToken;
req.decodedToken = decodedToken;
next();
});
} else {
// Forbidden
res.sendStatus(403);
}
}
I then have a token verify method.
This is supposed to check the token and then if it is legit will store it. However when using route user-questions(will show in next block of code) it always throws the 403 error.
// Verify Token
function verifyToken(req, res, next) {
// Get auth header value
const bearerHeader = req.headers["authorization"];
// Check if bearer is undefined
if (typeof bearerHeader !== "undefined") {
// Split at the space
const bearer = bearerHeader.split(" ");
// Get token from array
const bearerToken = bearer[1];
// verify the token and store it
jwt.verify(bearerToken, "secret", function(err, decodedToken) {
if (err) {
console.info("token did not work");
return res.status(403).send("Error");
}
// Set the token
req.token = bearerToken;
req.decodedToken = decodedToken;
next();
});
} else {
// Forbidden
res.sendStatus(403);
}
}
This is the user question route in which on the first line I am trying to call the verify Token method when this fetch is called however it always throws the 403 error.
app.get("/user-questions", verifyToken, function(req, res) {
// if a request has made it to this point, then we know they have a valid token
// and that token is available through either req.token (encoded)
// or req.decodedToken
sql.connect(config, function(err) {
if (err) console.log(err);
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.execute("dbo.ViewQuestions", function(err, recordset) {
if (err) console.log(err);
// send records as a response
res.json(recordset);
});
});
});
I am trying to build an authentication system so, i used node , mysql,express for that so now i am simply saving and checking user exist in database can access but now i added JWT to it, so now i want this JWT token to store in localstorage or in cookies so, can someone guide me how can i do so
this is my authentication controller.js
var Cryptr = require('cryptr');
cryptr = new Cryptr('myTotalySecretKey');
var express = require('express');
const ap = express();
var jwt = require('jsonwebtoken');
var connection = require('./../config');
module.exports.authenticate = function (req, res) {
var email = req.body.email;
var password = req.body.password;
connection.query('SELECT * FROM users WHERE email = ?', [email], function (error, results, fields) {
if (error) {
res.json({
status: false,
message: 'there are some error with query'
});
} else {
if (results.length > 0) {
decryptedString = cryptr.decrypt(results[0].password);
if (password == decryptedString) {
jwt.sign({ email, password },
'secretkey',
{ expiresIn: '10days' },
(err, token) => {
console.log('token:' + token);
module.exports = token;
console.log(token);
res.redirect('/home.html');
}
);
} else {
res.redirect('/login.html');
console.log("Wrong Input");
}
}
else {
res.redirect('/login.html');
}
}
});
};
now i want to pass the token value to the local-storage or cookies so that i can restrict someone from acessing a page, i am reallly new to node js so any help would be appriciated
First I should notify you that do not put any secret things like password in jwt payload because the values of the payload could be accessed easily, you can try to copy paste a jwt in jwt.io site and see the payload.
set jwt in cookie like below, this will use express cookie method that does set Http Set-Cookie header:
res.cookie('jwt', generated_cookie)
.redirect('/home.html');
Also if you want to use localStorage you can set jwt in header and then in your code get the jwt from the header of login request and save it in localStorage and after that you should pass it as header in all other request, but this approach is a better solution for api calls like when you use react or vue ...
res.set({x-token: generated_token});
// In your code get
// get token from response
localStorage.setItem('token', token);
// now whenever calling api pass token as header
I show you one solution using jwt token, you choose another way:
Back-end file e.g. api.js
let jwt = require('jsonwebtoken')
let secret = 'yourSecret'; //secret key necessary to encode token
let Cryptr = require('cryptr');
let cryptr = new Cryptr('myTotalySecretKey');
module.exports = function(router,upload) {
function tokenAuth(req, res, next){
let token = req.body.token || req.body.query || req.headers['x-access-token']
if(token){
jwt.verify(token, secret, function(err,decoded){
if(err){
res.json({ authenticated: false, message:'Invalid token'})
} else {
req.decoded = decoded;
next()
}
})
} else {
res.json({success:false, message:'No token provided'});
}
}
router.post('/authenticate', function(req, res){
connection.query('SELECT * FROM users WHERE email = ?', [email], function (error, results, fields){
if(error) {
res.json({ success:false, message: err })
}
if(!results.length){
res.json({success:false, message:'User no found'})
} else if (results.length>0){
if(!req.body.password){
res.json({success:false, message:'Password was not provided'});
} else {
var validPassword = cryptr.decrypt(results[0].password);
if(validPassword === req.body.password){
res.json({success:false, message:'Incorrect password'})
} else {
var token = jwt.sign({username: results[0].username, email: results[0].email}, secret, {expiresIn: '24h'})
res.json({success:true, message:'You have logged in correctly!', token: token })
}
}
}
})
})
//If you want create a route for authenticated users for example comment posts, you can use our `tokenAuth function`
router.post('/post/comment',tokenAuth,function(req,res){
//access only for authenticated users
}
return router
}
This tokenAuth function we'll be use in paths restricted to authenticated users
server file e.g. server.js
const express = require('express');
const app = express();
const port = process.env.PORT || 80;
const http = require('http').Server(app);
const routes = require(path_to_api.js)(router);
app.use('/myApi', routes)
//***Here you should implement more details about your project such as routes, body parsers and other middlewares*****//
//Connect to your database
http.listen(port, ()=> console.log(`Server running on ${port}`))
Front-end file e.g. controller.js
function(login){
return fetch('/myApi/authenticate',{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(login)
}).then(result=>result.json()).then(data=> window.localStorage.setItem('token', data.token))
}
//`login` argument should be an object and should be like {username: 'user username', password: 'user password'}
In order to make a user store cookies, you can use the Set-Cookie header. From MDN:
Set-Cookie: <cookie-name>=<cookie-value>
In order to pass a header using Express, you can use res.set(), e.g. res.set("Set-Cookie", "Token=" + token). I also suggest you use the HttpOnly cookie directive, since it seems from your post that you don't access this token directly via Javascript and you simply want to check it when the client requests a webpage: res.set("Set-Cookie", "Token=" + token + "; HttpOnly").
The client will send the Cookie header to you when it requests a resource. You can check this header using req.header('Cookie'), and the output will be "Token=<token>" if the user is authenticated. You can then check this token for authenticity.
i tried everything to store the token. nothing works.
things i tried: Cookies, headers, localStorage.
basically nothing is defined in authenticate.js so i get a 401.
what am i doing wrong ?
in app.js
{authenticate} = require('./public/middleware/authenticate');
// POST /users/login {email, password}
function userLogin(req, res){
var body = _.pick(req.body, ["email", "password"]);
User.findByCredentials(body.email, body.password).then(function(user){
return user.generateAuthToken().then(function(token){
// res.cookie('authorization', token).send(user);
res.setHeader('set-cookie',token).send(user);
// localStorage.setItem('token',token');
// res.header('x-auth',token).send(user);
console.log('APP.JS', token);
});
}).catch(function(e){
res.status(400).send();
});
}
app.post('/users/login', userLogin);
in Authenticate.js
const {User} = require('.././models/users-Model');
const cookieParser = require('cookie-parser');
const express = require('express');
const requestCookies = require('request-cookies');
var app = express();
app.use(cookieParser());
var authenticate = function(req, res, next){
// var token = req.cookie.authorization;
// var token = req.header('x-auth');
var token = req.header('set-cookie');
// var token = Cookie('auth');
// var token = localStorage.getItem("token");
console.log('authenticate.js',token);
User.findByToken(token).then(function(user){
if(!user){
return Promise.reject();
}
req.user = user;
req.token = token;
next();
}).catch(function(e){
res.status(401).send();
});
}
module.exports = {authenticate};
in app.js
function listPost(req, res){
var Data = new budgetCalculator({
_creator:req.user._id,
_id: req.body._id,
firstItem: req.body.firstItem,
firstPrice: req.body.firstPrice,
secondItem: req.body.secondItem,
secondPrice: req.body.secondPrice,
thirdItem: req.body.thirdItem,
thirdPrice: req.body.thirdPrice,
tBudget: req.body.tBudget
});
Data.save().then(function(Data){
return user.generateAuthToken().then(function(token){
res.header('x-auth',token).send({Data});
});
console.log('token', token);
}).catch(function(e){
res.send(e)
});
}
app.post('/', authenticate, listPost);
in users schema file
// generating the token
// which i use to pass the token
UserSchema.methods.generateAuthToken = function () {
var user = this;
var access = 'auth';
var token = jwt.sign({_id: user._id.toHexString(), access}, 'abc123').toString();
user.tokens.push({access, token});
return user.save().then(function(){
return token;
});
};
its exposed and wired to other files. i think all i need is to pass the token again when the listPost function is fired. so when authenticate.js make a req.header('x-auth) it receives the same token from generateAuthToken().
but i tried it returns undefined.
Problem solved.
turns out i was trying to make a req.cookies.authorization from authenticate.js
var authenticate = function(req, res, next){
var token = req.cookies.authorization;
console.log('authenticate.js',token);
User.findByToken(token).then(function(user){
if(!user){
return Promise.reject();
}
req.user = user;
req.token = token;
next();
}).catch(function(e){
res.status(401).send();
});
}
when i login using userLogin function but when i try to do anything related to the app after logging in i didn't send the same token via res.cookies.authorization
resulting in authenticate.js to recieve nothing. from the cookies. when i wired every function in the app to send the user token everytime it fires, that fixed the problem.
function listPost(req, res){
var Data = new budgetCalculator({
_creator:req.user._id,
_id: req.body._id,
firstItem: req.body.firstItem,
firstPrice: req.body.firstPrice,
secondItem: req.body.secondItem,
secondPrice: req.body.secondPrice,
thirdItem: req.body.thirdItem,
thirdPrice: req.body.thirdPrice,
tBudget: req.body.tBudget
});
Data.save().then(function(Data){
return Data.generateAuthToken();
}).then(function(token){
res.cookie('authorization', token).send({Data}); // this part was missing
console.log('token', token);
}).catch(function(e){
res.send(e)
});
}
app.post('/', authenticate, listPost);
I am struggling with how to get my user decrypted data for the current user, basicly everytime a user does login he get a token at the moment.
After i login i can capture a phote and send it to the server, following my code you guys can see that this request needs a token to work.
Basicly i have a problem related to my app.js(starting file) i try to set a app.use that needs a token for all the routes that comes after the register and login, like this:
app.use('/',require('./routes/index'));
app.use(jwtPermission);
router.use('/fotos',fotos);
my jwt permission file:
var jwt = require('jsonwebtoken');
var jwtConfig = require('../config/jwt');
module.exports = function(req, res, next) {
console.log("entered");
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['x-access-token'];
console.log(req.headers['x-access-token']);
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token,jwtConfig.secret, 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.'
});
}
}
router index(connection to authentication and fotos files)
var express = require('express');
var router = express.Router();
router.use(function timeLog(req, res, next) {
console.log('Time: ', Date());
console.log('Request Type:', req.method);
console.log('Request URL:', req.originalUrl);
next(); //passa a solicitação para a próxima função de middleware na pilha
});
router.use('/',require('./authentication'))
router.use('/fotos',require('./fotos'));
router.use(function(req,res,next){
return res.status(404).json({Error:"Invalid Url"});
})
module.exports = router;
when i point to /fotos it doesn't enter the jwtPermission as i want, what is wrong?
you can add your token validation middleware before function that handles some route. For example:
router.use('/fotos', jwtAuthentication, require('./fotos'));
I'm using nodejs, express 4 and everyauth for social network authentication. I'm having some problem where I click Accept from Google and and redirects back to my / I get
_http_outgoing.js:335
throw new Error('Can\'t set headers after they are sent.');
I'm using electrolyte as a DI as well but I don't think that would be an issue. Here's my code
index.js
var express = require('express'),
http = require('http'),
bootable = require('bootable');
var app = bootable(express());
app.phase(bootable.initializers('etc/init', app));
app.phase(bootable.routes(__dirname + '/routes.js', app));
app.phase(function listen(done) {
http.createServer(app).listen(process.env.PORT || 3000, function(err) {
if (err) { return done(err); }
var addr = this.address();
console.log('server listening on http://' + addr.address + ':' + addr.port);
done();
});
});
module.exports = app;
routes.js
var IoC = require('electrolyte');
module.exports = function routes() {
this.get('/', IoC.create('handlers/homepage'));
this.get('/api/foursquare', IoC.create('handlers/api/foursquare'));
//this.get('/auth/google/callback', IoC.create('handlers/auth/google_auth_callback'));
}
handlers/homepage
exports = module.exports = function() {
function render(req, res, next) {
res.render('index');
};
return [render];
};
google.js
var everyauth = require('everyauth'),
GoogleUser = require('../../app/models/google_user'),
mongoose = require('../../app/db/mongo');
require('dotenv').load();
module.exports = function() {
everyauth.google
.appId(process.env.GOOGLE_CLIENT_ID)
.appSecret(process.env.GOOGLE_CLIENT_SECRET)
.scope('https://www.googleapis.com/auth/plus.login') // What you want access to
.handleAuthCallbackError( function (req, res) {
// If a user denies your app, Google will redirect the user to
// /auth/facebook/callback?error=access_denied
// This configurable route handler defines how you want to respond to
// that.
// If you do not configure this, everyauth renders a default fallback
// view notifying the user that their authentication failed and why.
})
.findOrCreateUser( function (session, accessToken, accessTokenExtra, googleUserMetadata) {
console.log('come back from google');
console.log(session);
// find or create user logic goes here
// Return a user or Promise that promises a user
// Promises are created via
// var promise = this.Promise();
/*
var googleUser = new GoogleUser(mongoose);
var johndoe = new googleUser({
accessToken: 'accessToken',
expires: new Date(),
refreshToken: 'refreshToken',
email: 'john#doe.com',
createdAt: new Date()
});
var promise = this.Promise();
promise.resolve(johndoe);
return promise;
*/
}).redirectPath('/');
};
mongo.js
var mongoose = require('mongoose')
require('dotenv').load();
var uristring = process.env.MONGOLAB_URI;
mongoose.connect(uristring, function (err, res) {
if (err) {
console.log ('ERROR connecting to: ' + uristring + '. ' + err);
} else {
console.log ('Succeeded connected to: ' + uristring);
}
});
exports = module.exports = mongoose
You will get Can't set headers after they are sent. if your code is like this
if (err) {
res.send(err);
}
else if(!user) {
res.send(info);
}
res.send(user);
In the above code, if user is undefined it sends the info as response and it again comes to next statement i.e res.send(user);. So it cannot set headers to the response which is already sent.
You can prevent this by
if (err) {
res.send(err);
}
else if(!user) {
res.send(info);
}
else {
res.send(user);
}
or
if (err) {
return res.send(err);
}
else if(!user) {
return res.send(info);
}
return res.send(user);