I am needing to check that the username is not taken before a user creates an account. I am attempting to build a one page application with AJAX and passport.
How would I go about checking to see if the username is taken BEFORE posting when a user is registering using passport?
Not sure where to start.
Thanks!
app.post("/quiz", function(req, res){
var newUser = new User({username: req.body.username, datapoint: req.body.datapoint});
User.register(newUser, req.body.password, function(err, user){
// if(res.error){
if(err){
req.flash("error", err.message);
res.redirect('back')
return res.render("quiz");
} else {
passport.authenticate("local")(req, res, function(){
// req.flash("success", "Welcome to JobQuiz " + user.username);
res.redirect("jobquiz");
console.log(req.body.datapoint)
});
}
});
});
First find if the user exists using findOne function. If user exists then you can just return an error
app.post("/quiz", function(req, res){
User.findOne({username: req.body.username}, function(err, user){
if(err) {//error handling... }
if(user) { //user already exists. throw error accordingly}
//continue with your registration logic
var newUser = new User({username: req.body.username, datapoint: req.body.datapoint});
User.register(newUser, req.body.password, function(err, user){
// if(res.error){
if(err){
req.flash("error", err.message);
res.redirect('back')
return res.render("quiz");
} else {
passport.authenticate("local")(req, res, function(){
// req.flash("success", "Welcome to JobQuiz " + user.username);
res.redirect("jobquiz");
console.log(req.body.datapoint)
});
}
});
});
});
The above code is handled in a callback. If you want to avoid callback hell you can use promises
If you're using passport-local-mongoose, User.register() will check and give the error message:
A user with the given username is already registered
You can view the other error codes here
Related
I am migrating an application from parse.com to buddy.com. One of the caveats of the migration was that Parse.User.current() is no longer available on buddy.com, instead you have to get the user and session token from the request itself: https://github.com/ParsePlatform/Parse-Server/wiki/Compatibility-with-Hosted-Parse#no-current-user
The application I am migrating has a logoutUser method that I am attempting to migrate:
Parse.Cloud.define("logoutUser", function(request, response) {
Parse.User.logOut().then(
function onSuccess(result){
response.success(result);
},
function onError(error) {
response.error(error);
}
)
});
now I am attempting to do this in the new style, but am receiving an error. (NOTE: This is cloud code not a nodejs environment)
{
"code":"500",
"error":"Error: There is no current user user on a node.js server environment."
}
New implementation:
function logoutUser(request, response) {
var user = request.user;
var sessionToken = user.getSessionToken();
Parse.User.logOut({ sessionToken }).then(
function onSuccess(result){
response.success(result);
},
function onError(error) {
response.error(error);
}
)
}
Parse.Cloud.define("logoutUser", function(request, response) {
logoutUser(request, response);
});
Suggestions on how to correctly log out users in the Parse on Buddy cloud code?
You could fetch user's session or sessions and delete it / them:
var query = new Parse.Query("_Session");
query.descending('createdAt');
query.equalTo('user', {__type:"Pointer", className:"_User", objectId:"idhere"});
query.first({
useMasterKey: true
}).then(function(session) {
var sessions = [];
sessions.push(session);
Parse.Object.destroyAll(sessions);
}, function (err) {
console.log("Internal error " + err);
});
OR for more tokens you could use find instead of first like:
var query = new Parse.Query("_Session");
query.equalTo('user', {__type:"Pointer", className:"_User", objectId:"idhere"});
query.find({
useMasterKey: true
}).then(function(sessions) {
Parse.Object.destroyAll(sessions);
}, function (err) {
console.log("Internal error " + err);
});
The above will mostly delete or tokens related to the given user. If you wish to delete only tokens used for login, and not for signup or upgrade, then you could put into your query:
query.equalTo('createdWith', { action: 'login', authProvider: 'password'});
As far as i know, deleting a user's last used for login token, then he is logged-out.
To add to the above, if you pass up the user's session key to the Cloud Code function via the X-Parse-Session-Token header, you can use the populated request.user object in the session query directly, instead of the user's ID.
I'm using parse on node. I have an express app, and a JS browser app, that is hosted off the express server.
At the moment the app has it's own login. It logs the user in on the client, and the client remains logged in.
I want to be able to log the client in via an express route /login. When they log in via this route, i want to log them in on the client side.
I have poured over documentation on this but I have struggled to find any real examples of how this is all done.
Here is some code i have found:
var cookieSession = require('cookie-session'),
// I added this require as it seems the code is using it;
session = require('express-session');
app.use(cookieSession({
name: COOKIE_NAME,
secret: "SECRET_SIGNING_KEY",
maxAge: 15724800000
}));
//
// This will add req.user if they are logged in;
//
app.use(function (req, res, next) {
Parse.Cloud.httpRequest({
url: 'http://localhost:1337/parse/users/me',
headers: {
'X-Parse-Application-Id': 'myAppId',
'X-Parse-REST-API-Key': 'myRestAPIKey',
'X-Parse-Session-Token': req.session.token
}
}).then(function (userData) {
req.user = Parse.Object.fromJSON(userData.data);
next();
}).then(null, function () {
return res.redirect('/login');
});
});
//
// login route;
//
app.post('/login', function(req, res) {
Parse.User.logIn(req.body.username, req.body.password).then(function(user) {
req.session.user = user;
req.session.token = user.getSessionToken();
res.redirect('/');
}, function(error) {
req.session = null;
res.render('login', { flash: error.message });
});
});
//
// and logout.
//
app.post('/logout', function(req, res) {
req.session = null;
res.redirect('/');
});
This looks pretty good, but this won't add a session on the client? How do parse the server login down to the client; Do i pass the session Token and use it on the client?
//
// If i call this code in the browser, i want the logged in user;
//
var current_user = Parse.User.current();
I have been unable to find any real code on-line that demonstrates all of this in the best-practice manner.
Is this the 'best practice' known solution or is there a better way of doing this?
I wrote a simple function in an angularJS application for signing up new users:
$scope.registerUser = function(username, password) {
var user = new Parse.User();
user.set("username", username);
user.set("email", username);
user.set("password", password);
user.signUp(null, {
success: function(result) {
console.log(result);
$scope.registerUserSuccess = true;
$scope.registerUserError = false;
$scope.registerUserSuccessMessage = "You have successfully registered!";
$scope.$apply();
$timeout(function(){
$state.go("user");
}, 1000);
},
error: function(user, error) {
$scope.registerUserError = true;
$scope.registerUserSuccess = false;
$scope.registerUserErrorMessage = "Error: [" + error.code + "] " + error.message;
$scope.$apply();
}
});
Initially it worked fine, but when I deleted all the users directly through Parse.com, I can't sign up new users using this function anymore. Each time I get error 209 invalid session token. Here's a screenshot of my Parse database:
I've googled the error message and the solution is always to log out the current user. However, if no users exist this isn't an action I can possibly take.
So I would not only like to fix this problem, but also know how to prevent it in the future so my application can be used safely.
Edit: I created a user directly in Parse.com, wrote a function to log in that user, but got the same error. I am completely stuck until this session issue is resolved.
delete all your session tokens, and anything else Parse related really, from local storage:
if needed turn off legacy session tokens, and follow migration tutorial from scratch:
I encountered this same error when building apps with react native using back4app. to clear anything Parse related, from local storage:
add
import { AsyncStorage } from "react-native";
in to the page and Use
AsyncStorage.clear();
See Example Below:
import { AsyncStorage } from "react-native";
import Parse from "parse/react-native";
// Initialize Parse SDK
Parse.setAsyncStorage(AsyncStorage);
Parse.serverURL = "https://parseapi.back4app.com"; // This is your Server URL
Parse.initialize(
"APPLICATION_ID_HERE", // This is your Application ID
"JAVASCRIPT_KEY_HERE" // This is your Javascript key
);
.........
_handleSignup = () => {
// Pass the username, email and password to Signup function
const user = new Parse.User();
user.set("username", "username);
user.set("email", "email");
user.set("password", "password");
user.signUp().then(user => {
AsyncStorage.clear();
if (condition) {
Alert.alert(
"Successful!",
"Signin Successful! Log in to your account.",
[
{
text: "Proceed",
onPress: () => {
//in this example, i navigated back to my login screen
this.props.navigation.navigate("LoginScreen");
}
}
],
{ cancelable: false }
);
}
})
.catch(error => {
Alert.alert("" +error);
});
};
I've implemented passport authentication in an express application. And serialization is done on the username.
Passport:
passport.use(new passportLocal.Strategy(verifyCredentials));
function verifyCredentials(username, password, done){
//Check from the cached data
if(user has already logged in){
//Destroy session corresponding to `username'
}
else {
//Verify the credentials
if(verified){
//Cached username here
done(null, user.username);
} else {
done(404, null)
}
}
}
passport.serializeUser(function(username, done){
done(null, username);
});
passport.deserializeUser(function(id, done){
done(null, {username:username})
});
Routes
router.get('/login', function(req, res){
res.render('login');
});
router.post('/login', passport.authenticate('local'), function(req, res){
res.redirect('/');
});
router.get('/', function(req, res){
res.render('index', {
isAuthenticated : req.isAuthenticated(),
user : {name : req.body.username}
});
});
Scenario:
A user logs in from a tab - we get the session created
User does a GET /login in a new tab and logs in using the same credentials again
Requirement:
User is prompted that the user is already logged in and given an option to invalidate the previous session
If user chooses to invalidate, destroy the session corresponding to `username'
After the prompt, how do I go about invalidation?
Any help is appreciated. Much thanks!
I understand the reason to have the business logic in both client and server, but I don't understand well how to do that in some situations. Here for example:
// client/client.js
// hnadling click event on the Create Accounts button
Template.homecontent.events({
'click #btnCreateAccount': function (event, template) {
var userEmail = template.find('#email').value,
userName = template.find('#newusername').value,
password = template.find('#newpassword').value,
password2 = template.find('#password2').value,
name = template.find('#fullname').value;
validates = true;
//do some validation here
if(password != password2) {
validates = false;
}
if(validates === true) {
Accounts.createUser({
username: userName,
email: userEmail,
password: password,
profile: {
name: name
}
}, function (error) {
if (error) {
console.log("Cannot create user");
}
});
}
}
});
Since the validation is on the client only, it can easily be bypassed.
But there's a problem here: this is triggered by a user event, so I'm not sure what's the best way to have this code running on client & server.
You may be looking for something like Meteor.methods();, which allows you to define functions on the server that the client can call using Meteor.call(). You could provide a validation function and a user save function on the server, and call them both from the client, passing in the form data.
What I have done in the past is (on the client) I have a userFormParse() function that takes a form object and parses it into an object that can be passed into a server side validation function. I use the same userFormParse function for user edit and creation forms.
The validation function returns an error object to the client, or, if it's all valid data, I'll pass the data object on to a userCreateWithRole function (I usually always have roles assigned to users).
On the server:
Meteor.methods({
'createUserWithRole': function(data, role) {
var userId;
Meteor.call('createUserNoRole', data, function(err, result) {
if (err) {
return err;
}
Roles.addUsersToRoles(result, role);
return userId = result;
});
return userId;
},
'createUserNoRole': function(data) {
//Do server side validation
return Accounts.createUser({
email: data.email,
password: data.password,
profile: data.profile
});
}
});
And then on the client:
Template.userSignup.events({
'submit #userSignup': function(event) {
var data, validationErrors;
event.preventDefault();
data = userInputParse($(event.target)); //this function parses form into user object that can be inserted
validationErrors = userObjectValidate(data); //this function takes and does client side validation on the user object.
data.profile.status = 0;
if (validationErrors) {
//Show the user the validation errors
} else {
return Meteor.call('createUserWithRole', data, 'standard', function(err, userId) {
if (!err) {
//User created!!
} else {
//Insertion Error
}
});
}
}
});
That code is conceptual and untested :)
You should be doing it on server side, using Accounts.onCreateUser
The previous answers are not really exact.
Creating and using a Meteor method won't stop users to call the Accounts.createUser from the console for example. Therefore you also need to prevent the creation of users on the client :
Accounts.config({
forbidClientAccountCreation : true
});
You might want to look into Accounts.validateNewUser.
Example (taken from the docs):
Accounts.validateNewUser(function (user) {
if (user.username && user.username.length >= 3)
return true;
throw new Meteor.Error(403, "Username must have at least 3 characters");
});