Sailsjs: How do I transform a POST request's body/fields for the REST API - rest

It is awesome how sails will automatically generate a REST API for me. But, how can I add extra processing to a REST route? For example, I might have a POST /users route to create new users and it accepts a password as one of the attributes for the users model. But, I wouldn't want to store passwords as-is; rather I would want to hash passwords and salt them for better security. Is there a way to write code before the REST API handles the request so that way I can transform the input or do some kind of processing in general if need be?

You can implement beforeValidate or beforeCreate method under your user model
Check out the doc here http://www.sailsjs.org/#!/documentation/concepts/ORM/Lifecyclecallbacks.html
I use this for hash password :
/**
* User.js
*
* #description :: TODO: You might write a short summary of how this model works and what it represents here.
* #docs :: http://sailsjs.org/#!documentation/models
*/
module.exports = {
attributes : {
name : 'string',
password : {
type : 'string',
required : true,
minLength : 6
},
email : {
type : 'email',
unique : true,
required : true
},
toJSON : function ()
{
var obj = this.toObject();
delete obj.password;
return obj;
}
},
beforeCreate : function (attrs, next)
{
var bcrypt = require('bcrypt');
bcrypt.genSalt(10, function (err, salt)
{
if (err)
{
return next(err);
}
bcrypt.hash(attrs.password, salt, function (err, hash)
{
if (err)
{
return next(err);
}
attrs.password = hash;
next();
});
});
}
};

Related

TypeError: [function] is not a function in Passport local strategy

I'm trying to authenticate users locally with Passport.js while not keeping session and using my own JWTokens.
I was following this tutorial:
Learn using JWT with Passport authentication
While also reading Passport.js documentation. I don't know what went wrong, but passport doesn't seem to notice that some functions are indeed functions.
I've got a Load function to select a User from the DB(mongo) given certain criteria(a user might logIn with e-mail or phone number).
load: function(options, cb) {
options.select = options.select || 'email phone';
return this.findOne(options.criteria)
.select(options.select)
.exec(cb);
}
I'm calling passport.authenticate in my routes:
// Controllers //
const Users = require('../../app/controllers/users');
...
...
app.post('/api/login', passport.authenticate('local', { failureRedirect: '/api/login' }), Users.login);
And here's my local strategy:
const mongoose = require('mongoose');
const User = mongoose.model('User');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
{
usernameField: 'email',
phoneField: 'phone',
passwordField: 'password',
session: false
},
function(email, phone, password) {//cb == callback
const options = {
criteria: { email: email, phone: phone },
select: 'name username email hashed_password salt'
};
User.load(options, function(err, user) {
if (err || !user){
return res.status(400).json({
type: 'failure',
message: "User creation failed",
data: [err]
});
};
if (!user.authenticate(password)) {
return res.status(400).json({
type: 'failure',
message: "User creation failed",
data: [err]
});
};
req.login(user, {session: false}, (err) => {
if (err) {
res.send(err);
}
// generate a signed son web token with the contents of user object and return it in the response
const token = jwt.sign(user.id, 'your_jwt_secret');
return res.json({user, token});
});
});
}
));
I'm getting the following error:
TypeError: res.status is not a function
Before trying to get stuff back from passport with responde. I was trying to do it with cb(callback), as done in the tutorial, but I keep getting the same error.
Thanks in advance for any help!
There are a few issues with how you are implementing Passport's local strategy that are causing problems.
You are trying to use two fields as the username when Passport's local startegy only accepts one. (see: http://www.passportjs.org/packages/passport-local/)
function(username, password, done){}
If you want to use both as a username, you might want to consider creating your own custom strategy. This is going to be a little more in-depth, but you can start learning on the Passport Github page (https://github.com/jaredhanson/passport-strategy)
The second issue is that you are trying to get Passport to send a response in the local strategy which is not what it is intended to do. Instead, you should be passing the errors and return values to Passport's done() function, which will process them accordingly.
Here is an example of what your local strategy should look like:
passport.use(
new LocalStrategy(async (email, phone, password, done) => {
const options = {
criteria: { email, phone },
select: 'name username email hashed_password salt',
};
try {
const user = await User.load(options);
/**
* If null is returned meaning there was no user found, send the done call
* with the false flag. This tells passport to redirect to the failure URL.
*/
if (!user) {
return done(null, false);
}
/**
* If the user's password is incorrect, also return the done function with the false
* flag. This tells passport to redirect to the failure URL.
*/
if (!user.authenticate(password)) {
return done(null, false);
}
/**
* If a user is found and their password is verified, send the user object to
* the done function. This will tell Passport to call the next middelware attaching
* the user object.
*/
return done(null, user);
} catch (err) {
/**
* If there is an error with the DB call, return generic message
* for security purposes.
*/
return done('There was an internal server error.');
}
})
);
and an example of what your load function should look like:
load: options => {
return new Promise(async (resolve, reject) => {
options.select = options.select || 'email phone';
try {
const user = await this.findOne(options.criteria)
.select(options.select)
.exec();
resolve(user);
} catch (err) {
reject(err);
}
});
};
As a general best practice, I changed your callbacks to the newer method of promises (https://developers.google.com/web/fundamentals/primers/promises).
This should work in the way you are intending to use Passport.

auth0 Custom Database Action Script - How to access user_id?

I'm using auth0, I'm making a script on create under Action Scripts to save user to my mongoDB database. I only need to save the email and user_id, but I can't find the user_id?
Code is below, but the user only returns: {"tenant":"tenant-name","connection":"MongoDB-MainDB","email":"xxx","password":"xxx","debug":true}
Here is my code (This is basically the sample code provided by auth0):
function create (user, callback) {
mongo('mongodb://xxx', function (db) {
var users = db.collection('users');
users.findOne({ email: user.email }, function (err, withSameMail) {
if (err) return callback(err);
if (withSameMail) return callback(new Error('the user already exists'));
else {
users.insert({ "_id" : user.user_id, "email" : user.email}, function (err) {
if (err) return callback(err);
callback(new Error(JSON.stringify(user)))
});
}
});
});
}
auth0 tutorial for reference: https://auth0.com/docs/connections/database/custom-db

during sails lift it throw error

hi i am new to sails and passport js i am trying authentication using passport js in sails but while am adding passport.js in config folder it give an error..any one know how to resolve this issu..
Here is my error
C:\Users\sachinn\AppData\Roaming\npm\node_modules\sails\node_modules\include-all\lib\help-include-all-sync.js:281
throw e;
^
`include-all` attempted to `require(E:\myApi\config\passport.js)`, but an error occurred::
Details:TypeError: JwtStrategy requires a function to retrieve jwt from requests (see option jwtFromRequest)
at new JwtStrategy (E:\myApi\node_modules\passport-jwt\lib\strategy.js:39:15)
at Object.<anonymous> (E:\myApi\config\passport.js:68:3)
at Module._compile (module.js:570:32)
passort.js
/**
* Passport configuration file where you should configure strategies
*/
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var JwtStrategy = require('passport-jwt').Strategy;
var EXPIRES_IN_MINUTES = 60 * 24;
var SECRET = process.env.tokenSecret || "4ukI0uIVnB3iI1yxj646fVXSE3ZVk4doZgz6fTbNg7jO41EAtl20J5F7Trtwe7OM";
var ALGORITHM = "HS256";
var ISSUER = "nozus.com";
var AUDIENCE = "nozus.com";
/**
* Configuration object for local strategy
*/
var LOCAL_STRATEGY_CONFIG = {
usernameField: 'email',
passwordField: 'password',
passReqToCallback: false
};
/**
* Configuration object for JWT strategy
*/
var JWT_STRATEGY_CONFIG = {
secretOrKey: SECRET,
issuer : ISSUER,
audience: AUDIENCE,
passReqToCallback: false
};
/**
* Triggers when user authenticates via local strategy
*/
function _onLocalStrategyAuth(email, password, next) {
User.findOne({email: email})
.exec(function (error, user) {
if (error) return next(error, false, {});
if (!user) return next(null, false, {
code: 'E_USER_NOT_FOUND',
message: email + ' is not found'
});
// TODO: replace with new cipher service type
if (!CipherService.comparePassword(password, user))
return next(null, false, {
code: 'E_WRONG_PASSWORD',
message: 'Password is wrong'
});
return next(null, user, {});
});
}
/**
* Triggers when user authenticates via JWT strategy
*/
function _onJwtStrategyAuth(payload, next) {
var user = payload.user;
return next(null, user, {});
}
passport.use(
new LocalStrategy(LOCAL_STRATEGY_CONFIG, _onLocalStrategyAuth));
passport.use(
new JwtStrategy(JWT_STRATEGY_CONFIG, _onJwtStrategyAuth));
module.exports.jwtSettings = {
expiresInMinutes: EXPIRES_IN_MINUTES,
secret: SECRET,
algorithm : ALGORITHM,
issuer : ISSUER,
audience : AUDIENCE
};
Options passed to JwtStrategy constructor must have a key jwtFromRequest.
It is missing in your code.
See https://github.com/themikenicholson/passport-jwt#usage
jwtFromRequest (REQUIRED) Function that accepts a request as the only
parameter and returns either the JWT as a string or null. See
Extracting the JWT from the request for more details.

Call controller on afterCreate

I have the following code for my Sessions model:
module.exports = {
attributes: {
},
afterCreate: function(value,next) {
next();
}
};
And the following Sessions Controller:
module.exports = {
saveSession: function(res,req) {
console.log('in save');
}
};
I want to save a value to a user's session afterCreate
How can I call the saveSession function from my model? I tried Sessions.saveSession() but it doesn't work.
I don't think you need a session model and it's not a good idea to call a controller method directly.
I'd recommend just set req.session when you're trying to save the session and it'll be auto-saved when you respond from that controller action.
afterCreate will never have access to req unless you pass it down which I wouldn't recommend.
The pattern is something like:
{
// …
login: function (req,res) {
User.findOne({
username: req.param('username'),
password: req.param('password')
}).exec(function (err, user) {
if (err) return res.serverError(err);
if (!user) return res.view('/login');
req.session.user = user.toJSON();
return res.redirect('/dashboard');
});
}
// ...
I think that you want to save a value to a cookie or create another database record am i correct?
If so, you dont need to call a controller action from the model (not recommended), you just need to create a new record or save the value to the cookie, here are some alternatives that i see possible in your scenario.
creating another record:
// on models/YourModel
module.exports = {
attributes: {
},
afterCreate: function(newlyInsertedRecord,next) {
ModelOrResource.create({
param1: newlyInsertedRecord.attributeYouWant,
param2: value2
// and so on
}).exec(function(err, recordCreated){
if(err) return next(err);
// do somethign with recordCreated if you need to
// ...
next();
})
}
};
Saving a value to a cookie:
module.exports = {
attributes: {
},
afterCreate: function(newlyInsertedRecord, next) {
// do some other stuff not related to calling a controller action ;)
next();
}
};
This code was retrived from snippets from my own projects, so it should work on sails 0.9.x
Hope it helps!

sails.js + passport.js : managing sessions

I am trying to implement a facebook connection in sails using passport. Therefore, I've created a passport.js file in my services folder, the code is given below. It looks like the login is done successfully, however the user serialization doesn't seem to work as the console.log that I put in it never appears in the console and I cannot access the user id trhough req.user once the user is supposed to be logged in. Did anyone managed to get passport working with sails?
var passport = require('passport')
, FacebookStrategy = require('passport-facebook').Strategy,
bcrypt = require('bcrypt');
// helper functions
function findById(id, fn) {
User.findOne(id).done( function(err, user){
if (err){
return fn(null, null);
}else{
return fn(null, user);
}
});
}
function findByUsername(u, fn) {
User.findOne({
username: u
}).done(function(err, user) {
// Error handling
if (err) {
return fn(null, null);
// The User was found successfully!
}else{
return fn(null, user);
}
});
}
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing.
passport.serializeUser(function(user, done) {
console.log("utilisateur serilizé!");
done(null, user.uid);
});
passport.deserializeUser(function(id, done) {
//console.log("coucou");
findById(id, function (err, user) {
done(err, user);
});
});
// Use the LocalStrategy within Passport.
// Strategies in passport require a `verify` function, which accept
// credentials (in this case, a username and password), and invoke a callback
// with a user object.
// using https://gist.github.com/theangryangel/5060446
// as an example
passport.use(new FacebookStrategy({
clientID: 'XXX',
clientSecret: 'XXX',
callbackURL: "http://localhost:1337/callback"
},
function(accessToken, refreshToken, profile, done) {
User.findOne({uid: profile.id}, function(err, user) {
if (err) { return done(err); }
if (user) {
//console.log('momo');
User.update({uid : user.uid},{token : accessToken},function(){done(null, user);});
} else {
console.log(profile);
var user_data = {
token : accessToken
, provider: profile.provider
, alias: profile.username
, uid: profile.id
, created: new Date().getTime()
, name: {
first: profile.name.givenName
, last: profile.name.familyName
}
, alerts: {
email: true
, mobile: false
, features: true
}
};
console.log(user_data);
User.create(user_data).done(function(err, user) {
console.log(err);
if(err) { console.log("err");throw err; }
done(null, user);
});
}
});
}
));
While I do not have a direct answer for you, this was extremely useful to when getting it to work with GitHub OAuth: https://github.com/stefanbuck/sails-social-auth-example/blob/master/config/middleware.js
This is an entire, recent, Sails.js application implementing passport so it might be of use to you to side-by-side the two in the debugger and find out what is going on.
Check out this easy and full implementation for sails.js with passport.js supporting both Email, Twitter and Facebook.
https://github.com/bmustata/sails-auth-super-template