Programming Tutorial #5: Registration (Signup / Login in PHP)

in #utopian-io7 years ago (edited)

What Will I Learn?

  • You will learn how to validate login details.
  • How to create a basic login system
  • How to use PHP

Requirements

  • WAMP / XAMPP / LAMP
  • Notepad++ or PHPStorm
  • Previous Tutorial Code

Difficulty

  • Basic

Tutorial Contents

Firstly, for security, to stop users from spamming the signup form, you will need a copy of google's recaptcha library, which I have slightly modified to work with my code:

<?php
/**
 * This is a PHP library that handles calling reCAPTCHA.
 *    - Documentation and latest version
 *          https://developers.google.com/recaptcha/docs/php
 *    - Get a reCAPTCHA API Key
 *          https://www.google.com/recaptcha/admin/create
 *    - Discussion group
 *          http://groups.google.com/group/recaptcha
 *
 * @copyright Copyright (c) 2014, Google Inc.
 * @link      http://www.google.com/recaptcha
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
/**
 * A ReCaptchaResponse is returned from checkAnswer().
 */
class ReCaptchaResponse
{
    public $success;
    public $errorCodes;
}
class ReCaptcha
{
    private static $_signupUrl = "https://www.google.com/recaptcha/admin";
    private static $_siteVerifyUrl =
        "https://www.google.com/recaptcha/api/siteverify?";
    private $_secret;
    private static $_version = "php_1.0";
    /**
     * Constructor.
     *
     * @param string $secret shared secret between site and ReCAPTCHA server.
     */
    function ReCaptcha($secret)
    {
        if ($secret == null || $secret == "") {
            die("To use reCAPTCHA you must get an API key from <a href='"
                . self::$_signupUrl . "'>" . self::$_signupUrl . "</a>");
        }
        $this->_secret=$secret;
    }
    /**
     * Encodes the given data into a query string format.
     *
     * @param array $data array of string elements to be encoded.
     *
     * @return string - encoded request.
     */
    private function _encodeQS($data)
    {
        $req = "";
        foreach ($data as $key => $value) {
            $req .= $key . '=' . urlencode(stripslashes($value)) . '&';
        }
        // Cut the last '&'
        $req=substr($req, 0, strlen($req)-1);
        return $req;
    }
    /**
     * Submits an HTTP GET to a reCAPTCHA server.
     *
     * @param string $path url path to recaptcha server.
     * @param array  $data array of parameters to be sent.
     *
     * @return array response
     */
    private function _submitHTTPGet($path, $data)
    {
        $req = $this->_encodeQS($data);
        $response = file_get_contents($path . $req);
        return $response;
    }
    /**
     * Calls the reCAPTCHA siteverify API to verify whether the user passes
     * CAPTCHA test.
     *
     * @param string $remoteIp   IP address of end user.
     * @param string $response   response string from recaptcha verification.
     *
     * @return ReCaptchaResponse
     */
    public function verifyResponse($remoteIp, $response)
    {
        // Discard empty solution submissions
        if ($response == null || strlen($response) == 0) {
            $recaptchaResponse = new ReCaptchaResponse();
            $recaptchaResponse->success = false;
            $recaptchaResponse->errorCodes = 'missing-input';
            return $recaptchaResponse;
        }
        $getResponse = $this->_submitHttpGet(
            self::$_siteVerifyUrl,
            array (
                'secret' => $this->_secret,
                'remoteip' => $remoteIp,
                'v' => self::$_version,
                'response' => $response
            )
        );
        $answers = json_decode($getResponse, true);
        $recaptchaResponse = new ReCaptchaResponse();
        if (trim($answers ['success']) == true) {
            $recaptchaResponse->success = true;
        } else {
            $recaptchaResponse->success = false;
        }
        return $recaptchaResponse;
    }
}
?>

I have changed the output to simplify my code by having it just output true or false depending on success, because I don't want/need the other outputs (See Here: Google recaptcha documentation)

As it says, you will need to visit: https://www.google.com/recaptcha/admin/create and sign in / create a google account, and then type a label, enter your IP in the Domains Box (Search "What is my ip?"), accept the terms and click create:
image.png

If you wish to skip this, you can, but just exclude the recaptcha if statement in the login and signup files which we will write.

Save this as captchalib.php in scripts/

Next, to create a stylesheet for our login pages, create a folder called styles and inside it create a file called style.css and insert the following styles into it:

/*CSS*/

.darkClass
{
    background-color: black; /*Background Colour: Black (To Grey out page on register button press)*/ /*Background Colour: Black (To Grey out page on register button press)(Sets Opacity to 90%)*/
    filter:alpha(opacity=90); /* IE (Sets Opacity to 90%)*/
    opacity: 0.9; /* Safari, Opera (Sets Opacity to 90%)*/
    -moz-opacity:0.9; /* FireFox (Sets Opacity to 90%)/*(Order of display)*/
    height: 100%;  100%; /*(Width Of Box)*/
    background-repeat:no-repeat; /*No Repetition of background*/
    background-position:center; /*Center background*/
    position:absolute; /*Position with absolute coordinates*/
    top: 0px; /*Distance from the top*/
    left: 0px; /*Distance from the left*/
}

.visible {
    visible: false; /*Makes invisible*/
}

textarea:focus, input:focus{
    outline: none; /*Disables outlines on input boxes*/
}

Next create a script to show and hide the registration form, in scripts/ create script.js and enter the following:

/*Prints Heading into Error Heading Box*/
function errorHead(error) {/*Form (White Part)*/
    document.getElementById("register_issue_heading").innerHTML = error;
}
/*Prints Content into Error Box*/
function errorContent(errorCont) {
    document.getElementById("register_issue_content").innerHTML = errorCont;
}
/*Hide the registration form*/
function hideregister() {
    document.getElementById("registeroverlay").style.display = 'none'; /*Grey background*/
    document.getElementById("registerform").style.display = 'none';/*Form (White Part)*/
    document.getElementById("registerclose").style.display = 'none';/*Close Button*/
}
/*Show registration form*//*Close Button*/
}
/*Show registration form*/
function showRegister() {
    document.getElementById("registeroverlay").style.display = 'block';
    document.getElementById("registerform").style.display = 'block';
    document.getElementById("registerclose").style.display = 'block';
}


function hidelogin() {
    document.getElementById("loginoverlay").style.display = 'none';
    document.getElementById("loginform").style.display = 'none';
    document.getElementById("loginclose").style.display = 'none';
}

/*Don't understand why, maybe just me, but it needs to be set twice, Same As Error Heading but for login*/
function showlogin() {
    document.getElementById("loginoverlay").style.display = 'block';
    document.getElementById("loginform").style.display = 'block';
    document.getElementById("loginclose").style.display = 'block';
}

/*Don't understand why, maybe just me, but it needs to be set twice, Same As Error Heading but for login*/
function LerrorHead(error) {
    document.getElementById("login_issue_heading").innerHTML = error;
    document.getElementById("login_issue_heading").innerHTML = error;
}
/*Same as errorcontent but for login*/
function LerrorContent(errorCont) {
    document.getElementById("login_issue_content").innerHTML = errorCont;
}

Now, in the www or htdocs directory, create a file called register.php with the following contents:

<!DOCTYPE HTML>
<html>
<head>

<title>Register</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> (html comment removed: Scripts (See Previous Tutorials))
<script src="scripts/script.js"></script>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css"/>
<link rel="stylesheet" href="styles/style.css">

</head>

<body>
<button class="w3-btn w3-round-large w3-blue w3-medium w3-padding-large" style="margin: auto;" onClick="showRegister();">Register!</button>(html comment removed: Activates Registration Show Function)

<div class="darkClass w3-animate-top" id="registeroverlay" style="display: none;" width="100%" height="100%">
<form action="submitreg.php<?php if (isset($_GET['r'])) { echo "?r=" . $_GET['r']; }?>" class="w3-panel w3-white w3-container w3-round-large w3-animate-top" style="width: 400px; margin: auto; " id="registerform" method="post"> (html comment removed: Sends to registration along with 'r' variable for a refferer if wanted)
<br><br>
(html comment removed: Form Layout)
<input type="text" style="margin: auto;border-bottom-color: orange;border-bottom-style: solid;border-bottom-width: 2px;" class="w3-animate-top w3-input" required name="username"/>
<h4 style="text-align: center;" class="w3-label w3-validate w3-animate-top">* Username: (Avoid @\/&?"'`)</h4>
<input type="text" style="margin: auto;border-bottom-color: orange;border-bottom-style: solid;border-bottom-width: 2px;" class="w3-animate-top w3-input" required name="email"/>
<h4 style="text-align: center;" class="w3-label w3-validate w3-animate-top">* E-mail:</h4>
<input type="password" style="margin: auto;border-bottom-color: orange;border-bottom-style: solid;border-bottom-width: 2px" class="w3-animate-top w3-input pass" required name="password"/>
<h4 style="text-align: center;" class="w3-label w3-validate w3-animate-top">* Password:</h4>
(html comment removed: Submit the form)
<input type="password" style="margin: auto;border-bottom-color: orange;border-bottom-style: solid;border-bottom-width: 2px" class="w3-animate-top w3-input pass" required name="passwordvalidate"/>
<h4 style="text-align: center;" class="w3-label w3-validate w3-animate-top">* Confirm Password:</h4>
<div class="g-recaptcha" **data-sitekey="<your google reCAPTCHA sitekey here>"** style="align: center;"></div>
<script src='https://www.google.com/recaptcha/api.js'></script>
<p></p>
(html comment removed: Submit the form)
<input type="Submit" class="w3-btn w3-round-large w3-blue w3-medium w3-padding-large w3-animate-top" style="margin: auto; display:block;" value="Register!">
</form>
</div>
</body>
</html>

This creates a html form, which will be activated when the register button is pressed, also, make sure to, near the end of the file, replace data-sitekey="" with your sitekey generated on reCAPTCHA earlier.

Now, the FUN part, user input validation. The first part is just the same form.

<!DOCTYPE HTML>
<html>
<head>
<title>Register</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="scripts/script.js"></head>
<body></script>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css"/>
<link rel="stylesheet" href="styles/style.css">
</head>
<body>
<noscript>
<div class="w3-container w3-red">
<span onclick="this.parentElement.style.display='none'"
class="w3-button w3-display-topright" class="w3-red">&times;</span>
  <h3 id="register_issue_heading">Error: For full functionality of this site it is necessary to enable JavaScript.</h3>
  <p id="register_issue_content">Here are the <a href="http://www.enable-javascript.com/" tarPOST="_blank"> instructions how to enable JavaScript in your web browser</a>.</p>
</div>
</noscript>
<div class="darkClass w3-animate-top" id="registeroverlay" style="display:none;" width="100%" height="100%">
<div class="w3-container w3-red">
  <h3 id="register_issue_heading">Generic Error While Registering.</h3>
  <p id="register_issue_content">Try Again?</p>
</div> <div id="registerclose"></div>
<form action="submitreg.php" class="w3-panel w3-white w3-container w3-round-large w3-animate-top" style="width: 400px; margin: auto; display:none;" id="registerform" method="post">
<input type="text" style="margin: auto;border-bottom-color: orange;border-bottom-style: solid;border-bottom-width: 2px;" class="w3-animate-top w3-input" required name="username"/>
<h4 style="text-align: center;" class="w3-label w3-validate w3-animate-top">* Username: (Avoid @\/&?`"')</h4>
<input type="text" style="margin: auto;border-bottom-color: orange;border-bottom-style: solid;border-bottom-width: 2px;" class="w3-animate-top w3-input" required name="email"/>
<h4 style="text-align: center;" class="w3-label w3-validate w3-animate-top">* E-mail:</h4>
<input type="password" style="margin: auto;border-bottom-color: orange;border-bottom-style: solid;border-bottom-width: 2px" class="w3-animate-top w3-input pass" required name="password"/>
<h4 style="text-align: center;" class="w3-label w3-validate w3-animate-top">* Password:</h4>
<input type="password" style="margin: auto;border-bottom-color: orange;border-bottom-style: solid;border-bottom-width: 2px" class="w3-animate-top w3-input pass" required name="passwordvalidate"/>
<h4 style="text-align: center;" class="w3-label w3-validate w3-animate-top">* Confirm Password:</h4>
<div class="g-recaptcha" data-sitekey="<recaptcha sitekey>" style="align: center; margin: 0 auto;"></div>
<script src='https://www.google.com/recaptcha/api.js'></script>
<input type="Submit" class="w3-btn w3-round-large w3-blue w3-medium w3-padding-large w3-animate-top" style="margin: auto; display:block;" value="Register!"></div>
(html comment removed: Register Form Code End)
<?php
/*Includes Code*/
include 'scripts/DatabaseConnect.php';
include 'scripts/passwordHandler.php';
require_once 'scripts/captchalib.php';
// your secret key
$secret = "<recaptcha secret>";
// empty response
$response = null;
// check secret key
$reCaptcha = new ReCaptcha($secret);
// if submitted check response
if (isset($_POST["g-recaptcha-response"])) {
if (strlen($_POST["g-recaptcha-response"]) > 0 and $_POST["g-recaptcha-response"] != null) {
    $response = $reCaptcha->verifyResponse(
        $_SERVER["REMOTE_ADDR"],
        $_POST["g-recaptcha-response"]
);}
if ($response != null && $response->success) {
if (isset($_POST['password']) and isset($_POST['passwordvalidate'])) {
    if (strlen($_POST['passwordvalidate']) > 0 or strlen($_POST['password']) > 0) {
            if ($_POST['password'] == $_POST['passwordvalidate']) {
            //Passwords Match
            $hashed_password_for_submission = hashPass($_POST['password']);
            if (isset($_POST['username'])) {
            if (strpos($_POST['username'],'@') === False and strpos($_POST['username'],'\\') === False and strpos($_POST['username'],'/') === False and strpos($_POST['username'],'"') === False and strpos($_POST['username'],"'") === False and  strpos($_POST['username'],"?") === False and strpos($_POST['username'],"&") === False and strpos($_POST['username'],"/") === False and strpos($_POST['username'],'`') === False)  {
            $username_ready = strtolower($_POST['username']);
            if (isset($_POST['email'])) {
            $email = strtolower($_POST["email"]);
            if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
                echo '<script type="text/javascript">
                showRegister();
                errorHead("E-Mail is invalid!");
                errorContent("E-mail is of an invalid format / wasn\'t entered!");
                </script>';
            } else {/*Hey, does it exist already?
                    USERNAME ONLY. Password idc*/
                        if (emailExists($email)) {
                            //Display error
                            //Display error
                            echo '<script type="text/javascript">
                            showRegister();
                            errorHead("E-Mail already exists in our database!");
                            errorContent("Log in / Use a different email!");
                            </script>';
                        } else {
                            if (usernameExists($username_ready)) {
                                //Already exists, show error, redisplay form
                                //Already exists, show error, redisplay form
                                    //Checks that length of fields is acceptable
                                echo '<script type="text/javascript">
                            showRegister();
                            errorHead("Username already exists!");
                            errorContent("Log in / Choose a different username!");  //Generates new record in database (DatabaseConnect.php)
                            </script>';
                            } else {
                                if ((strlen($username_ready)) < 255 and (strlen($username_ready)) >= 5 and (strlen($email)) < 255) {
                                    //Checks that length of fields is acceptable
                                echo '
                                <div class="w3-container w3-green">
                                <h3>Congratulations!</h3>
                                <p>You have successfully signed up to the site! Click <a href="index.php">here</a>!</p> //redirect to home page... or success page
                                </div>
                                ';
                                /*Completely Ready*/
                                $config = include 'scripts/config.php'; //Get configuration settings
                                newRecord($username_ready,$hashed_password_for_submission,$email,$_POST['username']);  //Generates new record in database (DatabaseConnect.php)
                                if (isset($_GET['r'])) { //Forward refferer variable
                                $r = $_GET['r'];
                                $config = include 'scripts/config.php';
                                $goto = '<meta http-equiv="refresh" content="0; url=http://' . $config->domain . "/" . $r . '" />';
                                    //Too Long!
                                echo $goto;
                                echo "<p><a href='http://$config->domain/$r'>Redirect</a></p>"; //redirect to home page... or success page
                                } else {
                                $config = include 'scripts/config.php';
                                $goto = '<meta http-equiv="refresh" content="0; url=http://' . $config->domain . '" />';
                                echo $goto;
                                echo "<p><a href='http://$config->domain'>Redirect</a></p>";
                                }
                                } else {
                                    $mees = strlen($username_ready);
                                    //Too Long!
                                    //No Email
                echo '<script type="text/javascript">
                                    showRegister();
                                    errorHead("E-mail or Password is too long or username too short!");
                                    errorContent("The limit on these are 255 characters, and username can\'t be less than or equal to 4 Characters. Want a 4 Letter Name Or Less? E-mail: [email protected] <a href=\"mailto:[email protected]?subject=Aquiring%20A%20Short%20Username:%20<Username>\">MailTo Link</a>");
                                    </script>';}
                                }
                        }
                    }
            } else {
                //No Username
                //No Email
                echo '<script type="text/javascript">
                showRegister();
                errorHead("You didn\'t enter an email!");
                errorContent("You Must Enter An email!");
                </script>';}
            } else 
                //Bad characters
                echo '<script type="text/javascript">
                showRegister();
                errorHead("Your username contained a dissalowed character.");
                errorContent("You Were Warned, This ISN\'T Allowed.");
                </script>';
            }
            } else {
                //No Username
                echo '<script type="text/javascript">
                showRegister();
                errorHead("You didn\'t enter a username!");
                errorContent("You Must Enter A Username!");
                </script>';}
            } else {
            //Passwords exist and are of a length of one or more but don't match
            echo '<script type="text/javascript">
                showRegister();
                errorHead("Passwords Don\'t Match!");
                errorContent("Password and Confirm Password Fields are not the same, please try again!");
                </script>';
        }
    } else {
        //One or more password fields unFilled
        //Bad Captcha
    echo '<script type="text/javascript">
        showRegister();
        errorHead("No Password Entered")
        errorContent("Either you didn\'t fill out Password/Confirm Password/Both")
        </script>';
    }
} else {
        //One or more password fields unFilled
        echo '<script type="text/javascript">
        showRegister();
        errorHead("No Password Entered")
        errorContent("Either you didn\'t fill out Password/Confirm Password/Both")
        </script>';
}
} else {
    //Bad Captcha
    echo '<script type="text/javascript">
        showRegister();
        errorHead("Invalid Captcha Response")
        errorContent("Please complete the captcha!")
        </script>';
} } else {
    //Empty Captcha
    echo '<script type="text/javascript">
        showRegister();
        errorHead("Invalid Captcha Response")
        errorContent("Please complete the captcha!")
        </script>';
}
?>
</body>
</html>

Make sure to replace with your sitekey and with your secret

I will now explain the levels of validation.

  • If captcha response has been submitted
  • If recaptcha response is not blank or null
  • If response is successful (Captcha Correct)
  • If password and confirmation are set
  • If they are non Zero
  • If they match
  • If username is set
  • If The username has no invalid characters
  • If the email is set
  • If the email matches the PHP filter
  • If the email already exists
  • If the username already exists
  • If the username is no less than 5 characters, and no more than 255 (who does it but better to be safe than sorry)
  • We good, send credentials to the server.

Thanks for reading, next week we will create the login pages and learn how to know a user is logged in! Hope you found it useful!

This lesson's code is on github

Curriculum



Posted on Utopian.io - Rewarding Open Source Contributors

Sort:  

Hey @cadawg, your contribution was rejected by the supervisor @mcfarhat because he found out that it did not follow the Utopian rules.

Upvote this comment to help Utopian grow its power and help other Open Source contributions like this one. Do you want to chat? Join me on Discord.

Hey @utopian-io, I have just read the rules after fixing it, and I believe that it now complies with the Utopian Rules, so I hope that it can be accepted now.

Thank you for the contribution. It has been approved.

You can contact us on Discord.
[utopian-moderator]

Please check my note below for rejecting the post.

Congratulations! This post has been upvoted from the communal account, @minnowsupport, by Snaddyvitch Dispenser from the Minnow Support Project. It's a witness project run by aggroed, ausbitbank, teamsteem, theprophet0, someguy123, neoxian, followbtcnews, and netuoso. The goal is to help Steemit grow by supporting Minnows. Please find us at the Peace, Abundance, and Liberty Network (PALnet) Discord Channel. It's a completely public and open space to all members of the Steemit community who voluntarily choose to be there.

If you would like to delegate to the Minnow Support Project you can do so by clicking on the following links: 50SP, 100SP, 250SP, 500SP, 1000SP, 5000SP.
Be sure to leave at least 50SP undelegated on your account.

Your contribution cannot be approved because it does not follow the Utopian Rules.
Selecting the repository relevant to your tutorial is no longer accepted under utopian, Hence your contribution will need to be rejected.
In the future, you will need to select the repository relevant to your coding language.
Also please make sure to further provide details into your steps and modifications. You did provide some relevant content, yet the more details the better.

You can contact us on Discord.
[utopian-moderator]

Hi, @mcfarhat, I have updated my post and hope that it is up to scratch now. If not, just reply and I will try to fix the issue(s). Many thanks.

This post has received a 0.31 % upvote from @drotto thanks to: @banjo.

Coin Marketplace

STEEM 0.16
TRX 0.15
JST 0.029
BTC 55255.10
ETH 2314.82
USDT 1.00
SBD 2.33