How to Build a Secure PHP REST API with JWT Authentication

Introduction

We already seen REST API development tutorial, now we are going to implement Secure PHP REST API with JWT authentication. We can covering Securing end-points using JSON Web Tokens (JWT) and make it API more scalable, Robust and efficient.

Why Use of JWT Authentication?

JWT Authentication is Stateless, Scalable, Secure and better for Performance Optimization. Here are key point :

Stateless communication : Not store any data in any form like SESSION, COOKIE.

Security : Protect again CSRF injections.

Performance : Faster Authentication, without look up DATABASE

Implementation Prerequisites
  • PHP 7+ Installed with enable CURL extension.
  • Install Composer.
  • Web Server like APACHE, Nginx or built-in PHP server
Code Implementation Guide

Download and install Firebase PHP-JWT (JSON Web Tokens) library from GitHub, which can helps for generating web token.

composer require firebase/php-jwt
Create Database Connection db.php
<?php
$host = "127.0.0.1:3360";
$db_name = "new_programmerdesk";
$username = "root";
$password = "";
$conn = new mysqli($host, $username, $password, $db_name);
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
?>
Create JWT Authentication Function

Create jwt.php file where we can Manage JWT Token

  • issIssuer use for Identifies the token source, you can put on your domain name.
  • iatIssued at – Existing timestamp of creation
  • exp – Expiration – Set token expire time.

Here is complete guide for JSON Web Token

<?php
require 'vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

//echo $secret_key = base64_encode(random_bytes(32));

$secret_key = "yuSwN0wsV9QLJ6J3Y5R3JXepeIi214YGMLCXFb2aF84";


function createJWT($user_id, $role, $exp_time = 900) {
    global $secret_key;
    $payload = [
        "iss" => "localhost\programmerdesk\tutorial\new\php\php-jwt-auth",
        "iat" => time(),
        "exp" => time() + $exp_time,
        "user_id" => $user_id,
        "role" => $role
    ];
    return JWT::encode($payload, $secret_key, 'HS256');
}

function verifyJWT($token) {
    global $secret_key;
    try {
        return JWT::decode($token, new Key($secret_key, 'HS256'));
    } catch (Exception $e) {
        return false;
    }
}

function getBearerToken() {
    $headers = getallheaders();
    if (!isset($headers['Authorization'])) {
        return null;
    }
    $authHeader = explode(" ", $headers['Authorization']);
    return $authHeader[1] ?? null;
}
?>
Implement User Authentication and Token

Create file login.php for implementing login functionality and set Token expiry and refresh using setcookie

<?php
require 'db.php';
require 'jwt.php';

$data = json_decode(file_get_contents("php://input"), true);
$stmt = $conn->prepare("SELECT id, password, role FROM users_jwt_auth WHERE email=?");
$stmt->bind_param("s", $data['email']);
$stmt->execute();
$result = $stmt->get_result()->fetch_assoc();

if ($result && password_verify($data['password'], $result['password'])) {
    $access_token = createJWT($result['id'], $result['role'], 900); // 15 min expiry
    $refresh_token = createJWT($result['id'], $result['role'], 86400); // 24 hr expiry

    setcookie("refresh_token", $refresh_token, [
        "httponly" => true,
        "secure" => true,
        "samesite" => "Strict"
    ]);

    echo json_encode(["access_token" => $access_token]);
} else {
    echo json_encode(["error" => "Invalid credentials"]);
}
?>
Working Process of login.php
  • Validate Email and Password.
  • If exists generate JWT token else display invalid credentials.
  • Generate JWT token using createJWT function from jwt.php file.
  • Return JWT token as response.
Implement User Registration

Create register.php file and request for name, email and password. Simply insert user data using postman for later use.

<?php
require 'db.php';

$data = json_decode(file_get_contents("php://input"), true);
if (!empty($data['name']) && !empty($data['email']) && !empty($data['password'])) {
    $hashed_password = password_hash($data['password'], PASSWORD_DEFAULT);
    $stmt = $conn->prepare("INSERT INTO users_jwt_auth (name, email, password) VALUES (?, ?, ?)");
    $stmt->bind_param("sss", $data['name'], $data['email'], $hashed_password);
    if ($stmt->execute()) {
        echo json_encode(["message" => "User registered successfully"]);
    } else {
        echo json_encode(["error" => "Email already exists"]);
    }
} else {
    echo json_encode(["error" => "Missing parameters"]);
}
?>
Display All users data.

Create users.php file with pass token which generated by login.php

<?php
require 'db.php';
require 'jwt.php';

$token = getBearerToken();
$decoded = verifyJWT($token);

 

if (!$decoded) {
    echo json_encode(["error" => "Unauthorized"]);
    http_response_code(401);
    exit;
}

$stmt = $conn->prepare("SELECT id, name, email FROM users");
$stmt->execute();
$result = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
echo json_encode($result);
?>
Implement Refresh token

Create refresh.php file for refresh token. if token is expire then generate new one.

<?php
require 'jwt.php';

if (!isset($_COOKIE['refresh_token'])) {
    echo json_encode(["error" => "No refresh token found"]);
    exit;
}

$decoded = verifyJWT($_COOKIE['refresh_token']);
if (!$decoded) {
    echo json_encode(["error" => "Invalid refresh token"]);
    exit;
}

$new_access_token = createJWT($decoded->user_id, $decoded->role, 900);
echo json_encode(["access_token" => $new_access_token]);
?>
Implement logout functionality.

Create logout.php file and insert into blacklisted_tokens table.

<?php
require 'db.php';
require 'jwt.php';


$token = getBearerToken();
if (!$token) {
    echo json_encode(["error" => "No token provided"]);
    exit;
}

$stmt = $conn->prepare("INSERT INTO blacklisted_tokens (token) VALUES (?)");
$stmt->bind_param("s", $token);
$stmt->execute();

setcookie("refresh_token", "", time() - 3600, "/");

echo json_encode(["message" => "Logged out successfully"]);
?>
Testing API Request with the Token
  • Registration User.
//registration
curl -X GET http://localhost/register.php
JWT authentication Registration

  • Login User
curl -X POST http://localhost/login.php
JWT authentication login with access token
  • Retrieve all users

After login done, access users data with use Authorization header

curl -H "Authorization: Bearer YOUR_GENERATED_TOKEN" localhost/users.php
Retrieve all users after login using JWT authentication token
Security Enhancement
  • Always use HTTPS for secure transmission data.
  • Always use Token Refresh Mechanism for Prevent session hijacking
  • Secure Database for prevent SQL injection, always use prepared statements
FAQ
What is JWT authentication in PHP?

JWT Authentication securely transmitted JSON data from Server to client. JWT authentication stores public/private key pairs for exchanging information.

How do JSON Web Tokens Works?

After successfully, login using user credentials. In Response JSON OBJECT Return.

Authorization: Bearer <token> use for accessing secure routing or protected data.

How to refresh an expired JWT token?

User can refresh use refresh token mechanism to call them and regenerate new token.

Conclusion

We have implement JWT token based authentication system with follow best security practices and stateless authentication.