MeteorJS email form validation - javascript

Total novice here.
I'm trying to do a client side form validation for a subscribe to newsletter form. My client side code is such.
Template.body.events({
"submit .new-subscriber": function (event) {
// This function is called when the new task form is submitted
var newEmail = event.target.newEmail.value;
if (newEmail is email?){
Meteor.call("addNewSubscriber", newEmail);
}
I'm not sure how to perform form validation here? Can I perform the same server side?

We currently use two different approaches for email validation at Edthena depending on the situation. Hopefully one or both of these will fit your needs.
Regex
Regular expressions can be used for quick and dirty email validation. They will catch any obviously bogus emails like x#y.z or foo#bar, but that's about the limit of their accuracy. We use these inside the app on the client when an existing user has no motivation to enter an invalid address. Here's an example:
var isEmailValid = function(address) {
return /^[A-Z0-9'.1234z_%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(address);
};
In your case, you could add a call to isEmailValid in the submit handler. If the function returns false you could display an error instead of calling addNewSubscriber.
You can read more about email regular expressions here.
Mailgun
In cases where you think users could intentionally input invalid addresses, you can bring out the big guns and call the mailgun email validation API. This trades speed (results can take a couple of seconds to come back) for dramatically improved accuracy (mailgun does things like check that an MX record exists on the target domain). We use this approach in our public-facing forms.
Meteor.methods({
isEmailValid: function(address) {
check(address, String);
// modify this with your key
var result = HTTP.get('https://api.mailgun.net/v2/address/validate', {
auth: 'api:pubkey-XXXXXXXXXXXXX',
params: {address: address.trim()}
});
if (result.statusCode === 200) {
// is_valid is the boolean we are looking for
return result.data.is_valid;
} else {
// the api request failed (maybe the service is down)
// consider giving the user the benefit of the doubt and return true
return true;
}
}
});
In this example, isEmailValid is implemented as a method and can be called either on the server or the client depending on your needs. Note that you will need to get an API key and add the http package to your app with meteor add http.
For more details, see the docs.

Related

Handling data existence validation in ExpressJS service layer or controller

I'm working on a medium-sized website and I figured that I need to write more maintainable code with better project structure.
I found this article and a few others describing basically the same idea about 3-layer architecture
I find it great as I wasn't using the service layer before and It helped to DRY the code
but the article doesn't include anything about validation and how it should be handled. especially when validation is done against database (like checking resource existence)
Should I do the validation in the service layer or write validation middleware (which will need to access database and I think this is against the pattern described)
for example, I ended up with two API endpoints where website users update and remove their already added DeliveryAdddress. As you can see below validation in the service layer led to routes having duplicated code to handling HTTP response.
my routes files
router.put('/delivery-addresse/:id', DeliveryAddressesController.update);
router.delete('/delivery-addresses/:id', DeliveryAddressesController.remove);
DeliveryAddressesController
async update(req, res){
try {
....
await AddressesService.updateDeliveryAddress(userId, address);
....
} catch(error){
if (error instanceof ValidationError){
if (error.name === 'NO_SUCH_ADDRESS'){
return res.status(404).json(error);
}
}
....
}
},
async remove(req, res){
try {
...
await AddressesService.removeDeliveryAddress(userId, addressId);
....
} catch(error){
if (error instanceof ValidationError){
if (error.name === 'NO_SUCH_ADDRESS'){
return res.status(404).json(error);
}
}
....
}
},
Options
I could think of these options but not sure if they're good and which one.
Validation middleware before the controller which (the middleware) itself will call the
validation method in the service.
I think it's a good option but, maybe I
will end up with multiple database calls to fetch the resource if I don't store the result
in the req object
Using a function in the catch block to check for any ValidationErrors and respond but it's not a great readable way.
The first question you need to ask is what credentials I should write for ... The middleware layer is between the two layers of operation and application, and it is very important what kind of validation you want to do ... For example, if you want two communication services on a network, Establish a key for it and you need a validator to check every key in each service. In this case, it is better to send this key in the header and validate it in middleware, but sometimes you want to evaluate information from other services or even from the database, which requires several validation steps. It is better to implement them all in your services layer! I suggest option 2 for this 😉

HTML5 constraint validation for custom check

I would like to check if username and email address is unique on client side.
Is there any way to add custom validation for constraint validation?
Or is there any other recommended way for this?
I assume I have to send async http request (I am using node js on server side) but would like to follow best practice for this one.
Yes you can use the setCustomValidity function from the constraint validation api, which is part of every form field (e.g., HTMLInputElement).
<input name="username">
<script>
const field = document.querySelector('[name="username"]');
field.addEventListener('change', () => {
fetch('https://example.com/myapiforcheckingusername' {
method:'POST',
body: JSON.stringify({username: field.value})
}).then((response) => {
const alreadyExists = // process response to check if username already exists
if (alreadyExists) {
field.setCustomValidity('The username already exists!');
}
});
});
</script>
The above code shows how to make an async validation on an input field. If the setCustomValidity is not the empty string it means it is a form error. If checkValidity is called on the accompanying form it returns false then.
You can check the MDN documentation for further examples on this matter.
you can make a request on input's blur to check if what user has written in input is unique or not and show result to user with check mark or red X
or you can show user after he clicked on signup button but i prefer the first solution

firebase javascript injection

I want ask something about firebase security. How to handle following situations?
User is creating account with createUserWithEmailAndPassword() function, then i save his username,email,created_at...to realtime db. But what if data are not saved correctly. His account is created and he is logged in automatically but data is not stored.
I have some registration logic... for example unique usernames... so before creating acc i check if this username exist in realtime db. But he still can call createUserWithEmailandPassword() from js console and account is created.
For situation one:
According to the firebase docs (https://www.firebase.com/docs/web/api/firebase/createuser.html), creating a user does not automatically authenticate them. An additional call to authWithPassword() is required first. In order to ensure that a user isn't authenticated without valid data, you could run a check to the server to make sure the data is saved correctly before authenticating.
Edit: Nevermind that; looks like firebase does auto-auth now - take a look at what I wrote below.
Now a concern with this approach would be if your app allowed people to authenticate with an OAuth provider like gmail, then there is no function for creating the user before authenticating them. What you may need to do is pull the user data from the firebase, determine if it's valid, and if its not valid show a popup or redirect that lets the user fix any invalid data.
For situation two:
If you wanted to make sure that in the case of them calling createUserWithEmailAndPassword() from the console a new user is not created, you could try something like this with promises;
var createUserWithEmailAndPassword = function(username, password) {
var promise = isNewUserValid(username, password);
promise.then(function() {
// Code for creating new user goes here
});
}
In this way, you never expose the actual code that makes a new user because it exists within an anonymous function.
I don't think that this could solve the problem entirely though because firebases API would let anyone create an account using something
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
ref.createUser({
email: "bobtony#firebase.com",
password: "correcthorsebatterystaple"
}
(Taken from https://www.firebase.com/docs/web/api/firebase/createuser.html)
If you wanted to make sure that server side you can't ever create a user with the same user name, you'd need to look into firebases's rules, specifically .validate
Using it, you could make sure that the username doesn't already exist in order to validate the operation of creating a username for an account.
Here's the firebase doc on rules: https://www.firebase.com/docs/security/quickstart.html
And this is another question on stack overflow that is quite similar to yours. Enforcing unique usernames with Firebase simplelogin Marein's answer is a good starting point for implementing the server side validation.
First save the user credentials in the realtime database before you create the user:
var rootRef = firebase.database().ref('child');
var newUser = {
[name]: username,
[email]: useremail,
[joined]: date
};
rootRef.update(newUser);
After adding the Usersinfo into the realtime database create a new user:
firebase.auth().createUserWithEmailAndPassword(useremail, userpassword).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
When an error occured while inserting the data in the realtime database, it will skip the createuser function.
This works fine for me, hope this helps!

Automatically assign a customer to a specific customer group on sign-up - Bigcommerce

I've been told by BC support that this isn't possible, but I would be surprised if there really wasn't a way.
I need to be able to automatically assign a customer to a specific customer group when they create an account. My thought:
I would add an extra field to the sign-up form
Provide a user with a code (a string or number)
User enters code when creating new account
User hits submit
On form submit I would grab the value of the extra field:
var codeInput = document.getElementById('code-input').value;
I would then compare that value to a pre-defined string, and if there is a match, I would assign that customer to groupX (with a group id of 8):
if ( codeInput === "codeIGaveToTheUser" ) {
currentUserGroupID = 8;
}
Is it possible to assign a customer to a specific group on sign-up like this (or any other way)?
Any help is much appreciated.
Although using BigCommerce webhooks would ensure the highest success rate of executing your customer group assignment app, it requires quite a bit of setup on BigCommerce (creating a draft app, getting an oAuth key, jumping jacks, etc), and may be a bit of overkill for your requirements.
Here's an easier way, in my {mostly} humble opinion, that takes advantage of much of what you included in your original question. Any solution though will nonetheless require an external server to handle the customer group assignment through the BigCommerce API.
Within the BigCommerce control panel, add in the extra field to the user sign up form like you mentioned.
So as you can see, this new input field has been added natively to the default registration page:
So now, when a user creates an account on your site, the value for the Signup Code (the custom field created) will be directly accessible through the API for that customer's account. Take a look at what that JSON data looks like:
Okay, so this is nice and all, but how do we automate it?
To do so, we will have to let our external application know that a customer just registered. Furthermore, our external application will need some sort of reference to this newly created customer, so that it knows which customer to update the customer group for. Normally a BigCommerce webhook would notify us of all this, but since we aren't using a BigCommerce webhook, here's the alternative method to triggering the external script.
We will trigger our external application via the BigCommerce Registration Confirmation page - createaccount_thanks.html. This page is loaded immediately after a customer creates an account, so it is the perfect place to insert our trigger script.
Additionally, now that the customer is logged in, we can access the customer's email address via a BigCommerce Global system variable -%%GLOBAL_CurrentCustomerEmail%%.
We should make an HTTP request from this page to our external application along with the customer's email address. Specifically, we can make an XMLHttpRequest via JavaScript, or to be modern, we'll use Ajax via jQuery. This script should be inserted before the closing </body> tag on createaccount_thanks.html.
Example of POST request (although a GET would suffice as well):
<script>
$(function() {
$('.TitleHeading').text('One moment, we are finalizing your account. Please wait.').next().hide(); // Let the customer know they should wait a second before leaving this page.
//** Configure and Execute the HTTP POST Request! **//
$.ajax({
url: 'the_url_to_your_script.com/script.php',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({email:"%%GLOBAL_CurrentCustomerEmail%%"}),
success: function() {
// If the customer group assignment goes well, display page and proceed normally. This callback is only called if your script returns a 200 status code.
$('.TitleHeading').text('%%LNG_CreateAccountThanks%%').next().show();
},
error: function() {
// If the customer group assignment failed, you might want to tell your customer to contact you. This callback is called if your script returns any status except 200.
$('.TitleHeading').text('There was a problem creating your account').after('Please contact us at +1-123-456-7890 so that we can look into the matter. Please feel free to continue shopping in the meantime.');
}
});
});
</script>
Now finally, you just need to create your serverside application responsible for handling the request above, and updating the customer's customer group. You can use any language that you desire, and BigCommerce even offers several SDK's you can use to save mega development time. Just remember that you need to host it somewhere online, and then insert its URL to the JS script above.
PHP Example (quick & dirty):
git clone https://github.com/bigcommerce/bigcommerce-api-php.git
curl -sS https://getcomposer.org/installer | php && php composer.phar install
<?php
/**
* StackOverflow/BigCommerce :: Set Customer Group Example
* http://stackoverflow.com/questions/37201106/
*
* Automatically assigning a customer group.
*/
//--------------MAIN------------------------//
// Load Dependencies:
require ('bigcommerce-api-php/vendor/autoload.php');
use Bigcommerce\Api\Client as bc;
// Define BigCommerce API Credentials:
define('BC_PATH', 'https://store-abc123.mybigcommerce.com');
define('BC_USER', 'user');
define('BC_PASS', 'token');
// Load & Parse the Email From the Request Body;
$email = json_decode(file_get_contents('php://input'))->email;
// Execute Script if API Connection Good & Email Set:
if ($email && setConnection()) {
$customer = bc::getCollection('/customers?email=' .$email)[0]; //Load customer by email
$cgid = determineCustomerGroup($customer->form_fields[0]->value); //Determine the relevant customer group ID, via your own set string comparisons.
bc::updateCustomer($customer->id, array('customer_group_id' => $cgid)) ? http_send_status(200) : http_send_status(500); //Update the customer group.
} else {
http_send_status(500);
exit;
}
//-------------------------------------------------//
/**
* Sets & tests the API connection.
* #return bool true if the connection successful.
*/
function setConnection() {
try {
bc::configure(array(
'store_url' => BC_PATH,
'username' => BC_USER,
'api_key' => BC_PASS
));
} catch (Exception $e) {
return false;
}
return bc::getResource('/time') ? true : false; //Test Connection
}
/**
* Hard define the customer group & signup code associations here.
* #param string The code user used at signup.
* #return int The associated customergroup ID.
*/
function determineCustomerGroup($signupCode) {
switch ($signupCode) {
case 'test123':
return 1;
case 'codeIGaveToTheUser':
return 8;
default:
return 0;
}
}
So then you would do your customer group string comparisons directly in the serverside program. I'd recommend you rewrite your own BC API script as the one above in quality is really something along the lines of functional pseudo-code, but more so present to show the general idea. HTH
You would need to set up a server to listen for webhooks unless you wanted to do a cron job. We have some basic information on the developer portal, but I included more resources below. From there, you'd need to choose your server language of choice to listen for the webhooks once they been created, respond correctly (200 response if received), execute code based on this information, and then take action against the BC API.
So if you were looking for a code, you'd need to listen for the store/customer/created webhook, and have your code look for a custom field that contained the code. If it was present, then take action. Else, do nothing.
https://developer.github.com/webhooks/configuring/
http://coconut.co/how-to-create-webhooks
How do I receive Github Webhooks in Python

Checking if an email is valid in Google Apps Script

I'm using the built-in api for scripting against Google Spreadsheets to send some booking confirmations, and currently my script breaks if someone has filled in an invalid email. I'd like it to just save some data to a list of guests that haven't been notified, and then proceed with looping through the bookings.
This is my current code (simplified):
// The variables email, subject and msg are populated.
// I've tested that using Browser.msgBox(), and the correct column values are
// found and used
// The script breaks here, if an incorrect email address has been filled in
MailApp.sendEmail(email, subject, msg)
According to the documentation the only two methods on the MailApp class are to send emails and check the daily quota - nothing about checking for valid email addresses - so I don't really know what criteria must be fulfilled for the class to accept the request, and thus can't write a validation routine.
If you need to validate email addresses beforehand, create a blank spreadsheet in your drive. Then, run the function below, changing the testSheet variable to point to the spreadsheet you created. The function will do a simple regex test to catch malformed addresses, then check if the address is actually valid by attempting to temporarily add it as a viewer on the spreadsheet. If the address can be added, it must be valid.
function validateEmail(email) {
var re = /\S+#\S+\.\S+/;
if (!re.test(email)) {
return false;
} else {
var testSheet = SpreadsheetApp.openById(arbitrarySpreadsheetInYourDrive);
try {
testSheet.addViewer(email);
} catch(e) {
return false;
}
testSheet.removeViewer(email);
return true;
}
}
regex from How to validate email address in JavaScript?
Stay calm, catch and log the exception and carry on:
try {
// do stuff, including send email
MailApp.sendEmail(email, subject, msg)
} catch(e) {
Logger.log("Error with email (" + email + "). " + e);
}
On the otherhand, avoid Checking email in script and get rid of loses quota or try-catch etc. I used that I got a valid email when user attempt to send an email, by signing him in an email and got that email:
private void handleSignInResult(Task<GoogleSignInAccount> completedTask) {
try {
GoogleSignInAccount account = completedTask.getResult(ApiException.class);
String s = account.getEmail(); // here is the valid email.
} catch (ApiException e) {
// The ApiException status code indicates the detailed failure reason.
// Please refer to the GoogleSignInStatusCodes class reference for more information.
Log.w(TAG, "signInResult:failed code=" + e.getStatusCode());
}
}
Full procedure Here.
This answer is much later than this question was asked, but I piggy-backed off of remitnotpaucity's answer based on a comment in his answer. It does basically the same thing, adding the email to the spreadsheet and catching the error, however in my case it creates a new spreadsheet, attempts to add the user, and then after attempting to add the user, deletes the spreadsheet. In both cases, that the email is a valid email or not, it deletes the newly created spreadsheet.
Some things to note:
I am not as familiar with regular expressions, so I only check to see if the # symbol is within the email read into the function, and do not check for whitespaces.
I believe that even if it passes the first if-statement, even if it's not a valid email, an error will still be thrown and caught because Google will still catch that it's not a valid email, making the first if-statement redundant
If you are trying to validate an email outside your company, I'm unsure how it would react, so be fore-warned about that
This validation method takes a few seconds because you are creating and then deleting an email all within a single function, so it takes a fair bit longer than remitnotpaucity's
Most importantly, if you are able to, I would use an API. I believe that this one would work perfectly fine and should be free, it just may take some extra elbow-grease to get to work with GAS.
function validateEmail(email){
let ss = SpreadsheetApp.openByUrl(SpreadsheetApp.create('Email Validation Spreadsheet', 1, 1).getUrl())
if(!new RegExp('[#]').test(email)){
return false
} else{
try{
ss.addViewer(email)
} catch(e){
setTrashed()
return false
}
setTrashed()
return true
}
function setTrashed(){
DriveApp.getFilesByName('Email Validation Spreadsheet').next().setTrashed(true)
}
}

Categories