Milind Daraniya

Authentication in PHP: JWT vs. OAuth

Published February 4th, 2023 28 min read

Authentication is a crucial aspect of web development to ensure secure access to resources and protect user data. In PHP, developers have multiple options for implementing authentication, with JWT (JSON Web Tokens) and OAuth being two popular choices. In this post, we'll explore the differences between JWT and OAuth and provide a full example of implementing authentication using JWT with a Bootstrap UI for the frontend.

Understanding JWT and OAuth

JWT (JSON Web Tokens): JWT is a compact and self-contained way of representing information between two parties as a JSON object. It is commonly used for user authentication, as it securely transmits data between the server and client in the form of a token.

OAuth (Open Authorization): OAuth is an authorization framework that enables third-party applications to access resources on behalf of a user. It allows users to grant limited access to their data to other applications without sharing their credentials.

Setting Up the Project

For this example, we'll use PHP for the backend and Bootstrap for the frontend.

Create Project Structure

Create a new directory for your project and set up the following folder structure:

- backend
  - index.php
  - composer.json
  - composer.lock
- frontend
  - index.html
  - assets
    - css
      - bootstrap.min.css
    - js
      - bootstrap.bundle.min.js
      - jquery.min.js
  1. Backend: Installing Dependencies

In the backend directory, initialize a new Composer project and install the required dependencies:

composer init
composer require firebase/php-jwt

Implementing JWT Authentication

  1. Create index.php

Open index.php in the backend directory and set up the basic PHP script with JWT authentication:

<?php
require __DIR__ . '/vendor/autoload.php';

use Firebase\JWT\JWT;

$secretKey = 'your-secret-key'; // Replace this with your own secret key
$tokenExpiration = 3600; // 1 hour

// Sample user data (replace with your database query to fetch user data)
$user = [
    'id' => 1,
    'username' => 'john_doe',
];

// Generate JWT token
function generateToken($user)
{
    global $secretKey, $tokenExpiration;
    
    $payload = [
        'user_id' => $user['id'],
        'username' => $user['username'],
        'exp' => time() + $tokenExpiration,
    ];

    return JWT::encode($payload, $secretKey);
}

// Verify JWT token
function verifyToken($token)
{
    global $secretKey;

    try {
        $decoded = JWT::decode($token, $secretKey, ['HS256']);
        return (array) $decoded;
    } catch (Exception $e) {
        return false;
    }
}

// Handle login request
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['username']) && isset($_POST['password'])) {
    // Validate user credentials (replace with your authentication logic)
    if ($_POST['username'] === 'john_doe' && $_POST['password'] === 'password') {
        $userToken = generateToken($user);
        echo json_encode(['token' => $userToken]);
        exit;
    } else {
        http_response_code(401);
        echo json_encode(['error' => 'Invalid credentials']);
        exit;
    }
}

// Handle secured resource request
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['token'])) {
    $token = $_GET['token'];
    $decodedToken = verifyToken($token);

    if ($decodedToken) {
        echo json_encode(['message' => 'Access granted', 'user' => $decodedToken]);
        exit;
    } else {
        http_response_code(401);
        echo json_encode(['error' => 'Invalid token']);
        exit;
    }
}
  1. Frontend: Create index.html

Open index.html in the frontend directory and set up the HTML structure with a login form and a secured resource display:

<!DOCTYPE html>
<html>
<head>
    <title>JWT Authentication Example</title>
    <link rel="stylesheet" href="assets/css/bootstrap.min.css">
</head>
<body>
    <div class="container mt-5">
        <h1 class="mb-4">JWT Authentication Example</h1>
        
        <!-- Login Form -->
        <form id="loginForm">
            <div class="form-group">
                <label for="username">Username:</label>
                <input type="text" class="form-control" id="username" required>
            </div>
            <div class="form-group">
                <label for="password">Password:</label>
                <input type="password" class="form-control" id="password" required>
            </div>
            <button type="submit" class="btn btn-primary">Login</button>
        </form>

        <!-- Secured Resource Display -->
        <div id="securedResource" class="mt-5" style="display: none;">
            <h3>Secured Resource</h3>
            <p id="resourceContent"></p>
        </div>
    </div>

    <script src="assets/js/jquery.min.js"></script>
    <script src="assets/js/bootstrap.bundle.min.js"></script>
    <script>
        // Handle login form submission
        $('#loginForm').submit(function(event) {
            event.preventDefault();
            const username = $('#username').val();
            const password = $('#password').val();

            // Send login request to backend
            $.ajax({
                url: '../backend/index.php',
                method: 'POST',
                data: { username: username, password: password },
                dataType: 'json',
                success: function(response) {
                    // Store the token in local storage
                    localStorage.setItem('token', response.token);
                    // Hide login form and show secured resource display
                    $('#loginForm').hide();
                    $('#securedResource').show();
                    // Fetch secured resource
                    fetchResource();
                },
                error: function(error) {
                    alert('Login failed: ' + error.responseJSON.error);
                }
            });
        });

        // Fetch the secured resource from the backend
        function fetchResource() {
            const token = localStorage.getItem('token');

            // Send secured resource request to backend
            $.ajax({
                url: '../backend/index.php',
                method: 'GET',
                data: { token: token },
                dataType: 'json',
                success: function(response) {
                    $('#resourceContent').text(JSON.stringify(response, null, 2));
                },
                error: function(error) {
                    alert('Access denied: ' + error.responseJSON.error);
                }
            });
        }

        // Check if token exists and show secured resource display if so
        const token = localStorage.getItem('token');
        if (token) {
            $('#loginForm').hide();
            $('#securedResource').show();
            fetchResource();
        }
    </script>
</body>
</html>

Running the Example

To run the example, open two separate terminals, one for the backend and one for the frontend.

  1. In the backend terminal, navigate to the backend directory and start the PHP development server:
php -S localhost:8000

In the frontend terminal, navigate to the frontend directory and start a simple HTTP server:

php -S localhost:8080

Now, open your browser and visit http://localhost:8080 to see the example in action. The frontend will display a login form, and upon successful login, it will show the secured resource response obtained from the backend, demonstrating JWT-based authentication.