Previously, we learned how to create a simple REST API in PHP. The create, read, update and delete database records (CRUD operations) have been helpful for our projects.
Today, we will learn how to authenticate a user using REST API and JSON Web Tokens or JWT.
This tutorial will cover a basic sign-up or registration form, login and logout operations, updating a user account, and more.
In technical terms, JSON Web Token or JWT is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object.
This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
You may read more here and here.
The following is my own simple analogy. If you think you have a better one, please let me know via email. My email address is [email protected]
I’ve found another analogy that can be useful for you. Read it here.
The videos below will help explain the analogy.
A JSON Web Token or JWT looks like a string with three parts separated by dots. The following is an example of JWT.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9leGFtcGxlLm9yZyIsImF1ZCI6Imh0dHA6XC9cL2V4YW1wbGUuY29tIiwiaWF0IjoxMzU2OTk5NTI0LCJuYmYiOjEzNTcwMDAwMDAsImRhdGEiOnsiaWQiOiI5IiwiZmlyc3RuYW1lIjoiTWlrZSIsImxhc3RuYW1lIjoiRGFsaXNheSIsImVtYWlsIjoibWlrZUBjb2Rlb2ZhbmluamEuY29tIn19.h_Q4gJ3epcpwdwNCNCYxtiKdXsN34W9MEjxZ7sx21Vs
JWT in the serialized form represents a string of the following format:
header.payload.signature
The header
component contains information about how the JWT signature should be computed. The payload
component is the data that is stored inside the JWT. This can be the user information like user ID, name and email.
To create the signature
component, you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.
In this tutorial, we won’t have to worry about generating or encoding and decoding JWT because we will use a library called PHP-JWT.
We explained the JWT above. JWT is a token format and we can say it is a simple authorization protocol. OAuth is an authentication framework that can use JWT as a token.
OAuth is used as a way for Internet users to grant websites or applications access to their information on other websites but without giving them the passwords.
Use JWT if:
Use OAuth if:
Please read more here and here.
Let’s start coding below!
api_db
api_db
database, create a new table called users
.users
table.rest-api-authentication-example
as its name.htdocs
folder. In my case, I created it inside C:\xampp\htdocs
directory.rest-api-authentication-example
folder. api
folder. api
folder. config
folder.config
folder. database.php
.Place the following code.
<?php // used to get mysql database connection class Database{ // specify your own database credentials private $host = "localhost"; private $db_name = "api_db"; private $username = "root"; private $password = ""; public $conn; // get the database connection public function getConnection(){ $this->conn = null; try{ $this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password); }catch(PDOException $exception){ echo "Connection error: " . $exception->getMessage(); } return $this->conn; } } ?>
rest-api-authentication-example
folder. api
.api
folder. create_user.php
.We need to set headers on this new file so that it will only accept JSON data from a specific URL. Place the following code.
<?php // required headers header("Access-Control-Allow-Origin: http://localhost/rest-api-authentication-example/"); header("Content-Type: application/json; charset=UTF-8"); header("Access-Control-Allow-Methods: POST"); header("Access-Control-Max-Age: 3600"); header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"); // database connection will be here
We are saving the user registration information on a database so we need the database connection. We need to instantiate the user
table as well because this will make the insert
query later.
Replace // database connection will be here
comment of create_user.php
file with the following code.
// files needed to connect to database include_once 'config/database.php'; include_once 'objects/user.php'; // get database connection $database = new Database(); $db = $database->getConnection(); // instantiate product object $user = new User($db); // submitted data will be here
The user information will be submitted through an HTML form and JavaScript code. We will see this code later.
We need to assign the submitted data on the object properties such as firstname
, lastname
, etc.
Replace // submitted data will be here
comment of create_user.php
file with the following code.
// get posted data $data = json_decode(file_get_contents("php://input")); // set product property values $user->firstname = $data->firstname; $user->lastname = $data->lastname; $user->email = $data->email; $user->password = $data->password; // use the create() method here
One the code below, we check if the user data is not empty. We also use the user
object’s create()
method. It will tell the user if the user was created or not.
Replace // use the create() method here
comment of create_user.php
file with the following code.
// create the user if( !empty($user->firstname) && !empty($user->email) && !empty($user->password) && $user->create() ){ // set response code http_response_code(200); // display message: user was created echo json_encode(array("message" => "User was created.")); } // message if unable to create user else{ // set response code http_response_code(400); // display message: unable to create user echo json_encode(array("message" => "Unable to create user.")); } ?>
The previous section will not work without the user object class. This is where we’ll place all the user
methods that contains database queries.
If you’re not familiar with private or public scopes, please learn from this resource.
api
folder. objects
folder.user.php
. <?php // 'user' object class User{ // database connection and table name private $conn; private $table_name = "users"; // object properties public $id; public $firstname; public $lastname; public $email; public $password; // constructor public function __construct($db){ $this->conn = $db; } // create() method will be here }
The code below shows the INSERT query, data sanitation, and binding, and we used the built-in password_hash()
method to secure the user’s password on the database.
If the execution is a success, the user information will be saved on the database.
Replace the // create() method will be here
comment of user.php
file with the following code.
// create new user record function create(){ // insert query $query = "INSERT INTO " . $this->table_name . " SET firstname = :firstname, lastname = :lastname, email = :email, password = :password"; // prepare the query $stmt = $this->conn->prepare($query); // sanitize $this->firstname=htmlspecialchars(strip_tags($this->firstname)); $this->lastname=htmlspecialchars(strip_tags($this->lastname)); $this->email=htmlspecialchars(strip_tags($this->email)); $this->password=htmlspecialchars(strip_tags($this->password)); // bind the values $stmt->bindParam(':firstname', $this->firstname); $stmt->bindParam(':lastname', $this->lastname); $stmt->bindParam(':email', $this->email); // hash the password before saving to database $password_hash = password_hash($this->password, PASSWORD_BCRYPT); $stmt->bindParam(':password', $password_hash); // execute the query, also check if query was successful if($stmt->execute()){ return true; } return false; } // emailExists() method will be here
You need to use POSTMAN to test our API. Download your version of POSTMAN here.
http://localhost/rest-api-authentication-example/api/create_user.php
{ "firstname" : "Mike", "lastname" : "Dalisay", "email" : "[email protected]", "password" : "555" }
{ "message": "User was created." }
On the code below, we set the file headers so that it will know where the request should come from and what type of data is accepted.
rest-api-authentication-example
folder.login.php
. <?php // required headers header("Access-Control-Allow-Origin: http://localhost/rest-api-authentication-example/"); header("Content-Type: application/json; charset=UTF-8"); header("Access-Control-Allow-Methods: POST"); header("Access-Control-Max-Age: 3600"); header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"); // database connection will be here
We will compare the user email and password from the database so we need the database connection.
We need to instantiate the user table as well because this will allow us to verify if the email exists and read the hashed password.
Replace // database connection will be here
comment of login.php
file with the following code.
// files needed to connect to database include_once 'config/database.php'; include_once 'objects/user.php'; // get database connection $database = new Database(); $db = $database->getConnection(); // instantiate user object $user = new User($db); // check email existence here
On the code below, we get the email submitted by the user through the login form. We check if the email exists on our database.
Replace // check email existence here
comment of login.php
file with the following code.
// get posted data $data = json_decode(file_get_contents("php://input")); // set product property values $user->email = $data->email; $email_exists = $user->emailExists(); // files for jwt will be here
We will add an emailExists() method on our user
object class. This method will return true
if the submitted email exists, else it will return false
.
Replace // emailExists() method will be here
comment of /api/objects/user.php
file with the following code.
// check if given email exist in the database function emailExists(){ // query to check if email exists $query = "SELECT id, firstname, lastname, password FROM " . $this->table_name . " WHERE email = ? LIMIT 0,1"; // prepare the query $stmt = $this->conn->prepare( $query ); // sanitize $this->email=htmlspecialchars(strip_tags($this->email)); // bind given email value $stmt->bindParam(1, $this->email); // execute the query $stmt->execute(); // get number of rows $num = $stmt->rowCount(); // if email exists, assign values to object properties for easy access and use for php sessions if($num>0){ // get record details / values $row = $stmt->fetch(PDO::FETCH_ASSOC); // assign values to object properties $this->id = $row['id']; $this->firstname = $row['firstname']; $this->lastname = $row['lastname']; $this->password = $row['password']; // return true because email exists in the database return true; } // return false if email does not exist in the database return false; } // update() method will be here
The code below shows the necessary files we needed to include to generate or encode a JSON web token.
Replace // files for jwt will be here
comment of login.php
file with the following code.
// generate json web token include_once 'config/core.php'; include_once 'libs/php-jwt-master/src/BeforeValidException.php'; include_once 'libs/php-jwt-master/src/ExpiredException.php'; include_once 'libs/php-jwt-master/src/SignatureInvalidException.php'; include_once 'libs/php-jwt-master/src/JWT.php'; use \Firebase\JWT\JWT; // generate jwt will be here
The code below will check if the email exists and if the password matches what is in the database. We used the built-in password_verify()
function to do the matching.
If login is valid, it will generate the JSON Web Token.
Replace // generate jwt will be here
comment of login.php
file with the following code.
// check if email exists and if password is correct if($email_exists && password_verify($data->password, $user->password)){ $token = array( "iat" => $issued_at, "exp" => $expiration_time, "iss" => $issuer, "data" => array( "id" => $user->id, "firstname" => $user->firstname, "lastname" => $user->lastname, "email" => $user->email ) ); // set response code http_response_code(200); // generate jwt $jwt = JWT::encode($token, $key); echo json_encode( array( "message" => "Successful login.", "jwt" => $jwt ) ); } // login failed will be here
If the email does not exist or the password did not match, tell the user he cannot login.
Replace // login failed will be here
comment of login.php
file with the following code.
// login failed else{ // set response code http_response_code(401); // tell the user login failed echo json_encode(array("message" => "Login failed.")); } ?>
The login.php
file will not work without the core.php
file. This file contains common settings or variables of our application.
We have variables used by our JWT library to encode and decode a token. $key
‘s value must be your own and unique secret key.
The rest is called the registered claim names. The iss
(issuer) claim identifies the principal that issued the JWT.
The aud
(audience) claim identifies the recipients that the JWT is intended for. The iat
(issued at) claim identifies the time at which the JWT was issued.
The nbf
(not before) claim identifies the time before which the JWT MUST NOT be accepted for processing.
You can use another useful claim name called exp
(expiration time) which identifies the expiration time on or after which the JWT MUST NOT be accepted for processing.
Including these claims are optional. Please read more about registered claim names here.
api
folder. config
folder. core.php
. <?php // show error reporting error_reporting(E_ALL); // set your default time-zone date_default_timezone_set('Asia/Manila'); // variables used for jwt $key = "example_key"; $issued_at = time(); $expiration_time = $issued_at + (60 * 60); // valid for 1 hour $issuer = "http://localhost/CodeOfaNinja/RestApiAuthLevel1/"; ?>
The files included in login.php
file will not work without this library.
libs
folder. http://localhost/rest-api-authentication-example/api/login.php
{ "email" : "[email protected]", "password" : "555" }
111
because it is the wrong password.This file will return output in JSON format and will accept requests from the specified URL. We’ll set the correct headers.
api
folder. validate_token.php
file. <?php // required headers header("Access-Control-Allow-Origin: http://localhost/rest-api-authentication-example/"); header("Content-Type: application/json; charset=UTF-8"); header("Access-Control-Allow-Methods: POST"); header("Access-Control-Max-Age: 3600"); header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"); // files for decoding jwt will be here
The code below shows the inclusion of the necessary files to decode the given JSON web token.
Replace // files for decoding jwt will be here
comment of validate_token.php
file with the following code.
// required to decode jwt include_once 'config/core.php'; include_once 'libs/php-jwt-master/src/BeforeValidException.php'; include_once 'libs/php-jwt-master/src/ExpiredException.php'; include_once 'libs/php-jwt-master/src/SignatureInvalidException.php'; include_once 'libs/php-jwt-master/src/JWT.php'; use \Firebase\JWT\JWT; // retrieve gieve jwt here
The code below shows how to get the value of JSON web token.
Replace // retrieve gieve jwt here
comment of validate_token.php
file with the following code.
// get posted data $data = json_decode(file_get_contents("php://input")); // get jwt $jwt=isset($data->jwt) ? $data->jwt : ""; // decode jwt here
Check if a JWT is given. If true, decode it. Return a response code of 200, tell the user access is granted and some user information.
Replace // decode jwt here
comment of validate_token.php
file with the following code.
// if jwt is not empty if($jwt){ // if decode succeed, show user details try { // decode jwt $decoded = JWT::decode($jwt, $key, array('HS256')); // set response code http_response_code(200); // show user details echo json_encode(array( "message" => "Access granted.", "data" => $decoded->data )); } // catch will be here } // error if jwt is empty will be here
If decoding JWT failed, it means access to the resource is denied. We need to return a response code of 401, tell the user access is denied and some information about the error.
Replace // catch will be here
comment of validate_token.php
file with the following code.
// if decode fails, it means jwt is invalid catch (Exception $e){ // set response code http_response_code(401); // tell the user access denied & show error message echo json_encode(array( "message" => "Access denied.", "error" => $e->getMessage() )); }
If JWT is empty, it means access is also denied. We need to return a response code of 401 and tell the user access is denied.
Replace // error if jwt is empty will be here
comment of validate_token.php
file with the following code.
// show error message if jwt is empty else{ // set response code http_response_code(401); // tell the user access denied echo json_encode(array("message" => "Access denied.")); } ?>
http://localhost/rest-api-authentication-example/api/validate_token.php
{ "jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9leGFtcGxlLm9yZyIsImF1ZCI6Imh0dHA6XC9cL2V4YW1wbGUuY29tIiwiaWF0IjoxMzU2OTk5NTI0LCJuYmYiOjEzNTcwMDAwMDAsImRhdGEiOnsiaWQiOiI5IiwiZmlyc3RuYW1lIjoiTWlrZSIsImxhc3RuYW1lIjoiRGFsaXNheSIsImVtYWlsIjoibWlrZUBjb2Rlb2ZhbmluamEuY29tIn19.h_Q4gJ3epcpwdwNCNCYxtiKdXsN34W9MEjxZ7sx21Vs" }
This file will return output in JSON format and will accept requests from the specified URL. We’ll set the correct headers.
api
folder. update_user.php
file. <?php // required headers header("Access-Control-Allow-Origin: *"); header("Content-Type: application/json; charset=UTF-8"); header("Access-Control-Allow-Methods: POST"); header("Access-Control-Max-Age: 3600"); header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"); // files for decoding jwt will be here
The code below shows the inclusion of the necessary files to decode the given JSON web token.
Replace // files for decoding jwt will be here
comment of update_user.php
file with the following code.
// required to encode json web token include_once 'config/core.php'; include_once 'libs/php-jwt-master/src/BeforeValidException.php'; include_once 'libs/php-jwt-master/src/ExpiredException.php'; include_once 'libs/php-jwt-master/src/SignatureInvalidException.php'; include_once 'libs/php-jwt-master/src/JWT.php'; use \Firebase\JWT\JWT; // database connection will be here
We will need to update user information on the database. That’s why we need to get a database connection.
Replace // database connection will be here
comment of update_user.php
file with the following code.
// files needed to connect to database include_once 'config/database.php'; include_once 'objects/user.php'; // get database connection $database = new Database(); $db = $database->getConnection(); // instantiate user object $user = new User($db); // retrieve given jwt here
The code below shows how to get the value of given JSON web token.
Replace // retrieve given jwt here
comment of update_user.php
file with the following code.
// get posted data $data = json_decode(file_get_contents("php://input")); // get jwt $jwt=isset($data->jwt) ? $data->jwt : ""; // decode jwt here
Check if a JWT is given. If true, decode it inside a try
block.
Replace // decode jwt here
comment of update_user.php
file with the following code.
// if jwt is not empty if($jwt){ // if decode succeed, show user details try { // decode jwt $decoded = JWT::decode($jwt, $key, array('HS256')); // set user property values here } // catch failed decoding will be here } // error message if jwt is empty will be here
If decoding JWT fails, we need to set a response code of 401, tell the user access is denied and show information about the error.
Replace // catch failed decoding will be here
comment of update_user.php
file with the following code.
// if decode fails, it means jwt is invalid catch (Exception $e){ // set response code http_response_code(401); // show error message echo json_encode(array( "message" => "Access denied.", "error" => $e->getMessage() )); }
We need to set the submitted data (through the HTML form) to the user
object properties.
Replace // set user property values here
comment of update_user.php
file with the following code.
// set user property values $user->firstname = $data->firstname; $user->lastname = $data->lastname; $user->email = $data->email; $user->password = $data->password; $user->id = $decoded->data->id; // update user will be here
One the code below, we use the user object’s create()
method. If it returns true
, it means the user was updated. If it returns false
, the system is unable to update the user information.
Replace // update user will be here
comment of update_user.php
file with the following code.
// update the user record if($user->update()){ // regenerate jwt will be here } // message if unable to update user else{ // set response code http_response_code(401); // show error message echo json_encode(array("message" => "Unable to update user.")); }
The code below shows the UPDATE
query, data sanitation, and binding.
If a password was typed in the HTML form, we use the built-in password_hash()
method to secure the user’s password on the database.
If the execution is a success, the user information will be updated on the database.
Replace the // update() method will be here
comment of api/objects/user.php
file with the following code.
// update a user record public function update(){ // if password needs to be updated $password_set=!empty($this->password) ? ", password = :password" : ""; // if no posted password, do not update the password $query = "UPDATE " . $this->table_name . " SET firstname = :firstname, lastname = :lastname, email = :email {$password_set} WHERE id = :id"; // prepare the query $stmt = $this->conn->prepare($query); // sanitize $this->firstname=htmlspecialchars(strip_tags($this->firstname)); $this->lastname=htmlspecialchars(strip_tags($this->lastname)); $this->email=htmlspecialchars(strip_tags($this->email)); // bind the values from the form $stmt->bindParam(':firstname', $this->firstname); $stmt->bindParam(':lastname', $this->lastname); $stmt->bindParam(':email', $this->email); // hash the password before saving to database if(!empty($this->password)){ $this->password=htmlspecialchars(strip_tags($this->password)); $password_hash = password_hash($this->password, PASSWORD_BCRYPT); $stmt->bindParam(':password', $password_hash); } // unique ID of record to be edited $stmt->bindParam(':id', $this->id); // execute the query if($stmt->execute()){ return true; } return false; }
We need to re-generate or get a new JSON Web Token especially if user information was changed. The code below does that and it sets a response code of 200 and tells the user that the information was updated.
Replace the // regenerate jwt will be here
comment of update_user.php
file with the following code.
// we need to re-generate jwt because user details might be different $token = array( "iat" => $issued_at, "exp" => $expiration_time, "iss" => $issuer, "data" => array( "id" => $user->id, "firstname" => $user->firstname, "lastname" => $user->lastname, "email" => $user->email ) ); $jwt = JWT::encode($token, $key); // set response code http_response_code(200); // response in json format echo json_encode( array( "message" => "User was updated.", "jwt" => $jwt ) );
We need to tell the user that access is denied if JWT does not exist. We set a response code of 401 as well.
Replace the // error message if JWT is empty will be here
comment of update_user.php
file with the following code.
// show error message if jwt is empty else{ // set response code http_response_code(401); // tell the user access denied echo json_encode(array("message" => "Access denied.")); } ?>
http://localhost/rest-api-authentication-example/api/update_user.php
{ "firstname" : "Mike", "lastname" : "Dalisay", "email" : "[email protected]", "password" : "555", "jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9leGFtcGxlLm9yZyIsImF1ZCI6Imh0dHA6XC9cL2V4YW1wbGUuY29tIiwiaWF0IjoxMzU2OTk5NTI0LCJuYmYiOjEzNTcwMDAwMDAsImRhdGEiOnsiaWQiOiI5IiwiZmlyc3RuYW1lIjoiVmluY2UiLCJsYXN0bmFtZSI6IkRhbGlzYXkiLCJlbWFpbCI6Im1pa2VAY29kZW9mYW5pbmphLmNvbSJ9fQ.3Sv65TVYACkNPo4HMr4NvreyZY16wxG-nSorLi_jykI" }
EDITED
on the submitted JWT or just remove the JWT. It should look like the following.We will use the APIs we created earlier on a simple Single-Page Application (SPA) created using HTML, CSS and JavaScript.
All of the essential codes will be in this single index.html
file.
rest-api-authentication-example
folder. index.html
file. <!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <title>Rest API Authentication Example</title> <!-- CSS links will be here --> </head> <body> <!-- navigation bar will be here --> <!-- script links will be here --> </body> </html>
The navigation bar is where the menus like home page, account page, login page, logout and sign up page can be clicked or triggered.
Replace the <!-- navigation bar will be here -->
comment of index.html
file with the following code.
<!-- navbar --> <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top"> <a class="navbar-brand" href="#">Navbar</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNavAltMarkup"> <div class="navbar-nav"> <a class="nav-item nav-link" href="#" id='home'>Home</a> <a class="nav-item nav-link" href="#" id='update_account'>Account</a> <a class="nav-item nav-link" href="#" id='logout'>Logout</a> <a class="nav-item nav-link" href="#" id='login'>Login</a> <a class="nav-item nav-link" href="#" id='sign_up'>Sign Up</a> </div> </div> </nav> <!-- /navbar --> <!-- content section will be here -->
The content section is where the contents like HTML forms and message prompts will be rendered.
Replace the <!-- content section will be here -->
comment of index.html
file with the following code.
<!-- container --> <main role="main" class="container starter-template"> <div class="row"> <div class="col"> <!-- where prompt / messages will appear --> <div id="response"></div> <!-- where main content will appear --> <div id="content"></div> </div> </div> </main> <!-- /container -->
We are using Bootstrap 4 to make the user interface look good. We will use the CDN link so that we won’t have to download the whole library.
We will see the use of custom CSS file on the next section.
Replace the <!-- CSS links will be here -->
comment of index.html
file with the following code.
<!-- Bootstrap 4 CSS and custom CSS --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous" /> <link rel="stylesheet" type="text/css" href="custom.css" />
We use the custom CSS for any look & feel customization we want to implement.
rest-api-authentication-example
folder. custom.css
file. body { padding-top: 5rem; } .starter-template { padding: 3rem 1.5rem; } #logout{ display:none; }
In this tutorial, we use the jQuery library to render the interface and make HTTP requests.
To make Bootstrap 4 work, we need to include its own JavaScript as well.
Replace the <!-- script links will be here -->
comment of index.html
file with the following code.
<!-- jQuery & Bootstrap 4 JavaScript libraries --> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script> <!-- jquery scripts will be here -->
When you click the Sign Up menu on the navigation bar, it will show a sign up or registration form.
The code below shows the click
trigger and the HTML form.
Replace the <!-- jquery scripts will be here -->
comment of index.html
file with the following code.
<script> // jQuery codes $(document).ready(function(){ // show sign up / registration form $(document).on('click', '#sign_up', function(){ var html = ` <h2>Sign Up</h2> <form id='sign_up_form'> <div class="form-group"> <label for="firstname">Firstname</label> <input type="text" class="form-control" name="firstname" id="firstname" required /> </div> <div class="form-group"> <label for="lastname">Lastname</label> <input type="text" class="form-control" name="lastname" id="lastname" required /> </div> <div class="form-group"> <label for="email">Email</label> <input type="email" class="form-control" name="email" id="email" required /> </div> <div class="form-group"> <label for="password">Password</label> <input type="password" class="form-control" name="password" id="password" required /> </div> <button type='submit' class='btn btn-primary'>Sign Up</button> </form> `; clearResponse(); $('#content').html(html); }); // trigger when registration form is submitted here // show login form trigger will be here // clearResponse() will be here }); </script>
We need to process the form data when it is submitted.
Replace the trigger when the registration form is submitted here
comment of index.html
file with the following code.
// trigger when registration form is submitted $(document).on('submit', '#sign_up_form', function(){ // get form data var sign_up_form=$(this); var form_data=JSON.stringify(sign_up_form.serializeObject()); // submit form data to api $.ajax({ url: "api/create_user.php", type : "POST", contentType : 'application/json', data : form_data, success : function(result) { // if response is a success, tell the user it was a successful sign up & empty the input boxes $('#response').html("<div class='alert alert-success'>Successful sign up. Please login.</div>"); sign_up_form.find('input').val(''); }, error: function(xhr, resp, text){ // on error, tell the user sign up failed $('#response').html("<div class='alert alert-danger'>Unable to sign up. Please contact admin.</div>"); } }); return false; });
The clearResponse()
method was used in the previous section. Its only purpose is to remove any prompt messages that may have been displayed on the screen.
Replace the // clearResponse() will be here
comment of index.html
file with the following code.
// remove any prompt messages function clearResponse(){ $('#response').html(''); } // showLoginPage() will be here // serializeObject will be here
The serializeObject function will convert form data to JSON format. We need this function to send values from an HTML form to the API.
Replace the // serializeObject will be here
comment of index.html
file with the following code.
// function to make form values to json format $.fn.serializeObject = function(){ var o = {}; var a = this.serializeArray(); $.each(a, function() { if (o[this.name] !== undefined) { if (!o[this.name].push) { o[this.name] = [o[this.name]]; } o[this.name].push(this.value || ''); } else { o[this.name] = this.value || ''; } }); return o; };
When the user clicks the Sign-Up
link on the navigation bar.
After the user filled out and submitted the form.
When you click the Login
menu on the navigation bar, it will show a login form.
The code below shows the click
trigger and showLoginPage();
function to show a login form.
Replace the // show login form trigger will be here
comment of index.html
file with the following code.
// show login form $(document).on('click', '#login', function(){ showLoginPage(); }); // login form submit trigger will be here
The function below shows the HTML form for users to login.
Replace the // showLoginPage() will be here
comment of index.html
file with the following code.
// show login page function showLoginPage(){ // remove jwt setCookie("jwt", "", 1); // login page html var html = ` <h2>Login</h2> <form id='login_form'> <div class='form-group'> <label for='email'>Email address</label> <input type='email' class='form-control' id='email' name='email' placeholder='Enter email'> </div> <div class='form-group'> <label for='password'>Password</label> <input type='password' class='form-control' id='password' name='password' placeholder='Password'> </div> <button type='submit' class='btn btn-primary'>Login</button> </form> `; $('#content').html(html); clearResponse(); showLoggedOutMenu(); } // setCookie() will be here // showLoggedOutMenu() will be here
The setCookie()
function will help us store JWT on the cookie.
Replace the // setCookie() will be here
comment of index.html
file with the following code.
// function to set cookie function setCookie(cname, cvalue, exdays) { var d = new Date(); d.setTime(d.getTime() + (exdays*24*60*60*1000)); var expires = "expires="+ d.toUTCString(); document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; }
The showLoggedOutMenu()
function was used in the previous section.
This function will make the menu look like the options for a logged-out user.
Replace the // showLoggedOutMenu() will be here
comment of index.html
file with the following code.
// if the user is logged out function showLoggedOutMenu(){ // show login and sign up from navbar & hide logout button $("#login, #sign_up").show(); $("#logout").hide(); } // showHomePage() function will be here
Login
menu on the navigation bar.The code below shows a submit
trigger for the login form.
It gets the data from the form and stores it in the form_data
variable.
Replace the // login form submit trigger will be here
comment of index.html
file with the following code.
// trigger when login form is submitted $(document).on('submit', '#login_form', function(){ // get form data var login_form=$(this); var form_data=JSON.stringify(login_form.serializeObject()); // http request will be here return false; }); // trigger to show home page will be here
The code below shows how we make an HTTP request, specifically an AJAX request to verify if the submitted email and password are valid.
If it is valid, we will save the JWT to localStorage
, show the home page and tell the user it was a successful login.
Replace the // http request will be here
comment of index.html
file with the following code.
// submit form data to api $.ajax({ url: "api/login.php", type : "POST", contentType : 'application/json', data : form_data, success : function(result){ // store jwt to cookie setCookie("jwt", result.jwt, 1); // show home page & tell the user it was a successful login showHomePage(); $('#response').html("<div class='alert alert-success'>Successful login.</div>"); }, // error response will be here });
On the showHomePage()
function, we need to validate the stored JWT before showing the home page HTML.
Replace the // showHomePage() function will be here
comment of index.html
file with the following code.
// show home page function showHomePage(){ // validate jwt to verify access var jwt = getCookie('jwt'); $.post("api/validate_token.php", JSON.stringify({ jwt:jwt })).done(function(result) { // home page html will be here }) // show login page on error will be here } // getCookie() will be here // showLoggedInMenu() will be here
If JWT is valid, we show the home page HTML and call the showLoggedInMenu()
function.
Replace the // home page html will be here
comment of index.html
file with the following code.
// if valid, show homepage var html = ` <div class="card"> <div class="card-header">Welcome to Home!</div> <div class="card-body"> <h5 class="card-title">You are logged in.</h5> <p class="card-text">You won't be able to access the home and account pages if you are not logged in.</p> </div> </div> `; $('#content').html(html); showLoggedInMenu();
The getCookie()
function will help us read the JWT we stored earlier.
Replace the // getCookie() will be here
comment of index.html
file with the following code.
// get or read cookie function getCookie(cname){ var name = cname + "="; var decodedCookie = decodeURIComponent(document.cookie); var ca = decodedCookie.split(';'); for(var i = 0; i <ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' '){ c = c.substring(1); } if (c.indexOf(name) == 0) { return c.substring(name.length, c.length); } } return ""; }
If the submitted email and password are invalid, we tell the user login failed and empty the login form.
Replace the // error response will be here
comment of index.html
file with the following code.
error: function(xhr, resp, text){ // on error, tell the user login has failed & empty the input boxes $('#response').html("<div class='alert alert-danger'>Login failed. Email or password is incorrect.</div>"); login_form.find('input').val(''); }
The code below shows a click
trigger with showHomePage();
function.
Replace the // trigger to show home page will be here
comment of index.html
file with the following code.
// show home page $(document).on('click', '#home', function(){ showHomePage(); clearResponse(); }); // trigger to show account form will be here
The showLoggedInMenu()
function will change the menu options to look like a menu for a logged-in user.
Replace the showLoggedInMenu() will be here
comment of index.html
file with the following code.
// if the user is logged in function showLoggedInMenu(){ // hide login and sign up from navbar & show logout button $("#login, #sign_up").hide(); $("#logout").show(); } // showUpdateAccountForm() will be here
If JWT is invalid, we will show the login page and ask the user to login.
Replace the // show login page on error will be here
comment of index.html
file with the following code.
// show login page on error .fail(function(result){ showLoginPage(); $('#response').html("<div class='alert alert-danger'>Please login to access the home page.</div>"); });
Home
menu on the navigation bar.Home
menu on the navigation bar.The code below shows a click
trigger with showUpdateAccountForm();
function.
Replace the // trigger to show account form will be here
comment of index.html
file with the following code.
// show update account form $(document).on('click', '#update_account', function(){ showUpdateAccountForm(); }); // trigger for updating user account will be here
We need the showUpdateAccountForm()
function to render to HTML form for updating a user account.
First, we need to verify if JWT is valid. We use the getCookie('jwt');
function to get the JWT and send it to validate_token.php
via jQuery $.post
method.
Replace the // showUpdateAccountForm() will be here
comment of index.html
file with the following code.
function showUpdateAccountForm(){ // validate jwt to verify access var jwt = getCookie('jwt'); $.post("api/validate_token.php", JSON.stringify({ jwt:jwt })).done(function(result) { // html form for updating user account will be here }) // error message when jwt is invalid will be here }
If JWT is valid, we will show the HTML form using the code below.
Replace the // html form for updating user account will be here
comment of index.html
file with the following code.
// if response is valid, put user details in the form var html = ` <h2>Update Account</h2> <form id='update_account_form'> <div class="form-group"> <label for="firstname">Firstname</label> <input type="text" class="form-control" name="firstname" id="firstname" required value="` + result.data.firstname + `" /> </div> <div class="form-group"> <label for="lastname">Lastname</label> <input type="text" class="form-control" name="lastname" id="lastname" required value="` + result.data.lastname + `" /> </div> <div class="form-group"> <label for="email">Email</label> <input type="email" class="form-control" name="email" id="email" required value="` + result.data.email + `" /> </div> <div class="form-group"> <label for="password">Password</label> <input type="password" class="form-control" name="password" id="password" /> </div> <button type='submit' class='btn btn-primary'> Save Changes </button> </form> `; clearResponse(); $('#content').html(html);
If JWT is invalid, we will logout the user and ask him to login.
Replace the // error message when jwt is invalid will be here
comment of index.html
file with the following code.
// on error/fail, tell the user he needs to login to show the account page .fail(function(result){ showLoginPage(); $('#response').html("<div class='alert alert-danger'>Please login to access the account page.</div>"); });
If the submit button was clicked, we will use the code below to catch that trigger.
We will get the form handle and JWT as well.
Replace the // trigger for updating user account will be here
comment of index.html
file with the following code.
// trigger when 'update account' form is submitted $(document).on('submit', '#update_account_form', function(){ // handle for update_account_form var update_account_form=$(this); // validate jwt to verify access var jwt = getCookie('jwt'); // get form data and jwt here return false; }); // trigger to logout will be here
On the code below, we get the form values and add the JWT to it. We convert the form values to JSON via stringify()
function so that it can be sent to the API.
Replace the // get form data and jwt here
comment of index.html
file with the following code.
// get form data var update_account_form_obj = update_account_form.serializeObject() // add jwt on the object update_account_form_obj.jwt = jwt; // convert object to json string var form_data=JSON.stringify(update_account_form_obj); // send data to api here
We send the form values to update_user.php using jQuery AJAX method. If the response is successful, we tell the user his account was updated.
We store the new JWT to localStorage
as well.
Replace the // send data to api here
comment of index.html
file with the following code.
// submit form data to api $.ajax({ url: "api/update_user.php", type : "POST", contentType : 'application/json', data : form_data, success : function(result) { // tell the user account was updated $('#response').html("<div class='alert alert-success'>Account was updated.</div>"); // store new jwt to coookie setCookie("jwt", result.jwt, 1); }, // errors will be handled here });
If the system is unable to update the user, we tell the user about that.
If JWT is invalid and access is denied, we logout the user and ask him to log in.
Replace the // errors will be handled here
comment of index.html
file with the following code.
// show error message to user error: function(xhr, resp, text){ if(xhr.responseJSON.message=="Unable to update user."){ $('#response').html("<div class='alert alert-danger'>Unable to update account.</div>"); } else if(xhr.responseJSON.message=="Access denied."){ showLoginPage(); $('#response').html("<div class='alert alert-success'>Access denied. Please login</div>"); } }
The click
trigger below is used when the user click the Logout
link on the menu.
We use the showLoginPage();
method to logout the user. We tell he is logged out as well.
Replace the // trigger to logout will be here
comment of index.html
file with the following code.
// logout the user $(document).on('click', '#logout', function(){ showLoginPage(); $('#response').html("<div class='alert alert-info'>You are logged out.</div>"); });
Logout
link on the menu.FEATURES | BASIC | PRO |
---|---|---|
API for user registration / sign up | ✔ | ✔ |
API for user login | ✔ | ✔ |
API for JWT validation | ✔ | ✔ |
API for updating user account | ✔ | ✔ |
Sign up page / registration form | ✔ | ✔ |
HTML5 validation for registration form | ✔ | ✔ |
Tell the user if sign up is successful | ✔ | ✔ |
Login using email and password | ✔ | ✔ |
Tell the user if login failed | ✔ | ✔ |
Tell the user if successfully logged in | ✔ | ✔ |
Restricted access to home page | ✔ | ✔ |
Restricted access to account page | ✔ | ✔ |
Show home page when logged in | ✔ | ✔ |
Show accounts page when logged in | ✔ | ✔ |
Update user information | ✔ | ✔ |
Tell the user if updating the account failed | ✔ | ✔ |
Logout user | ✔ | ✔ |
User access level | – | ✔ |
Admin access level | – | ✔ |
Admin can create user | – | ✔ |
Admin can read different user information | – | ✔ |
Admin can update user information | – | ✔ |
Admin can delete user | – | ✔ |
Admin can read users with pagination | – | ✔ |
Admin can search users | – | ✔ |
API for creating a user | – | ✔ |
API for reading users list (with pagination) | – | ✔ |
API for reading user information | – | ✔ |
API for updating a user | – | ✔ |
API for deleting a user | – | ✔ |
Tell the user if a request fails or succeeds. | – | ✔ |
Validate JWT for every HTTP request | – | ✔ |
Use the buttons below to download. ↓ | BASIC | PRO |
We have an upcoming tutorial about the JavaScript Shopping Cart System tutorial. We will release it soon. Please subscribe here to be notified.
For now, you may want to proceed to our next tutorial series, our Social Media API series. We will start with this tutorial: How to display Facebook page events on your website using PHP?
[adinserter block=”3″]