Meteor - Account.createUser in client and server - validation

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");
});

Related

Getting UserID from the Session In Extjs in some other js files of same App

In other way I want to access
(One functions Variable in another function in some other file of same application)
I'm trying to retrieve the UserID i got it from the session in some other .js files of a same application, How will I be able to do it?
This is my
session.js
function SessRequest() {
};
SessRequest.prototype.get = function (url, params, payload, session, cb, scope) {
var data = {};
if (payload) {
data.payload = payload;
}
if (url.indexOf('?') > -1) {
url = url + '&logonID=' + session.user.userID;
} else {
url = url + '?logonID=' + session.user.userID;
}
cb.call(scope, result.message, resp);
});
SessRequest.prototype.post = function (url, params, payload, session, cb, scope) {
params.logonID = session.user.userID;
rest.post(url, {data: params}).on('complete', function (data) {
cb.call(scope, 0, data);
});
};
Now This session.user.userID, I want this value to retrieve in a grid.js file of the same application, Can anyone help me in this ?
say me how to retrieve it and print it on console? If possible.
Please ask for any extra thing if My question is not clear?
thanks in advance
You could create a singleton class which holds the userid information.
Ext.define('APP.SessionInformation', {
singleton: true,
userid: null
});
When you retrived the information after successful login, you set it in the singleton.
APP.SessionInformation.userid = session.user.userID;
Now you are able to access it everywhere in you application by APP.SessionInformation.userid.

Parse On Buddy Logout User

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.

Parse for javascript - "error 209 invalid session token" when signing up a new user

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);
});
};

Forms validation in Nancy not working with AJAX login requests

I'm trying to implement an extremely simple spike using Nancy as an alternative to ASP.NET MVC.
It should take a username (no password) and provide meaningful error messages on the same login page without requiring a refresh. If login was successful, the response includes the URL to navigate to.
The POCO for the response looks like this:
public class LoginResponseModel
{
public bool IsSuccess { get; set; }
public string RedirectUrl { get; set; }
public string ErrorMessage { get; set; }
}
The JS handler for the login request:
$.ajax({
url: '/login',
type: "POST",
data: { UserName: username }
}).done(function (response) {
if (response.IsSuccess) {
showSuccess();
document.location.href = response.RedirectUrl;
return;
}
showError(response.ErrorMessage);
}).fail(function (msg) {
showError("Unable to process login request: " + msg.statusText);
});
The problem I'm having is with Nancy's Forms-based authentication. I've walked through half a dozen different tutorials which all more or less do the same thing, as well as gone over the Nancy authentication demos. The one thing they all have in common is that they rely on the LoginAndRedirect extension method. I don't want to return a redirect. I want to return a result of the login attempt and let the client handle the navigation.
The IUserMapper implementation I'm using:
public class UserMapper : IUserMapper
{
public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
{
// Don't care who at this point, just want ANY user...
return AuthenticatedUser {UserName = "admin"};
}
}
The relevant part of my LoginModule action:
var result = _userMapper.ValidateUser(input.AccessCode);
if (result.Guid != null) this.Login(UserMapper.GUID_ADMIN, expiry);
return Response.AsJson(result.Response);
but for subsequent requests Context.CurrentUser is always null.
If I add the following method to the Nancy.Demo.Authentication.Forms sample it reproduces the behaviour I'm seeing in my own project, leading me to believe LoginWithoutRedirect doesn't work how I expected.
Get["/login/{name}"] = x =>
{
Guid? userGuid = UserDatabase.ValidateUser(x.Name, "password");
this.LoginWithoutRedirect(userGuid.Value, DateTime.Now.AddYears(2));
return "Logged in as " + x.Name + " now <a href='~/secure'>see if it worked</a>";
};
The problem turns out to be that Context.CurrentUser with FormsAuthentication is dependent upon a cookie which isn't set if you don't return the NancyModule.Login() response.
var result = _userMapper.ValidateUser(input.AccessCode);
if (result.IsSuccess) {
this.LoginWithoutRedirect(result.Guid);
}
return Response.AsJson(result);
In this example, the LoginWithoutRedirect call returns a Response object with the cookie set. To handle this in an Ajax scenario I've had to add a AuthToken property to the LoginAjaxResponse class, then pass the cookie like so:
var result = _userMapper.ValidateUser(input.AccessCode);
var response = Response.AsJson(result);
if (result.IsSuccess) {
var authResult = this.LoginWithoutRedirect(result.Guid);
result.AuthToken = authResult.Cookies[0].Value;
}
return Response.AsJson(result);
On the client, the Ajax response handler changes to (assuming use of jQuery cookie plugin:
$.ajax({
url: '/login',
type: "POST",
data: { UserName: username }
}).done(function (response) {
if (response.IsSuccess) {
showSuccess();
$.cookie("_ncfa", response.AuthToken); // <-- the magic happens here
document.location.href = response.RedirectUrl;
return;
}
showError(response.ErrorMessage);
}).fail(function (msg) {
showError("Unable to process login request: " + msg.statusText);
});
The AuthToken is the GUID which has been encrypted and base64-encoded. Subsequent requests with this.RequiresAuthentication() enabled will first check for this auth token cookie.
If no "_ncfa" cookie is present,the UserMapper's GetUserFromIdentifier() is never called.
If the value in Context.Request.Cookies["_ncfa"] does not result in a valid GUID when base64-decoded and decrypted, GetUserFromIdentifier() is never called.
If GetUserFromIdentifier() isn't called, Context.CurrentUser is never set.
If you want the source for a working example it's on GitHub.
LoginAndRedirect is only one option, there are equivalent methods for not redirecting (LoginWithoutRedirect), or one that picks up on whether it's an AJAX request and handles it appropriately (Login). The same applies to logging out.
This is all covered, in detail, in the documentation.

Doing ajax call on response of another function but not working

I have registration form and i have created three function in jquery
First one is validate the form.
Second one is for checking the email uniqueness with ajax request.
Third one is for creating user this also with ajax request.
My flow on submit event is that first i am calling validation function and then on the response of that function i calling the function to check the email uniqueness on the response of this a an ajax request is done to create a user.
First one is validate the form.
function validateregForm()
{
if($('#u_name').val()=="" || !IsEmail($('#u_email').val()) || $('#u_pwd').val().length<6 || $('#c_pwd').val()!=$('#u_pwd').val())
{
if($('#u_name').val()=="")
{
$('#reg_error1').show();
}
if(!IsEmail($('#u_email').val()))
{
$('#email_msg').remove();
$('#reg_error2').show();
}
if($('#u_pwd').val().length<6)
{
$('#reg_error3').show();
}
if($('#u_pwd').val()!=$('#c_pwd').val())
{
$('#reg_error4').show();
}
return false;
}
else
{
return true ;
}
Second one is for checking the email uniqueness with ajax request.
function chkmail(email) {
var posting=$.post('http://localhost/tv100.info/index.php/user/chkmail',{u_email:$('#u_email').val()});
posting.done(function(data){
if(data=='success')
{
$('#email_error').css('display','none');
$('#email_msg').css('display','block');
return true;
}
if(data=='failure')
{
$('#email_msg').css('display','none');
$('#email_error').css('display','block');
return false;
}
});
}
Third one is for creating user this also with ajax request.
$('#regform').submit(function(event) {
var res=validateregForm()
event.preventDefault();
if(res)
{
var email=chkmail();
}
if(email)
{
$('#loading2').show();
var posting=$.post('http://localhost/tv100.info/index.php/user/create_user',$("#regform").serialize());
posting.done(function(data)
{
$('#loading2').hide();
if(data=="success")
{
$('#reg_panel').append('<span id="reg_msg">Registration successful Now You are logged IN</span>');
$('#overlay').fadeOut(300);
$('#login').html('Logout');
$('#sign_in').hide();
$('#cmmnt_field').show();
}
if(data=="failure")
{
$('#reg_panel').append('<span id="res_msg">Something Went Wrong try again Latter</span>');
}
});
}
});
Just telling the case
if(res)
{
var email=chkmail(); // for getting the result in var email, ajax will wait until the success
}
if(email) // In your case before completing the ajax request, javascript come to this line and won't return true. So it it always go to else part.
You can do the user creation on success of chkmail success part. It will work fine
Error in your first line of validateregForm() function,
change
if($('#u_name').val=="" || !IsEmail($('#u_email').val())
to
if($('#u_name').val() =="" || !IsEmail($('#u_email').val())
^ `.val()` here.
You need to learn about asynchronously and synchronously concepts. Ajax calls are usually Asynchronously. Simple set the paramter async as false of each ajax request and you will get the result. From documentation
async (default: true)
Type: Boolean
By default, all requests are sent asynchronously (i.e. this is set to true by default).
If you need synchronous requests, set this option to false.
Cross-domain requests and dataType: "jsonp" requests do not support synchronous operation.
Note that synchronous requests may temporarily lock the browser, disabling any actions while the request is active.
As of jQuery 1.8, the use of async: false with jqXHR ($.Deferred) is deprecated; you must use the success/error/complete callback options instead of the corresponding methods of the jqXHR object such as jqXHR.done() or the deprecated jqXHR.success().
You need to use a callback to process the result of email validation
function chkmail(email, callback) {
var posting = $.post('http://localhost/tv100.info/index.php/user/chkmail', {
u_email : email
});
posting.done(function(data) {
if (data == 'success') {
callback(true);
} else if (data == 'failure') {
callback(false);
}
});
}
$('#regform').submit(function(event) {
var res = validateregForm()
event.preventDefault();
if (res) {
chkmail($('#u_email').val(), function(valid) {
if (valid) {
$('#email_error').css('display', 'none');
$('#email_msg').css('display', 'block');
$('#loading2').show();
var posting = $.post('http://localhost/tv100.info/index.php/user/create_user', $("#regform").serialize());
posting.done(function(data) {
$('#loading2').hide();
if (data == "success") {
$('#reg_panel').append('<span id="reg_msg">Registration successful Now You are logged IN</span>');
$('#overlay').fadeOut(300);
$('#login').html('Logout');
$('#sign_in').hide();
$('#cmmnt_field').show();
}
if (data == "failure") {
$('#reg_panel').append('<span id="res_msg">Something Went Wrong try again Latter</span>');
}
});
} else {
$('#email_msg').css('display', 'none');
$('#email_error').css('display', 'block');
}
});
}
});

Resources