ASP.NET MVC6 Identity Part 5 – Form Validation

In the last few articles, I’ve covered the following:

  1. Creating an ASP.NET Identity Database
  2. Data Annotations and Custom Validators for Models
  3. A WebAPI Authenticator
  4. An ECMAScript 6 Modal Dialog
  5. AJAX Authentication

In the middle of all that, I left out client-side validation. We still need server-side validation – after all, how much do you trust those people who are not your primary users? The hackers, the would be hackers, and the downright malicious? I don’t trust them very much, so I definitely want to continue doing server-side validation. Client-side validation is provided so you can provide a responsive experience to your users. Server-side validation is to protect you against the hackers.

While you were gone, I tidied up my LoginModal class a bit. I abstracted three functions from the submit() method:

    /**
     * Deactivate the login form and add a spinner to show activity
     */
    deactivateForm() {
        this.submitButton.disabled = true;
        this.submitButton.innerHTML = this.spinner + this.submitButton.innerHTML;
        return;
    }

    /**
     * Deactivate the login form and add a spinner to show activity
     */
    activateForm() {
        this.submitButton.disabled = false;
        this.submitButton.innerHTML = this.submitButton.innerHTML.slice(this.spinner.length);
        this.modal.querySelector("input[name=Email]").focus();
        return;
    }

    /**
     * Display an error message in a form field.  If the field provided is
     * empty, display a general form error.
     */
    setErrorMessage(field, value) {
        let fieldSelector = this.modal.querySelector("#errorForSignInModal");
        if (field !== "") {
            fieldSelector = this.modal.querySelector("#errorFor" + field);
        }
        fieldSelector.innerHTML = value;
        return;
    }

I’ve also updated the submit() method to use ES6 fat arrow functions to ensure that the this class identifier variable is preserved in event handlers – particularly in the ajaxPost call:

        ajaxPost("/api/login", requestArgs)
            .catch(() => {
                this.setErrorMessage("", "Error transmitting credentials - try later");
                this.setErrorMessage("Email", "");
                this.setErrorMessage("Password", "");
                this.activateForm();
            })
            .then((response) => {
                if (response.status === 200) {
                    // We got a 200 Success back, so refresh the page
                    location.reload();
                } else if (response.status === 400) {
                    let r = JSON.parse(response.response);

                    // If there were errors, then add them to the appropriate areas on
                    // the form.  If there weren't errors, make sure those same areas
                    // don't have errors (handles multiple submissions)
                    this.setErrorMessage("", r[""] ? r[""][0] : "");
                    this.setErrorMessage("Email", r.Email ? r.Email[0] : "");
                    this.setErrorMessage("Password", r.Password ? r.Password[0] : "");
                    this.activateForm();
                }
            });

Now, on to validation. I like to write somewhat semantically – the code should be readable once I’ve completed it. One could argue that’s the precise purpose of the Promise format – do something then do something else. With that in mind, I decided on the following format:

        // Client Side Validation
        let validation = true;

        // Check the Email field
        try {
            let emailValidator = new Validator(requestArgs.Email);
            emailValidator.required().minLength(4).maxLength(254).isApplicationUser();
        } catch (validatorError) {
            console.error("Validation Error: ", validatorError);
            validation = false;
            this.setErrorMessage("Email", validatorError);
        }

        // Check the Password field
        try {
            let passValidator = new Validator(requestArgs.Password);
            passValidator.required().minLength(4).maxLength(254);
        } catch (validatorError) {
            validation = false;
            this.setErrorMessage("Password", validatorError);
        }

        // If either validation failed, then activate the form and continue
        if (validation === false) {
            this.activateForm();
            return;
        }

Take a look at the email field. I create a new validator and then I say “this field is required, has a minimum length of 4, a maximum length of 254 and must be an application user”. If any of those conditions do not hold true, throw an error which is caught and an error message is set. If validation failed for any reason at the end, activate the form again and return (without submitting the form).

I’ve created a new class in file Static/js/validator.js with the following contents:

"use strict";

class Validator {
    constructor(s) {
        this.s = s;
    }

    /**
     * Provided string is a string and has non-zero length
     */
    required() {
        if (typeof this.s !== "string" || this.s.length === 0) {
            throw "Field is required";
        }
        return this;
    }

    /**
     * Provided string is of minimum length
     */
    minLength(len) {
        if (this.s.length < len) {
            throw "No more than " + len.toString() + " characters allowed";
        }
        return this;
    }

    /**
     * Provided string is a valid email address
     */
    isApplicationUser() {
        // references: RFC 5321, RFC 5322, RFC 1035, plus errata.
        /*eslint-disable quotes*/
        let atom = '[A-Z0-9!#%&\+-=^_{|}~]+';
        let dot = '\.';
        let dnsLabel = '[A-Z]([A-Z0-9-]{0,61}[A-Z0-9])?';
        /*eslint-enable quotes*/

        let localPart = atom + "(" + dot + atom + ")*";
        let domain = dnsLabel + "(" + dot + dnsLabel + ")*";

        let userAccountPattern = new RegExp("^[a-z]+$", "i");
        let emailAddressPattern = new RegExp("^" + localPart + "@" + domain + "$", "i");

        if (!userAccountPattern.test(this.s) && !emailAddressPattern.test(this.s)) {
            throw "A valid RFC5321 email address is required";
        }
        return this;
    }
}

export { Validator };

Most of this is fairly obvious. The non-obvious parts (in my mind) are as follows:

  1. I return this to the calling routine. this is a reference to the current object and returning this allows for chaining.
  2. For each test (and I can add more), I check the condition and throw an error if the condition is not met. This is caught during the try…catch clause in the LoginModal
  3. The isApplicationUser is a slightly modified version of the same test in the ApplicationUserAttribute.cs validator that is used server-side. I’ve removed some special characters for ease of writing the regular expression. As a result, this regular expression is a little more constrained than the validator one.

In the isApplicationUser() test, I’ve also not embedded my admin user – I just allow any sequence of alpha characters to be a “normal username” – if someone is trying to hack your application, the last thing you want to do is give them a clue to the default username.

Final Thoughts

Someone is going to berate me for not using one of the fine Javascript Validation libraries out there – and there are many. However, you should always take some time out to understand what those libraries are doing for you. Are they actually providing enough value that it’s worth bringing in the size? What I presented here was a very minimal solution. Bringing in a generalized library would almost certainly cost more in bandwidth than my modest efforts here.