I'm making an app with express + passport and angularJS; I want to be able to send any errors produced from passport (such as username taken or no email provided) by json so my angularJS app can receive these errors in a json response. More specifically right now I want to have a json response to my signup POST method that outputs any errors. I have tried to do this for myself and I've search all over the web and stack overflow I just cannot work this out!
Here is my users route file in express:
var express = require('express');
var router = express.Router();
var isAuthenticated = require('../config/isAuthenticated');
module.exports = function(passport){
router.get('/loggedin', function(req, res){
res.send(req.isAuthenticated() ? req.user : '0');
});
router.post('/signup', passport.authenticate('local-signup', {
successRedirect : '/',
failureRedirect : '/signup',
failureFlash: true
}));
router.post('/login', passport.authenticate('local-login'), function(req, res){
res.send(req.user);
});
router.post('/signout', function(req,res){
req.logout();
res.json({redirect: '/'});
});
router.get('/authtest', isAuthenticated, function(req, res){
res.render('authtest', {user: req.user});
});
return router;
};
This is my passport signup strategy:
passport.use('local-signup', new LocalStrategy({
usernameField : 'username',
passwordField : 'password',
passReqToCallback : true
},
function(req, username, password, done){
process.nextTick(function(){
User.findOne({'local.username' : username}, function(err, user){
if(err) return done(err);
if (user) { //username already exists
return done(null, false, {message: 'Username already exists'});
} else if(!req.body.email) { //no email address provided
return done(null, false, {message: 'You must provide an email address!'});
} else {
var newUser = new User();
newUser.local.username = username;
newUser.generateHash(password, function(err, hash){
if(err) return done(err);
newUser.local.password = hash;
});
newUser.email = req.body.email;
newUser.servers = [];
newUser.save(function(err){
if(err) throw err;
return done(null, newUser);
});
};
});
});
}
));
I know looking at my code right now it looks like I haven't tried to solve this myself at all but this is just my latest working code; I have been stuck at this for the past few days!
Any help would be greatly appreciated :)
According to the current code of passport this is probably achievable by passing custom callback to handle all results of authentiction yourself. This callback is given after options or instead of those.
passport( "local-signup", { ... }, callbackFn );
or
passport( "local-login", callbackFn );
This callback is used in all resulting situations of trying to authenticae. It is thus invoked on processing errors like this:
callbackFn( err )
If (all configured) authentications have failed it is called with
callbackFn( null, false, challenge(s), status(es) )
On successfully having authenticated user the callback is invoked like so:
callbackFn( null, user, infos )
with infos optionally provided by strategies.
Now comes the bottom-side: In either situation passport.authenticate() skips usual processing but instantly invokes provided callback to care for the rest. This includes processing of any options passed in call for passport.authenticate() like flashing messages, preparing session and request for containing authenticated user etc.
Since options given passport.authenticate() are never passed into callback there is actually no obvious reason to use both.
When I was stumbling over the very same problem (linking passport-service with angular-js POST request) I declined to consider use of callback a proper solution. This callback isn't documented. And it doesn't even look quite useful for it isn't passing req, res and next to pass any actual request in callback. Thus it makes very little sense to use it at all and I'd expect it to vanish soon or to change its behaviour quite much.
So the second approach was about trying to figure out why there is a problem in AngularJS. Passport is sending plain text Unauthorized in response with status code 401. AngularJS is trying to parse this as JSON and produces Syntax error. The text Unauthorized results from passprt ending response very simply by invoking
res.statusCode = 401;
res.end(http.STATUS_CODES[res.statusCode]);
Thus a proper workaround might try to replace
either text in http.STATUS_CODES though this is having impact on processing further requests and thus isn't preferrable
or res.end() by an overloaded method acting differently if res.statusCode is 401.
Due to affecting any current request, only, I tried the latter. Replaced res.end() might be used to send any text you want:
router.post('/login',
function(req, res, next) {
var _end = res.end;
res.end = function() {
if (res.statusCode === 401) {
return _end('{"status":"Unauthorized"}');
}
return _end.apply(this, arguments);
};
next();
},
passport.authenticate('local-login'),
function(req, res) {
res.send(req.user);
}
);
Alternatively the replaced method might add previously missing response header information on content type, for this was actually causing issues in AngularJS processing that response as JSON by default.
router.post('/login',
function(req, res, next) {
var _end = res.end;
res.end = function() {
if (res.statusCode === 401) {
res.set("Content-Type", "text/plain");
}
return _end.apply(this, arguments);
};
next();
},
passport.authenticate('local-login'),
function(req, res) {
res.send(req.user);
}
);
Finally, either approach is really just a workaround. I think passport is in the need for revising this annoying limitation.
Related
I'm currently learning MySQL by creating an REST API using Express. I've opted for validating data on the server instead of the database. My question is, WHERE on the server should I do that?
Should I validate data (for example minimum and maximum length for the username)...
...using a middleware before the controller file?
...in the controller file, after reciving the request and before sending the data to the models file? (example file below)
...the models file before querying? (example file below)
...some completely other solution I haven't thought of?
./controllers/authController.js
const register = async (req, res) => {
const { username, email, password } = req.body;
**// Validating input data here?**
// TODO hash password
const activationToken = generateActivationToken(48);
const newUser = await User.create(
{ username, email, password, activationToken },
(err, result) => {
console.log(err);
if (err)
return res.status(400).json({
msg: err.message || "Some error has occured. Please try again.",
});
else res.json({ result });
}
);
};
./models/User.js
var db = require("../dbconnection");
// constructor for User object
const User = function (user) {
this.username = user.username;
this.email = user.email;
this.password = user.password;
this.activationToken = user.activationToken;
};
User.create = (newUser, result) => {
**// Validating input data here?**
db.query("INSERT INTO users SET ?", newUser, (err, res) => {
if (err) return result(err, null);
console.log("Created user.");
result(null, { id: res.insertId });
});
};
module.exports = User;
What's the usual/best practice way of doing this? If there isn't a best practice, how do YOU do it? (I validate data on the front-end too of course.) Do you know of any good example projects I could take a look at?
Thank you for your time!
In OOP there's a principle called as:
"The information Expert principle"
According to this principle we should assign responsibilities / methods inside the objects that contain the most information to fulfil the task
(It helps us create cohesive classes).
So, you should probably put the validation logic inside the User model.
I'm trying to retrieve data from a database and send it back to the user, but since mysql queries work asynchronously, I can't just put the code that sends the response after the query code, I have to send the response within the callback function of the query.
var express = require('express');
var fs = require('fs');
var mysql = require('mysql')
var app = express();
var con = mysql.createConnection({
host:"localhost",
user:"root",
password:"",
database:"tempdb"
})
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT,
PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-
type');
res.setHeader('Access-Control-Allow-Credentials', true);
next();
});
app.get('/',function(req,res,next){
function foo(res){
con.query('SELECT * FROM `data` WHERE 1',function(err,result){
if (err) throw err;
res.end('weee',function(err){
if (err) throw err;
)}
})
}
foo(res)
next();
})
app.listen(1001)
Even with something as simple as this, when the data from the database isn't processed or used at all, the res.end function doesn't send any data.
I've tried many variations of this, using waterfall callbacks, putting the res.end in a function outside of the query and calling it, but nothing seems to work. The only time it works it when I put it outside the query, but since the query is async I can't get any data out of it that way. Is there a way to work around this or am I just missing something?
Thanks in advance!
Remove the call to next() in your app.get() handler.
You only want to do that when you are NOT handling the request and sending a response and you want to let other handlers have a crack at the request.
The way you have it, you are calling next() BEFORE the con.query() finishes so that lets the default handler in express handle the request and thus your res.end() doesn't actually do anything because a response has already been sent for this request.
Here's what I would suggest:
app.get('/', function(req, res){
con.query('SELECT * FROM `data` WHERE 1',function(err, result){
if (err) {
console.log(err);
res.status(500).end();
} else {
res.send('weee');
}
});
});
So, you only call next() if you are not going to be sending a response and you want someone else in the handler chain to send the response.
When using res in middlewares throw a return before res.
app.get('/',function(req,res,next){
function foo(res){
con.query('SELECT * FROM `data` WHERE 1',function(err,result){
if (err) throw err;
return res.end('weee',function(err){
if (err) throw err;
)}
})
}
foo(res)
next();
})
Hopefully this works...
I'm trying to retrieve data from a SQL database and display that said data on a Reactjs web app. However, all the calls I make to the database results in the HTML of the webpage in focus. I have set the headers, and I've tried to change the way the response from the express call is being handled.
Here is the expressjs script I am using right now:
const express = require('express');
const sql = require('mssql/msnodesqlv8');
const bodyParser = require('body-parser');
const path = require('path');
const cors = require('cors');
const db = require('./db.js');
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use('/counselling/triageadmin/', express.static(path.join(__dirname, '/build')));
app.use(cors());
app.get('/getTable', function (req, res, next){
var request = new sql.Request(db);
request.query('select * from Counselling order by TicketID desc', (err, result) =>{
if (err) { return next(err); }
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(result["recordset"]));
});
});
From there, my axios calls look like this:
componentWillMount(){
let self = this;
axios.get("/getTable")
.then(function (response){
console.log(response.data);
self.setState({
data: response.data,
});
})
.catch(function (error){
console.log(error);
})
}
I added the console.log to check what was being returned, and as said, it was the HTML code of the current page of focus.
I made some changes to reflect what steps I took to get the 500 issue out. The current code, however, results in a 404.
If you move your get on top of your put it should work. The problem seems to be that the static clause resolves your request before it gets to your endpoint, so if you do this:
app.get('/counselling/triageadmin/getTable', function (req, res, next){
var request = new sql.Request(db);
request.query('select * from Counselling order by TicketID desc', (err, result) =>{
if (err) { return next(err); }
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(result["recordset"]));
});
});
app.use('/counselling/triageadmin/', express.static(path.join(__dirname, '/build')));
the path to the get will attempt to be matched before you're routed to your static files.
Ideally you would want to have your rest endpoints under a different namespace, i.e. /api but if you decide to keep your setup, this should help.
I think your routes might be conflicting with each other. From the express documentation at: http://expressjs.com/en/4x/api.html#app.use
// this middleware will not allow the request to go beyond it
app.use(function(req, res, next) {
res.send('Hello World');
});
// requests will never reach this route
app.get('/', function (req, res) {
res.send('Welcome');
});
Thus, your route '/counselling/triageadmin/getTable' will never be reached, because your route '/counselling/triageadmin/' is intercepting it, responding with static resources.
To solve this, try organizing your routes in a way that puts all of your API requests at a different subfolder, like '/api'. So your getTable endpoint would be located at: '/api/counselling/triageadmin/getTable/' or something like that.
I'm also learning the MEAN stack and I stumbled upon your question since I had the opposite problem. I wanted it to respond with an HTML instead of a JSON
this line of code MAKES it respond with an HTML
res.send(JSON.stringify(result["recordset"]));
(I tried res.send("<h3 HTML T_T </h3>");) and it did send and HTML
however, if you try
res.json(String(req.params.id)); <= Notice the res.json instead of res.send
It responds with a JSON :)
I hope this helped
I'm trying to build a stateless API using JWT. I have a middleware function in my Router which checks if the JWT has expired, if it has, a new Token with a new timestamp is generated on the fly.
I would like to pass the new token along with the response in the JSON Object. My current approach is like this, but it of course doesn't work:
router.use(function (req, res, next) {
// JWT Expired
// Generate new JWT
res.write(JSON.stringify({token: token});
next();
});
router.get('/securedRoute' function(req, res) {
// Fetch data
res.json({data: data});
});
:
// Output should be:
{token: 'erg34jlkgjre.3f4fknlj.34f3', data: ['sdfa', 'sdf']}
It would be nice to find a way, where I don't have to alter all of my existing code and check if there is a new token.
Thanks for your help!
One option would be to add the authorization token in the response header:
router.use((request, response, next) => {
response.setHeader('Token', token);
next();
});
router.get('/securedRoute', (request, response) => {
response.json({ data: data });
});
Alternatively, you could always add the token to the request and then conditionally add the request.token into all of your routes like the previous answer suggested. Unfortunately that would mean that you need to modify all of your routes.
As an alternative you could override the response.json method and manually inject the token if it exists. In doing so, you wouldn't need to modify any of your routes.
router.use((request, response, next) => {
request.token = token;
((proxied) => {
response.json = function (data) {
if (request && request.token) {
data.token = request.token;
}
return proxied.call(this, data);
};
})(response.json);
next();
});
router.get('/securedRoute', (request, response) => {
response.json({ data: data });
});
In the example above, the response.json method is overridden in the middleware. This is done by passing a reference to the old response.json method and then calling it after conditionally injecting the token into the payload.
The answer is assuming you want to achieve that in same method
Rather than writing the token in middleware do something like
(req,res,next)=>{
req.token = newToken;
next();
}
And in your route
res.json(req.token ? {token:req.token,data:data} : {data:data});
I'm trying to figure out how to use nodejs + Passport + MySQL. It seems as though just about every tutorial out there is using mongoDB and I don't want to do that. In fact some quick searches of this type will yield web pages like (http://nodejsrocks.blogspot.com/2012/04/nodejs-expressjs-mysql.html) and a youtube video that is a guy (https://www.youtube.com/watch?v=jGBbMVJx3h0) doing nothing but loging in, and who knows what he is really using, but the page has had 3K + views. I'd hope that some of the developers would look at that and say maybe there is a use for something like a comprehensive non MVC type of thing with MySQL. My reason for this is I am trying to get iOS and Android capabilities only and have no need for a large scaffolding overhead. Just the DB and server side scripting handling the queries and returning JSON objects to the phones.
So, that being said, can someone who has had real experience with this please help me out(And the rest of the world trying to do similar things without any in-depth tutorials, because we aren't using mongoDB and full blown scaffolding).
The tables I have set up for a 'TwitterStrategy' are users(id (PK), username, email, salt, password), and twitterusers(id (PK), name, screenname, location, description, url, img, token, tokensecret).
Here is the code I am trying to get going from a single main.js file. I know this is not best practices, and I plan to clean it up later, but for now, I would like to understand what I am missing and get things working. It would be extremely appreciated if someone can help, and I'm SURE others would find this very useful as well. Thanks.
var http = require('http'),
mysql = require('mysql'),
url = require('url'),
crypto = require('crypto'),
express = require('express'),
flash = require('connect-flash'),
passport = require('passport'),
TwitterStrategy = require('passport-twitter').Strategy;
var db = mysql.createConnection({
host : "****",
user : "****",
password : "****",
port : '****',
database : '****'
});
// Connect the connection to DB and throw error if exists
db.connect(function(err) {
if (err) {
console.error('Error connecting to db');
console.error(err);
return;
}
console.log('Database connected');
});
var TWITTER_CONSUMER_KEY = "****";
var TWITTER_CONSUMER_SECRET = "****";
passport.use(new TwitterStrategy({
consumerKey: TWITTER_CONSUMER_KEY,
consumerSecret: TWITTER_CONSUMER_SECRET,
callbackURL: 'http://127.0.0.1:3000/auth/twitter/callback'},
function(accessToken, refreshToken, profile, done) {
//db.query(SELECT ........ FROM ...... WHERE ........, function (err, user){
if (err) {
console.log(err);
}
if (!err && user != null){
done(null, result);
} else {
console.log(result);
}
})
});
}
));
passport.serializeUser(function(user, done) {
console.log('serializeUser: ' + user.id);
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
db.query('SELECT * FROM users WHERE id = ' + id, function(err, result) {
if (err){
console.log(err);
} else {
console.log(result);
}
if (!err) {
done(null, result);
} else {
done(err, null);
}
});
});
var app = express();
app.set(function(){
// app.set('views', __dirname + '/views'); // Definitely for some views which aren't being used here
// app.set('view engine', 'jade'); // Using jade for views, not used
// app.use(express.favicon()); // Not really sure this is important, should be web only
app.use(express.logger('dev')); // Again, not really sure this is important
app.use(express.bodyParser()); // Have no idea what this is used for
app.use(express.methodOverride()); // Same no Fn clue
app.use(express.cookieParser('what the F'));
app.use(express.session());
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
// app.use(app.router); // Here again we are defining our routes in main, so shouldn't need this.
// app.use(express.static(__dirname + '/public'));
});
var server = http.createServer(function(req, res) {
console.log('url: ' + req.url);
var params = url.parse(req.url, true)
var path = params.pathname;
if (path == '/signup') {
console.log("User signing up");
onSignUp(params, res);
} else if (path == '/signin') {
console.log("User signing in");
onSignIn(params, res);
} else if (path == '/auth/twitter'){
passport.authenticate('twitter'),
function(req, res){
console.log('Twitter User Created or Signed In');
}
}
});
//Keep server alive and listening to requests with DB connected also
server.listen(3000);
Am I missing another auth table? What is it that I need to put in the MySQL statement where the dots are so that I can find the user, and what parameters are being passed from the user request to get the query going, i.e. what is this oauth ID I have seen in tutorials that is getting passed from what seems to be the user to twitter for authorization? Also, what should I be expecting from this callback from Twitter? Anyway, I'll be glad to post all of this somewhere for everyone else to look at once I have a solution made so that all of us using MySQL and node don't get left out and have to search google to find something that seems as though it should be readily available, instead of copies of the same exact nodejs + mongoDB + express tutorial (with many that are out of date except for the scotch io, which looks very good if you wanna use mongo...might I add instances over at Amazon run about $279 estimated per month on the low end) that is floating around and being redistributed by nearly anyone with a "tutorial" out there. Thanks again.
Try wrapping strategy function under process.nextTick, e.g.,
passport.use(new TwitterStrategy({
consumerKey: TWITTER_CONSUMER_KEY,
consumerSecret: TWITTER_CONSUMER_SECRET,
callbackURL: 'http://127.0.0.1:3000/auth/twitter/callback'},
function(accessToken, refreshToken, profile, done) {
process.nextTick(function(){
// this is where you put logic to check the profile sent from twitter already in your DB or not,
// its totally up to you whether you keep a separate auth table for it or not
// NB: there will be some unique value in profile that can be used for next references
db.query(SELECT ........ FROM ...... WHERE ........, function (err, user){
if (err) {
console.log(err);
}
if (!err && user != null){
done(null, result);
} else {
console.log(result);
}
})
});
});
}
));
you also have to have a route for accepting the callback, e.g.,
app.get('/auth/twitter/callback', function(req, res, next) {
passport.authenticate('twitter',
{ },
function(err, user) {
// the result you send from the strategy function will be here
// do anything you like with the user send
}
)(req, res, next);
});
Hope it makes things clearer.