In this step-by-step guide, we’ll build a simple Login and Registration API using PHP, MySQL, and the “firebase/php-jwt” library for JWT handling.
This guide assumes you have PHP and MySQL set up, as well as Composer for managing dependencies.
1. Set Up Your Development Environment:
Ensure you have PHP, a web server (e.g., Apache or Nginx), MySQL, and Composer installed on your machine. You can also use XAMPP or WAMP for an all-in-one solution.
2. Create a MySQL Database:
Create a MySQL database to store user information. You can use phpMyAdmin or the MySQL command line for this purpose.
- Database Name:
php_login_api
- Database Table Name:
users
First, create a database called php_login_api, then into the database create a table called users
. Use the following SQL code to create the users table and its structure:
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`email` varchar(50) NOT NULL,
`password` varchar(65) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
Learn: How to use the above SQL code to create the users table using phpMyAdmin?
3. Create the Application Folder or Directory:
- Go inside your XAMPP
htdocs
folder or your local server’swww
directory, - and here create a new folder called
login-api
, login-api
is our Project or App folder.
4. Install the Required Library:
In this project we will use JWT for Authorization. Therefore use Composer to install the “firebase/php-jwt“ library in the login-api
folder for JWT handling:
composer require firebase/php-jwt
5. Create API Files:
After installing the JWT, we need to create 6 PHP files to build this PHP Authentication API. Here is the login-api
folder structure:


database.php
: For Database Connection
<?php
$hostname = 'localhost';
$username = 'root';
$password = '';
$database = 'php_login_api';
$connection = mysqli_connect($hostname, $username, $password, $database);
if (mysqli_connect_errno()) {
echo "Connection Failed - " . mysqli_connect_error();
exit;
}
sendJson.php
: Send Response in JSON format
In this file you can see the sendJson.php that will be used to send response to the client in JSON format.
<?php
function sendJson(int $status, string $message, array $extra = []): void
{
$response = ['status' => $status];
if ($message) $response['message'] = $message;
http_response_code($status);
echo json_encode(array_merge($response, $extra));
exit;
}
jwtHandler.php
: Encode and Decode JWT Tokens
This file contains two functions:
encodeToken()
: For encoding a new token.decodeToken()
: For decoding the token.
<?php
require_once __DIR__ . "/vendor/autoload.php";
require_once __DIR__ . "/sendJson.php";
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
$tokenSecret = 'my_strong_token_secret';
function encodeToken($data)
{
global $tokenSecret;
$token = array(
'iss' => 'http://localhost/php/login-api/',
'iat' => time(),
'exp' => time() + 3600, // 1hr
'data' => $data
);
return JWT::encode($token, $tokenSecret, 'HS256');
}
function decodeToken($token)
{
global $tokenSecret;
try {
$decode = JWT::decode($token, new Key($tokenSecret, 'HS256'));
return $decode->data;
} catch (ExpiredException | SignatureInvalidException $e) {
sendJson(401, $e->getMessage());
} catch (UnexpectedValueException | Exception $e) {
sendJson(400, $e->getMessage());
}
}
register.php
: For User Registration
The register.php contains the code for inserting new users through the API.
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: access');
header('Access-Control-Allow-Methods: POST');
header('Content-Type: application/json; charset=UTF-8');
header('Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With');
require_once __DIR__ . '/database.php';
require_once __DIR__ . '/sendJson.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') :
$data = json_decode(file_get_contents('php://input'));
if (
!isset($data->name) ||
!isset($data->email) ||
!isset($data->password) ||
empty(trim($data->name)) ||
empty(trim($data->email)) ||
empty(trim($data->password))
) :
sendJson(
422,
'Please fill all the required fields & None of the fields should be empty.',
['required_fields' => ['name', 'email', 'password']]
);
endif;
$name = mysqli_real_escape_string($connection, htmlspecialchars(trim($data->name)));
$email = mysqli_real_escape_string($connection, trim($data->email));
$password = trim($data->password);
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) :
sendJson(422, 'Invalid Email Address!');
elseif (strlen($password) < 8) :
sendJson(422, 'Your password must be at least 8 characters long!');
elseif (strlen($name) < 3) :
sendJson(422, 'Your name must be at least 3 characters long!');
endif;
$hash_password = password_hash($password, PASSWORD_DEFAULT);
$sql = "SELECT `email` FROM `users` WHERE `email`='$email'";
$query = mysqli_query($connection, $sql);
$row_num = mysqli_num_rows($query);
if ($row_num > 0) sendJson(422, 'This E-mail already in use!');
$sql = "INSERT INTO `users`(`name`,`email`,`password`) VALUES('$name','$email','$hash_password')";
$query = mysqli_query($connection, $sql);
if ($query) sendJson(201, 'You have successfully registered.');
sendJson(500, 'Something going wrong.');
endif;
sendJson(405, 'Invalid Request Method. HTTP method should be POST');
POST - http://localhost/login-api/register.php
Payload (JSON)
{
"name":"username",
"email":"[email protected]",
"password":"user password"
}

login.php
: For login user through API
<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: access");
header("Access-Control-Allow-Methods: POST");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
require_once __DIR__ . '/database.php';
require_once __DIR__ . '/jwtHandler.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') :
$data = json_decode(file_get_contents('php://input'));
if (
!isset($data->email) ||
!isset($data->password) ||
empty(trim($data->email)) ||
empty(trim($data->password))
) :
sendJson(
422,
'Please fill all the required fields & None of the fields should be empty.',
['required_fields' => ['email', 'password']]
);
endif;
$email = mysqli_real_escape_string($connection, trim($data->email));
$password = trim($data->password);
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) :
sendJson(422, 'Invalid Email Address!');
elseif (strlen($password) < 8) :
sendJson(422, 'Your password must be at least 8 characters long!');
endif;
$sql = "SELECT * FROM `users` WHERE `email`='$email'";
$query = mysqli_query($connection, $sql);
$row = mysqli_fetch_array($query, MYSQLI_ASSOC);
if ($row === null) sendJson(404, 'User not found! (Email is not registered)');
if (!password_verify($password, $row['password'])) sendJson(401, 'Incorrect Password!');
sendJson(200, '', [
'token' => encodeToken($row['id'])
]);
endif;
sendJson(405, 'Invalid Request Method. HTTP method should be POST');
POST - http://localhost/login-api/login.php
Payload (JSON)
{
"email":"[email protected]",
"password":"user password"
}

home.php
: Where users can see their Info
Here we check the Authorization header for a JWT token. If the token is present, we decode and verify it. If the user is authenticated, can see his/her Information. Otherwise, a 401 Unauthorized response is sent.
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: access');
header('Access-Control-Allow-Methods: GET');
header('Content-Type: application/json; charset=UTF-8');
header('Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With');
require_once __DIR__ . '/database.php';
require_once __DIR__ . '/jwtHandler.php';
if ($_SERVER['REQUEST_METHOD'] == 'GET') :
$headers = getallheaders();
if (array_key_exists('Authorization', $headers) && preg_match('/Bearer\s(\S+)/', $headers['Authorization'], $matches)) :
$data = decodeToken($matches[1]);
$userId = (int) $data;
if (!is_numeric($data)) sendJson(401, 'Invalid User!');
$sql = "SELECT `id`,`name`,`email` FROM `users` WHERE `id`='$userId'";
$query = mysqli_query($connection, $sql);
$row = mysqli_fetch_array($query, MYSQLI_ASSOC);
if ($row === null) sendJson(404, 'User not found!');
sendJson(200, '', $row);
endif;
sendJson(403, "Authorization Token is Missing!");
endif;
sendJson(405, 'Invalid Request Method. HTTP method should be GET');
GET - http://localhost/login-api/home.php
Payload (Header)
Authorization - Bearer Token

This is a very simple project that gives you a basic idea of building a PHP login and registration API with JWT.
There are a lot of things that you can implement in this project like – refresh token and logout features, proper error handling, store secret keys properly, etc. But, If you want me to make a project on this – let me know on Linkedin.
Thank You… Keep Learning 🙏❤️❤️