Has anyone experienced extending the built-in model "user" in StrongLoop via Client?
The thing is, I can't skip the verification for the newly created clients. I have declared the Client with the option "emailVerificationRequired": false, but still I can't log in.
Error:
error: Object code: "LOGIN_FAILED_EMAIL_NOT_VERIFIED" message: "login
failed as the email has not been verified" name: "Error" stack:
"Error: login failed as the email has not been verified↵ at
C:\xampp\htdocs\loopback-getting-started\node_modules\loopback\common\models\user.js:248:21↵
at
C:\xampp\htdocs\loopback-getting-started\node_modules\loopback\common\models\user.js:312:9"
status: 401 statusCode: 401
If you don't want email verification PUT below code in
model-config.json
"user": {
"dataSource": "YOUR DATASOURCE",
"public": true,
"options": {
"emailVerificationRequired": false
},
"$promise": {},
"$resolved": true}
And other thing is, you manually stop verification doing below logic.
For example if users created by Admin.
user.beforeRemote('create' ,function(ctx, modelInstance, next)
{
if(ctx.req.query.key == "admin") // if users created by Admin.
{
ctx.args.data.emailVerified = 1;
}
next();
});
IN afterRemote
user.afterRemote('create', function(context, user, next) {
if(!user.emailVerified)
{
console.log('> user.afterRemote triggered');
var options = {
type: 'email',
to: user.email,
from: 'youremail,
subject: 'Thanks for registering.',
template: path.resolve(__dirname, '../../server/views/verify.ejs'),
redirect: '/verified',
user: user
};
user.verify(options, function(err, response) {
if (err) return next(err);
context.res.render('response', {
title: 'Signed up successfully',
content: 'Please check your email and click on the verification link ' +
'before logging in.',
redirectTo: '/',
redirectToLinkText: 'Log in'
});
});
}
else
{
next();
}
});
Related
this is my login post method in the reactjs frontend
const login = () => {
Axios.post("http://localhost:3001/api/users/login", {
email: values.email,
password: values.password,
}).then((response) => {
console.log(response.data);
}).catch(err =>{
console.log(err)
})
};
this is my expressjs server side, here i have login post method for reactjs frontend, where iam on response i want to send token to set in cookie whenever user post on login method, below is code for login post method
login: (req, res) => {
const body = req.body;
console.log("req.body :", req.body);
getUserByEmail(body.email, (err, results) => {
console.log("results :", results);
if (err) {
console.log(err);
return;
}
if (!results) {
res.json({
status: "failure",
msg: "Invalid email or password",
});
}
const result = compareSync(body.password, results.password);
const SECRET_KEY = "xyz123";
if (result) {
results.password = undefined;
const jsontoken = sign({ result: results }, SECRET_KEY, {
expiresIn: "1h",
});
// console.log(res)
res.cookie("token", jsontoken, {
httpOnly: true,
domain: "http://localhost:3000/login",
});
return res.json({
status: "Success",
msg: "login Successfully",
token: jsontoken,
});
} else {
return res.json({
status: "failure",
msg: "Invalid email or password",
});
}
});
},
What you could do, that is actually more secure, is tell the browser using headers on the response to create a cookie.
There is a header in HTTP called Set-Cookie, which is responsible to do just that, you can read more about it here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie.
The way you add it to your request on express is by calling the res.cookie function on your express request handler. I would suggest telling the cookie to be httpOnly in order for it to not be accessible through JS code, this is just a way to avoid XSS attacks.
Here you have an example to how to achieve that:
res.cookie('token', jsontoken, { httpOnly: true });
Then in order to access the cookie, you would need to use the cookieParser middleware which is responsible in putting all the cookies the client sent in req.cookies.
You use it this way:
app.use(express.cookieParser());
When I have tried to implement Google authentication in my site, using sails JavaScript, and MySQL getting error. I have using passport and passport-Google-auth Strategy. Problem is not getting data to my site from Google
My Express Config(express.js) file is like below,
var passport = require('passport')
, GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
var verifyHandler = function(token, tokenSecret, profile, done) {
process.nextTick(function() {
console.log(profile)
User.findOne({uid: profile.id}, function(err, user) {
if (user) {
return done(null, user);
} else {
var data = {
provider: profile.provider,
uid: profile.id,
name: profile.displayName
};
if (profile.emails && profile.emails[0] && profile.emails[0].value) {
data.email = profile.emails[0].value;
}
if (profile.name && profile.name.givenName) {
data.firstname = profile.name.givenName;
}
if (profile.name && profile.name.familyName) {
data.lastname = profile.name.familyName;
}
User.create(data, function(err, user) {
return done(err, user);
});
}
});
});
};
passport.serializeUser(function(user, done) {
console.log(user)
done(null, user.uid);
});
passport.deserializeUser(function(uid, done) {
User.findOne({uid: uid}, function(err, user) {
done(err, user);
});
});
module.exports.http = {
customMiddleware: function(app) {
passport.use(new GoogleStrategy({
clientID: 'Client Id here',
clientSecret: 'Secret key here',
callbackURL: 'http://localhost:1337/auth/google/callback'
}, verifyHandler));
app.use(passport.initialize());
app.use(passport.session());
}
};
module.exports.cache = {
// The number of seconds to cache files being served from disk
// (only works in production mode)
maxAge: 31557600000
};
module.exports.userlogin = {
userModel: 'user'
};
And My Auth Controller I have added code like below,
google: function(req, res) {
passport.authenticate('google',{
failureRedirect: '/login', scope: ['profile', 'email']
}, function(err, user) {
req.logIn(user, function(err) {
if (err) {
console.log(err);
res.view('500');
return;
}
res.redirect('/');
return;
});
})(req, res);
},
You didn't post your code, so we can't find the exact problem :/
I usually use this method for google/facebook authentication with sails.js.
I follow at first this documentation to add the authentication buttons in the frontend:
https://developers.google.com/identity/sign-in/web/sign-in
Then I post the token that I got from google/facebook to the backend where I can check if the user is banned or whatever... If everything is correct, I create an account for him in the database, I send him his password to his email and finally authenticate him using sessions
(req.session.userId = createdUser.id)
In the next time the user can log in using his email and password or just using google. And both options lead him to the same account :D
My Sails.js function in the authentication controller:
googleAuth: function(req, res) {
if (_.isUndefined(req.param('googleToken'))) {
return res.json({
success: false,
msg: 'Error! Please post your google token'
});
}
var urlToRq = "https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=" + req.param('googleToken');
// Get information about the google user with the specified access token.
request.get({url: urlToRq}, function(err, response, body) {
if(err) {
return res.json({
success: false,
msg: 'Server Error'
});
}
var receivedData = JSON.parse(body);
var userId = receivedData.sub;
var userEmail = receivedData.email;
var emailVerified = receivedData.email_verified;
var userName = receivedData.name;
var userPicture = receivedData.picture;
if (emailVerified == false) {
return res.json({
success: false,
msg: 'Your email is not verified'
});
}
else {
// AUTHENTICATION VERIFIED, YOU CAN SAVE THE CONNECTED USER IN A SESSION, OR ADD HIM TO THE DATABASE AS A NEW ACCOUNT, OR CHECK IF HE HAS A PREVIOUS ACCOUNT OR WHATEVER YOU WANT...
}
});
},
Of course don't forget to run npm install request --save
If anyone needs the facebookAuth function just tell me :D I will post it for you :)
I've tried to find similar questions with this problem and while there are lots with the same error I don't think I'm getting the error for the same reason.
I have a route, POST /api/servers which creates a server listing in the database. I don't know if it is wrong to include so much logic in an express route but it seems to be working up until this error I keep getting.
Basically, if creating a server that already exists (address + port combo) then the route sends back an error with 'SERVER ALREADY EXISTS' as the message in JSON, but when ever this happens if I try to create a server that already exists in postman I do get the JSON back as expected but my server crashes with 'Cant set headers after they are sent'
router.post('/servers', passport.authenticate('jwt', { session: false }), function(req, res) {
//This is very lazy validation, to do in future version, create error object
// which accumulates the problems with the form and can return all problems at
// once to user
if (!req.body.name) {
return res.json({success: false, message: 'NAME REQUIRED'})
}
if (!req.body.address) {
return res.json({success: false, message: 'ADDRESS REQUIRED'})
}
if (!req.body.image) {
return res.json({success: false, message: 'IMAGE REQUIRED'})
}
if (!req.body.description) {
return res.json({success: false, message: 'DESCRIPTION REQUIRED'})
}
var newServer = new Server();
newServer.name = req.body.name;
newServer.address = req.body.address;
//if a custom port is specified, uses it otherwise defaults to 25565
if (req.body.port) {
newServer.port = req.body.port;
} else {
newServer.port = 25565;
}
//Check if server with address and port not already exists
Server.findOne({ address: newServer.address, port: newServer.port }, function(err, server) {
if (err) {
return res.json({success: false, message: 'Internal Error'});
} else if (server) {
return res.json({success: false, message: 'SERVER ALREADY EXISTS'});
}
});
newServer.image = req.body.image;
newServer.description = req.body.description;
newServer.owner = req.user._id; // Gives the current user id
if (req.body.votifier_enabled) {
//If votifier is enabled on this server
newServer.votifier.enabled = true;
if (!req.body.votifier_address) {
return res.json({success: false, message: 'VOTIFIER ADDRESS REQUIRED'})
}
//if custom votifier port specified, it is used otherwise defaults to 8192
if (req.body.votifier_port) {
newServer.votifier.port = req.body.votifier_port;
} else {
newServer.votifier.port = 8192;
}
if (!req.body.votifier_pubkey) {
return res.json({success: false, message: 'VOTIFIER PUBLIC KEY REQUIRED'})
}
newServer.votifier.address = req.body.votifier_address;
newServer.votifier.pubkey = req.body.votifier_pubkey;
//Check if votifier server with address and port not already exists
Server.findOne({ votifier: { address: newServer.votifier.address, port: newServer.votifier.port } }, function(err, server) {
if (err) {
return res.json({success: false, message: 'Internal Error'});
} else if (server) {
return res.json({success: false, message: 'VOTIFIER ALREADY EXISTS'});
}
});
};
if(!req.body.hidden) {
newServer.hidden = false;
} else {
newServer.hidden = req.body.hidden;
};
console.log('passed all verification');
//Lastly: Verify that the server really does belong to this user,
//checks that string config.VERIFY_OWNERSHIP_STRING is in servers motd
mcping(req.body.address, req.body.port, function(err, stats) {
if (err) {
//Problem connecting to server
return res.json({success: false, message: 'SERVER CONNECTION FAILED: Is your server online?'})
} else {
console.log('pinged server successfully')
var status = stats;
if (status.description.text.includes(config.VERIFY_OWNERSHIP_STRING)){
newServer.status.online = true;
newServer.status.motd = status.description.text;
newServer.status.currPlayers = status.players.online;
newServer.status.maxPlayers = status.players.max;
//Verification successful, server does belong to this user
//Validation complete and newServer object created! Now to save it to database
newServer.save(function(err, server) {
console.log('attempted to save the server');
if (err) {
//Theoretically - this should not happen unless theres an internal error
console.log(err.errors);
return res.json({success: false, message: err});
} else if (server) {
console.log('save successful')
return res.json({success: true, _id: server._id});
}
});
} else {
return res.json({success: false, message: "SERVER VALIDATION FAILED: Is '" + config.VERIFY_OWNERSHIP_STRING + "' in your motd?"});
}
}
}, 3000);
})
This is the route that is causing the problem, I think the problem is because even though the server already exists so the JSON message is being sent back to say SERVER ALREADY EXISTS all the console.logs below still are called, including in the mongoose save function.
I thought that res.json is supposed to end the request response cycle? So I was under the impression it's okay to have so many res.jsons called but now I'm thinking this is the problem
Thanks for any help and sorry for the confusing code!
I am trying to build a user signup api using Passport, MySql, NodeJS and Sequelize. The only problem that i face is that when a user has signed up once and he tries to sign up again with the same email user is thrown a 401 Unauthorized Error instead of the user object. When i tried to debug the same the response that i was getting from the server was this
[object SequelizeInstance:users]. The files have been mentioned below. Thanks a tonnn in advance!!!.
Passport.js file:
var LocalStrategy = require('passport-local').Strategy;
var mysql = require('mysql');
var Model = require('../models/models.js');
// expose this function to our app using module.exports
module.exports = function(passport) {
// =========================================================================
// passport session setup ==================================================
// =========================================================================
// required for persistent login sessions
// passport needs ability to serialize and unserialize users out of session
// used to serialize the user for the session
passport.serializeUser(function(user, done) {
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser(function(id, done) {
connection.query("select * from users where id = " + id, function(err, rows) {
done(err, rows[0]);
});
});
// =========================================================================
// LOCAL SIGNUP ============================================================
// =========================================================================
// we are using named strategies since we have one for login and one for signup
// by default, if there was no name, it would just be called 'local'
passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) {
Model.User.findOne({
where: {
email: email
}
}).then(function(user) {
if (user == null) {
Model.User.create({
email: email,
password: password
}).then(function(user) {
return done(null, user);
}).catch(function(err) {
return done(null, err);
});
} else {
return done(null, false);
}
})
}));
};
The Signup api:
router.post('/signup', passport.authenticate('local-signup'), function(req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
console.log(req.user);
if(req.user){
res.send({
success: true,
response: 'signup successful'
});
} else {
res.send({
success: false,
response: 'Email already in use'
});
}
});
The User model is:
//models/users.js
var Sequelize = require('sequelize')
var attributes = {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
},
created_by: {
type: Sequelize.INTEGER
}
}
var options = {
// Add the timestamps attributes (updatedAt, createdAt)
timestamps: true,
// don't delete database entries but set the newly added attribute deletedAt
// to the current date (when deletion was done). paranoid will only work if
// timestamps are enabled
paranoid: true,
// don't use camelcase for automatically added attributes but underscore style
// so updatedAt will be updated_at
underscored: true,
// disable the modification of table names; By default, sequelize will automatically
// transform all passed model names (first parameter of define) into plural.
// if you don't want that, set the following
freezeTableName: true,
// define the table's name
tableName: 'users'
}
module.exports.attributes = attributes
module.exports.options = options
The automated table creation model script is:
// models/models.js
var UserMeta = require('./users.js'),
connection = require('./index.js')
var User = connection.define('users', UserMeta.attributes, UserMeta.options)
// force: true will drop the table if it already exists
User.sync({
force: true,
match: /_servernew$/
}).then(function() {
// Table created
return User.create();
});
// you can define relationships here
module.exports.User = User;
So i came up with the solution. The following code needs to be changed.
router.post('/signup', function(req, res, next) {
passport.authenticate('local-signup', function(err, user, info) {
if(user){
req.logIn(user, function(err) {
if (err) {
return next(err);
} else {
res.send({
success: true,
response: 'signup successful'
});
}
});
}
if(!user){
res.send({
success: false,
response: 'Authentication Failed'
});
}
if(err){
res.send({
success: false,
response: 'Authentication failed'
})
}
})(req, res, next);
});
and the passport.js code should be like this.
// =========================================================================
// LOCAL SIGNUP ============================================================
// =========================================================================
// we are using named strategies since we have one for login and one for signup
// by default, if there was no name, it would just be called 'local'
passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) {
Model.User.findOne({
where: {
email: email
}
}).then(function(user, err) {
console.log('I entered'+user);
console.log('I entered'+err);
if(err) {
console.log(err);
return done(null, false);
}
if(user == null) {
Model.User.create({
email: email,
password: password
}).then(function(user) {
return done(null, user);
}).catch(function(err) {
return done(null, err);
});
}
if(user){
return done(null, false);
}
})
}));
It will work just like a charm :D.
I am trying to use passport js for authentication in my local mysql database. I am using postman extension to test the application.
I am sending two fields i.e. username and password for authentication. When any one of the field is blank then response is shown in json format as
{
"message": "Missing credentials",
"user": false
}
But when I pass values for both the fields I get 500 internal server error.
error: Sending 500 ("Server Error") response:
TypeError: Cannot read property 'message' of undefined
at d:\Test\api\controllers\AuthController.js:25:23
at Strategy.strategy.error (d:\Test\node_modules\passport\lib\middleware\authenticate.js:333:18)
at Strategy.authenticate (d:\Test\node_modules\passport-local\lib\strategy.js:94:17)
at attempt (d:\Test\node_modules\passport\lib\middleware\authenticate.js:341:16)
at authenticate (d:\Test\node_modules\passport\lib\middleware\authenticate.js:342:7)
at Object.module.exports.login (d:\Test\api\controllers\AuthController.js:37:7)
at bound (C:\Users*\AppData\Roaming\npm\node_modules\sails\node_modules\lodash\dist\lodash.js:729:21)
at routeTargetFnWrapper (C:\Users*\AppData\Roaming\npm\node_modules\sails\lib\router\bind.js:179:5)
at callbacks (C:\Users*\AppData\Roaming\npm\node_modules\sails\node_modules\express\lib\router\index.js:164:37)
at param (C:\Users*\AppData\Roaming\npm\node_modules\sails\node_modules\express\lib\router\index.js:138:11)
at pass (C:\Users*\AppData\Roaming\npm\node_modules\sails\node_modules\express\lib\router\index.js:145:5)
at nextRoute (C:\Users*\AppData\Roaming\npm\node_modules\sails\node_modules\express\lib\router\index.js:100:7)
at callbacks (C:\Users*\AppData\Roaming\npm\node_modules\sails\node_modules\express\lib\router\index.js:167:11)
at C:\Users*\AppData\Roaming\npm\node_modules\sails\lib\router\bind.js:187:7
at alwaysAllow (C:\Users*\AppData\Roaming\npm\node_modules\sails\lib\hooks\policies\index.js:207:11)
at routeTargetFnWrapper (C:\Users*\AppData\Roaming\npm\node_modules\sails\lib\router\bind.js:179:5) [TypeError: Cannot read property 'message' of undefined]**
Below is my AuthController
var passport=require('passport');
login:function(req,res){
passport.authenticate('local', function(err, user, info) {
if ((err) || (!user)) {
return res.send({
message:info.message,
user: user
});
}
req.logIn(user, function(err) {
if (err) res.send(err);
return res.send({
message:"User Loged In",//info.message,
user: user
});
});
})(req, res);
}
};
I am using the below model for testing
module.exports = {
tableName: 'users',
connection:'TestDB',
autoCreatedAt:false,
autoUpdatedAt:false,
attributes: {
username:{
type:'string',
required:true
},
password:{
type:'string',
required:true
},
toJSON: function() {
var obj = this.toObject();
delete obj.password;
return obj;
}
}
};
The table which contains the username and password also has other fields like country. Is there any way I can authenticate it using passport.
did you do the sixth step of this link
module.exports.http = {
middleware: {
passportInit : require('passport').initialize(),
passportSession : require('passport').session(),
order: [
'startRequestTimer',
'cookieParser',
'session',
'passportInit',
'passportSession',
'myRequestLogger',
'bodyParser',
'handleBodyParserError',
'compress',
'methodOverride',
'poweredBy',
'router',
'www',
'favicon',
'404',
'500'
],
}
};
maybe you want to see sails-hook-sanpassport, is easy and fast