I am working with a jwt token to validate user registration. A unique url is sent to user's email and it redirects to the authentication page, the token is decoded on server side and I need to send this json data to angular, on client side. How can I do this using token as query parameter and retrieve it using ngResource?
server.js
'use strict';
var express = require('express');
var app = express();
var router = express.Router();
var bodyParser = require('body-parser');
var nodemailer = require('nodemailer');
var jwt = require('jsonwebtoken');
var moment = require('moment');
var port = process.env.PORT || 5000;
app.use(express.static('./src/client/'));
app.use(express.static('./'));
app.use(express.static('./.tmp'));
app.use('/*', express.static('./src/client/index.html'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// sign with default (HMAC SHA256)
var expires = moment().add(12, 'hours').valueOf();
var token = jwt.sign({
user: 'userdata',
iat: Math.floor(Date.now() / 1000),
expireIn: expires
}, 'thisismysecretstring');
// nodemailer sendMail function
app.post('/sendMail', function(req, res) {
var transporter = nodemailer.createTransport('smtp://b204bf8f6ede15:71b5c1473852e2#mailtrap.io:2525');
var data = req.body;
var mailOptions = {
from: 'noreply#ixfire.com',
to: data.email,
subject: 'Email sent by ' + data.displayName,
html: '<p>Please click on url below to register</p><br>CLICK HERE'
};
transporter.sendMail(mailOptions, function(error, info) {
if (error) {
return console.log(error);
}
console.log('Message sent: ' + info.response);
});
res.json(token);
});
// decode token from url parameter
app.get('/auth', function(req, res) {
var token = req.query.token;
try {
var decoded = jwt.verify(token, 'thisismysecretstring');
if (decoded.exp <= Date.now()) {
res.end('Access token has expired', 400);
}
res.json(decoded);
} catch (err) {
console.log(err);
res.json(err);
}
});
// router.use(function(req, res, next) {
// var token = req.query.token;
// try {
// var decoded = jwt.verify(token, 'thisismysecretstring');
// res.json(decoded);
// } catch (err) {
// console.log(err);
// res.json(err);
// }
// });
// app.use('/auth', router);
app.listen(port, function () {
console.log('Express app listening on port: ' + port);
console.log(__dirname);
});
token.js
(function() {
'use strict';
angular
.module('xfire.token', ['ngResource'])
.factory('Token', function($resource) {
return $resource('auth/:token', {
token: '#token'
});
});
})();
url format:
http://localhost:3000/auth/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZ2NmYWJyaSIsImlhdCI6MTQ2ODI0NDI1NCwiZXhwaXJlSW4iOjIxNjAwMDAwfQ.5rs1rlWMTTcap4idG-XOU-UiwbU0YzlnAYjm9Vwz-B0
I usually send it in a header, most often I name it x-auth-header.
I don't however use nor I recommend anyone (except for playing around) to use ngResource, as it is limiting.
Personally, I use restangular, with request/response interceptors.
Response interceptor to grab and decode the token, and request interceptor to authorize the request with "Bearer" + tokenString.
Related
When I make a post request to the /login endpoint in postman it works fine and returns all the information. However when I try to navigate to the end point in the url the route returns unfound. In the console I get GET http://localhost:5000/login 404 (Not Found). Why is the console returning for a get request? If I try to call the post request in axios I get xhr.js:177 POST http://localhost:3000/login 404 (Not Found).
app.js
require("dotenv").config();
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const router = express.Router();
const cors = require('cors');
const app = express();
app.use(cors())
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
const mongoose = require('mongoose');
const connection = "password"
mongoose.connect(connection, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true
})
const clientRoutes = require('./routes/clientRoutes');
const traderRoutes = require('./routes/traderRoutes');
const loginRoute = require('./routes/loginRoute')
app.use('/', clientRoutes, traderRoutes, loginRoute);
// setup a friendly greeting for the root route
app.get('/', (req, res) => {
res.json({
message: 'Welcome to the REST API for Pave!',
});
});
// send 404 if no other route matched
app.use((req, res) => {
res.status(404).json({
message: 'Route Not Found',
});
});
// setup a global error handler
app.use((err, req, res, next) => {
if (enableGlobalErrorLogging) {
console.error(`Global error handler: ${JSON.stringify(err.stack)}`);
}
res.status(err.status || 500).json({
message: err.message,
error: {},
});
});
app.listen(5000, () => console.log('Listening on port 5000!'))
loginRoute.js
require("dotenv").config();
const express = require("express");
const router = express.Router();
const jwt = require("jsonwebtoken");
const bcryptjs = require("bcryptjs");
const Client = require("../models/clientSchema");
const Trader = require("../models/traderSchema");
function asyncHandler(callback) {
return async (req, res, next) => {
try {
await callback(req, res, next);
} catch (error) {
next(error);
console.log(error);
}
};
}
router.post('/login', asyncHandler(async (req, res, next) => {
let user = req.body;
const trader = await Trader.findOne({ emailAddress: req.body.emailAddress })
if (user && trader) {
console.log(trader)
let traderAuthenticated = await bcryptjs.compareSync(user.password, trader.password);
console.log(traderAuthenticated)
if (traderAuthenticated) {
console.log('Trader match')
const accessToken = jwt.sign(trader.toJSON(), process.env.ACCESS_TOKEN_SECRET)
res.location('/trader');
res.json({
trader: trader,
accessToken: accessToken
}).end();
} else {
res.status(403).send({ error: 'Login failed: Please try again'}).end();
}
} else if (user && !trader) {
const client = await Client.findOne({emailAddress: req.body.emailAddress})
console.log(client)
let clientAuthenticated = await bcryptjs.compareSync(user.password, client.password);
console.log(clientAuthenticated)
if (clientAuthenticated) {
console.log('Client match')
const accessToken = jwt.sign(client.toJSON(), process.env.ACCESS_TOKEN_SECRET)
res.location('/client');
res.json({
client: client,
accessToken: accessToken
});
} else {
res.status(403).send({ error: 'Login failed: Please try again'}).end();
}
} else {
res.status(403).send({ error: 'Login failed: Please try again'}).end();
}
})
);
module.exports = router;
You set POSTMAN to make a POST request, right? When you enter a url in the browser, that causes a GET request - and you have no route to manage this that I can see, but for the default Not found.
you are calling with axios with wrong port no. it should, POST method http://localhost:5000/login as your application is running on port 5000.
but you are calling, POST http://localhost:3000/login
I wrote a small node.js server with a login system and I am trying to protect my routes. I have created the middleware that should check authentication on each protected route, but it seems that I am not sending the JWT token correctly, because every time I log in I get the Authentication failed message. How can I send the JWT token correctly and log in if the password and username are correct? Here is my Node.js server:
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');
require('dotenv').config();
const PORT = process.env.PORT || 1337;
const jwt = require('jsonwebtoken');
const checkAuth = require('./middleware/check-auth.js')
let Post = require('./models/post.model.js');
app.use(cors());
app.use("/assets", express.static(__dirname + "/assets"));
app.use(bodyParser.urlencoded({ extended: true }));
app.set('view-engine', 'ejs');
app.get('/', (req, res) => {
res.render('index.ejs');
});
app.post('/', (req, res) => {
let username = req.body.username;
let password = req.body.password;
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
res.json('Invalid credentials');
} else {
const token = jwt.sign({
username: username,
}, process.env.SECRET_KEY, {
expiresIn: '1h'
});
res.redirect(`/dashboard?token=${token}`);
}
});
app.get('/dashboard', checkAuth, (req, res) => {
res.render('dashboard.ejs');
});
app.get('/dashboard/createPost', checkAuth, (req, res) => {
res.render('post.ejs');
});
app.post('/dashboard/createPost', async (req, res) => {
let collection = connection.collection(process.env.POSTS_WITH_TAGS);
res.setHeader('Content-Type', 'application/json');
let post = new Post(req.body);
collection.insertOne(post)
.then(post => {
res.redirect('/dashboard')
})
.catch(err => {
res.status(400).send(err);
});
});
app.listen(PORT);
and here is my check-auth middleware:
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization.split(' ')[1];
console.log(token);
const decoded = jwt.verify(token, process.env.SECRET_KEY, null);
req.body.decoded = decoded;
console.log(req.body.decoded);
} catch (error) {
return res.status(401).json({
message: 'Authentication failed'
});
}
next();
};
Use Javascript fetch API for sending JWT token as header authorization
fetch('backend_domain/dashboard', {
method: 'get',
headers: {
Authorization: JWT_Token
}
}).then(data => {..your operation here..})
Reference: fetch_mdn for better understanding of fetch API
I think the problem here is that you are getting an empty object in req.body when you try to pass it as a json string in postman. I would recommend either
pass your credentials in x-www-form-urlencoded tag in postman or
Using express.Router() and creating different files for routes
In your index.js file write:
app.use('/', require('./routes.js'))
And create a file with name routes.js and put your routes as this:
const express = require('express');
const router = express.Router();
router.post('/', (req, res) => {
let username = req.body.username;
let password = req.body.password;
console.log(req.body);
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
res.json('Invalid credentials');
} else {
const token = jwt.sign({
username: username
}, process.env.SECRET_KEY, {
expiresIn: 3600
});
return res.send({token});
}
});
module.exports = router;
I am using Node.JS Express and socket.io.
So my idea was to emit the message directly from my controller’s insert-method. In my server.js file, iam creating the socket.io object and try to make it accessible for other modules:
My server.js is like this
require('rootpath')();
var express = require('express');
var app = express();
var cors = require('cors');
var bodyParser = require('body-parser');
var expressJwt = require('express-jwt');
var config = require('config.json');
var mongoose = require('mongoose');
mongoose.connect(config.connectionString);
var db = mongoose.connection;
db.on('error',console.error.bind(console,'Connection error'));
db.on('open',function(){
console.log('connected');
});
app.use(cors());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// use JWT auth to secure the api, the token can be passed in the authorization header or querystring
app.use(expressJwt({
secret: config.secret,
getToken: function (req) {
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
return req.headers.authorization.split(' ')[1];
} else if (req.query && req.query.token) {
return req.query.token;
}
return null;
}
}).unless({
path: [
'/users/authenticate',
'/users/register',
'/users',
'/chats','/chats/sendMessage',
'/rooms','/rooms/create',
'/chats/connection',
'/chats/add-message'
]
}));
// routes
app.use('/users', require('./controllers/users.controller'));
app.use('/chats', require('./controllers/chats.controller'));
app.use('/rooms', require('./controllers/rooms.controller'));
var http = require('http').Server(app);
var io = require('socket.io')(http);
io.on('connection', (socket) => {
console.log('The user is connected');
socket.on('new-message', (message,userInfo) => {
console.log(userInfo);
console.log(message);
io.emit('message', {type:'new-message', text: message});
});
socket.on('disconnect', function(){
console.log('The user is disconnected');
});
socket.on('add-message', (message,userInfo) => {
console.log(userInfo);
console.log(message);
io.emit('message', {type:'new-message', text: message});
});
socket.on('add-record', (record) => {
io.emit('getrecord', {type:'new-record', text: record});
});
});
// start server
var port = process.env.NODE_ENV === 'production' ? 80 : 4000;
var server = http.listen(port, function () {
console.log('Server listening on port ' + port);
});
Client - Data received
Emit welcome message on connection
how can i use this below socket connection in chat.controller.js
io.on('connection', (socket) => {
console.log('The user is connected');
socket.on('new-message', (message,userInfo) => {
console.log(userInfo);
console.log(message);
io.emit('message', {type:'new-message', text: message});
});
socket.on('disconnect', function(){
console.log('The user is disconnected');
});
socket.on('add-message', (message,userInfo) => {
console.log(userInfo);
console.log(message);
io.emit('message', {type:'new-message', text: message});
});
socket.on('add-record', (record) => {
io.emit('getrecord', {type:'new-record', text: record});
});
});
and i am creating controller file like this
chats.controller.js
var config = require('config.json');
var express = require('express');
var router = express.Router();
var userService = require('../services/user.service');
var chatService = require('../services/chat.service');
// routes
router.post('/sendMessage', send);
router.get('/', getAll);
module.exports = router;
function send(req, res) {
chatService.sendMessage(req.body)
.then(function () {
res.sendStatus(200);
})
.catch(function (err) {
res.status(400).send(err);
});
}
function getAll(req, res) {
chatService.getAllMessage()
.then(function (chats) {
res.send(chats);
})
.catch(function (err) {
res.status(400).send(err);
});
}
function getCurrent(req, res) {
userService.getById(req.user.sub)
.then(function (user) {
if (user) {
res.send(user);
} else {
res.sendStatus(404);
}
})
.catch(function (err) {
res.status(400).send(err);
});
}
function update(req, res) {
userService.update(req.params._id, req.body)
.then(function () {
res.sendStatus(200);
})
.catch(function (err) {
res.status(400).send(err);
});
}
function _delete(req, res) {
userService.delete(req.params._id)
.then(function () {
res.sendStatus(200);
})
.catch(function (err) {
res.status(400).send(err);
});
}
And also i have created one service file
name is chat.service.js
var config = require('config.json');
var _ = require('lodash');
var jwt = require('jsonwebtoken');
var bcrypt = require('bcryptjs');
var Q = require('q');
var mongoose = require('mongoose');
var Chat = require('../model/chat');
var service = {};
service.sendMessage = sendMessage;
service.getAllMessage = getAllMessage;
module.exports = service;
function sendMessage(userParam) {
var deferred = Q.defer();
var chat = new Chat({
room_id:userParam.from_id,
from_id:userParam.from_id,
to_id:userParam.to_id,
chatrp_id:userParam.chatrp_id,
message:userParam.message
});
chat.save(function (err,doc) {
if (err) deferred.reject(err.name + ': ' + err.message);
deferred.resolve();
})
return deferred.promise;
}
function getAllMessage() {
var deferred = Q.defer();
Chat.find({},function (err, response) {
if (err) deferred.reject(err.name + ': ' + err.message);
// return users (without hashed passwords)
response = _.map(response, function (res) {
return _.omit(res, 'hash');
});
deferred.resolve(response);
});
return deferred.promise;
}
But is is neither giving me an error or not working.
Can anyone please help, how can I export that in a separate file so that client and server code can be separated?
Thanks in advance for helping.
This how I got this to work.
Steps are as follows:
1) I created a utils folder, inside which I created a file named socket.js
utils/socket.js
const Server = require('socket.io');
const io = new Server();
var Socket = {
emit: function (event, data) {
console.log(event, data);
io.sockets.emit(event, data);
}
};
io.on("connection", function (socket) {
console.log("A user connected");
});
exports.Socket = Socket;
exports.io = io;
2) Then in my server(app.js) I did this
app.js
const app = express();
const server = http.createServer(app);
const { io } = require("./utils/socket");
io.attach(server);
module.exports = app;
3)After this 2 steps, In your controller file or any other file(s) where you need to emit an event, you can then just do this:
const { Socket } = require("../../utils/socket");
Socket.emit("newMessage", {
message: "hello from controller"
});
To emit messages from your controllers, the controllers need to have access to the original socket.io instance.
So, in chats.controller.js I would do like this:
module.exports = function(io){
return {
function: getAll(req, res){
// here you now have access to socket.io.
// you can either do:
io.emit('new-message',{
// new message object
});
// or you can include your chat service the same way
// that this file is included and call the appropriate function
},
}
};
To use this controller, just do
const someController = require('./path/to/chats.controller.js')(io);
You can do exactly the same in your service file.
I'm setting up a universal React app and using this project as a base. I am successfully proxying requests (using http-proxy) to my Laravel backend. However, I'm new to Nodejs and I don't know how the best method to securely store a JWT from the proxied server to the client.
My initial thought was to store the token to localStorage, but the problem is that the express server won't have access to it. So my next guess would be to store it as a cookie, but I'm not sure how to store it on the client or include it as a header for all outgoing requests (additionally, I would likely need some sort of csrf middleware).
So how would I manipulate the response from my api server to put a token in a cookie that is set in the client, then use that as a bearer token for all api requests?
// server.js
const targetUrl = 'http://' + config.apiHost + ':' + config.apiPort;
const app = new Express();
const server = new http.Server(app);
const proxy = httpProxy.createProxyServer({
target: targetUrl,
changeOrigin: true
});
// Proxy to Auth endpoint
app.use('/auth', (req, res) => {
// on a successful login, i want to store the token as a cookie
proxy.web(req, res, {target: targetUrl});
});
// Proxy to api endpoint
app.use('/api', (req, res) => {
// use the token in the cookie, and add it as a authorization header in the response
proxy.web(req, res, {target: targetUrl});
});
Given that the response from the auth endpoint in laravel is like this:
{
"token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"
}
This code will do what you want:
// server.js
const targetUrl = 'http://' + config.apiHost + ':' + config.apiPort;
const Express = require('express');
const http = require('http');
const httpProxy = require('http-proxy');
const app = new Express();
const server = new http.Server(app);
const Cookies = require( "cookies" )
const proxy = httpProxy.createProxyServer({
target: targetUrl,
changeOrigin: true
});
// Proxy to Auth endpoint
app.use('/auth', (req, res) => {
// on a successful login, i want to store the token as a cookie
// this is done in the proxyRes
proxy.web(req, res, {target: targetUrl});
});
// Proxy to api endpoint
app.use('/api', (req, res) => {
// use the token in the cookie, and add it as a authorization header in the response
var cookies = new Cookies( req, res )
req.headers.authorization = "JWT " + cookies.get('jwt-token');
proxy.web(req, res, {target: targetUrl});
});
proxy.on('proxyRes', function(proxyRes, req, res) {
if (req.originalUrl === '/auth') {
var cookies = new Cookies( req, res )
var body = '';
var _write = res.write;
var _end = res.end;
var _writeHead = res.writeHead;
var sendHeader = false;
res.writeHead = function () {
if (sendHeader) {
_writeHead.apply( this, arguments );
}
}
res.write = function (data) {
body += data;
}
res.end = function () {
sendHeader = true;
var parsed = JSON.parse(body);
cookies.set('jwt-token', parsed.token);
_write.apply(this, [ body ]);
_end.apply(this, arguments);
}
}
});
Problem: Satellizer is not sending the Authorization Header to server.
I am trying out Satellizer.Js on a project that I am working on. It's great so far, however, it is not properly sending the Authorization Header in the request to the server (Note: I am using Node for the backend). It's not a CORS problem, since I'm working with localhost for the time being. When I locally log in/sign up, the server responds with a token, and Satellizer correctly sets that in local storage. I check the Network tab in the dev tools to check the headers, but no Authorization header. Has anyone dealt with this, or have any ideas/tips I can use? Thanks in advance.
Here is my server.js code:
var express = require('express'),
app = express(),
path = require('path'),
cors = require('cors'),
bodyParser = require('body-parser'),
mongoose = require('mongoose'),
config = require('./config/config'),
morgan = require('morgan'),
port = process.env.PORT || 8080;
var express = require('express'),
app = express(),
path = require('path'),
cors = require('cors'),
bodyParser = require('body-parser'),
mongoose = require('mongoose'),
config = require('./config/config'),
morgan = require('morgan'),
port = process.env.PORT || 8080;
//connect to the database
mongoose.connect(config.db);
//morgan - log all requests to the console
app.use(morgan('dev'));
//middleware for body parser
app.use(bodyParser.urlencoded({extended:true}));
app.use(bodyParser.json());
//handle CORS requests
app.use(cors());
/*app.use(function(req,res,next){
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, content-type, \ Authorization');
next();
});*/
//set the location for static files
app.use(express.static(__dirname + '/public'));
//API Routes
var apiRoutes = require('./app/routes/app-routes.js')(app,express);
app.use('/auth', apiRoutes);
//send the users to the front end
app.get('*', function(req,res){
res.sendFile(path.join(__dirname + '/public/app/views/index.html'));
});
//listen on port
app.listen(port, function(){
console.log('Listening on port: ' + port + "....");
});
Here is where to token gets set on the angular side with satellizer:
vm.login = function(){
$auth.login({email: vm.user.email, password: vm.user.password})
.then(function(res){
//check for token;
if(!res.data.token){
vm.error = true;
vm.errorMessage = res.data.message;
}else{
//redirect to the dashboard
$location.path('/dashboard');
}
})
.catch(function(){
vm.error = true;
vm.errorMessage = "Failed to login, please try again."
});
};
Here is my only authenticated route:
router.get('/dashboard', ensureAuthenticated, function(req,res){
//with a validated token
console.log(req.headers);
console.log(req.headers.authorization);
res.json({success: true, message:'You made it, congrats!'});
});
This is my create-a-token function, and this is my authentication middleware:
function createToken(user){
var payload = {
exp: moment().add(14, 'days').unix,
iat: moment().unix(),
sub: user._id
}
return jwt.encode(payload,config.secret);
};
function ensureAuthenticated(req, res, next) {
if (!req.headers.authorization) {
return res.status(401).send({ message: 'Please make sure your request has an Authorization header' });
}
var token = req.headers.authorization.split(' ')[1];
var payload = null;
try {
payload = jwt.decode(token, config.secret);
}
catch (err) {
return res.status(401).send({ message: err.message });
}
if (payload.exp <= moment().unix()) {
return res.status(401).send({ message: 'Token has expired' });
}
req.user = payload.sub;
next();
}
Note: Satellizer.Js 's $httpInterceptor is responsible for sending the token in the request. Here is that code:
.factory('SatellizerInterceptor', [
'$q',
'SatellizerConfig',
'SatellizerStorage',
'SatellizerShared',
function($q, config, storage, shared) {
return {
request: function(request) {
if (request.skipAuthorization) {
return request;
}
if (shared.isAuthenticated() && config.httpInterceptor(request)) {
var tokenName = config.tokenPrefix ? config.tokenPrefix + '_' + config.tokenName : config.tokenName;
var token = storage.get(tokenName);
if (config.authHeader && config.authToken) {
token = config.authToken + ' ' + token;
}
request.headers[config.authHeader] = token;
}
return request;
},
responseError: function(response) {
return $q.reject(response);
}
};
}])
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('SatellizerInterceptor');
}]);
Try setting the Authorization header like this...
request.headers.Authorization = token;
What's your motivation behind setting token manually with $auth.setToken when Satellizer takes care of it automatically when $auth.login is used?
Here is Satellizer login example which works perfectly in our apps:
angular.module('MyApp')
.controller('LoginCtrl', function($scope, $location, $auth, toastr) {
$scope.login = function() {
$auth.login($scope.user)
.then(function() {
toastr.success('You have successfully signed in!');
$location.path('/');
})
.catch(function(error) {
toastr.error(error);
});
};
});
Bottom line: you shouldn't set token yourself. Satellizer does it automatically behind the scenes.
I had the same problem. You must send all requests with "/api/" prefix. Without this prefix I got error, but w