Skip to content

Php Jwt Auth

DodaTech 5 min read

title: PHP JWT Authentication — Complete Guide to JSON Web Tokens description: 'Learn JWT authentication in PHP: token generation, validation, refresh tokens, secure storage, middleware, and stateless API authentication.' date: 2026-06-28 lastmod: 2026-06-28 weight: 37 tags: [backend, php]


PHP JWT authentication uses JSON Web Tokens to create stateless, self-contained authentication tokens that carry user identity and claims securely between client and server.

## What You'll Learn

By the end of this tutorial, you'll generate and validate JWTs, create login/signup endpoints, secure API routes with middleware, implement token refresh, and follow security best practices.

## Why JWT Matters

JWTs enable stateless authentication — the server doesn't need to store session data. This scales horizontally and works across microservices. Tokens carry user claims verified by digital signature.

## Real-World Use

A PHP REST API issues JWTs on login. The mobile app stores the token and sends it with every request. The API verifies the signature without database lookups for each request.

## JWT Learning Path

```mermaid
flowchart LR
  A[REST API] --> B[JWT Auth]
  B --> C[Testing]
  C --> D[Security]
  D --> E[Upload]
  A --> F{You Are Here}
  style F fill:#f90,color:#fff

Installing JWT Library

composer require firebase/php-jwt
<?php
require "vendor/autoload.php";
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
const SECRET_KEY = "your-256-bit-secret-change-in-production";
const ALGORITHM = "HS256";

Generating Tokens

<?php
function generateToken(array $user): string {
    $payload = [
        "iss" => "myapp.com",          // Issuer
        "sub" => $user["id"],          // Subject (user ID)
        "iat" => time(),               // Issued at
        "exp" => time() + 3600,        // Expires (1 hour)
        "role" => $user["role"]        // Custom claims
    ];
    return JWT::encode($payload, SECRET_KEY, ALGORITHM);
}

Validating Tokens

<?php
function validateToken(string $token): ?object {
    try {
        return JWT::decode($token, new Key(SECRET_KEY, ALGORITHM));
    } catch (\Exception $e) {
        return null;
    }
}

Login Endpoint

<?php
header("Content-Type: application/json");
if ($_SERVER["REQUEST_METHOD"] === "POST" && $uri === "/api/login") {
    $data = json_decode(file_get_contents("php://input"), true);
    $user = findUserByEmail($data["email"]);
    if (!$user || !password_verify($data["password"], $user["password_hash"])) {
        http_response_code(401);
        echo json_encode(["error" => "Invalid credentials"]);
        exit;
    }
    $token = generateToken($user);
    echo json_encode(["token" => $token, "expires_in" => 3600]);
}

Auth Middleware

<?php
function authenticate(): object {
    $authHeader = $_SERVER["HTTP_AUTHORIZATION"] ?? "";
    if (!preg_match("/^Bearer\s+(.+)$/", $authHeader, $matches)) {
        http_response_code(401);
        echo json_encode(["error" => "No token provided"]);
        exit;
    }
    $decoded = validateToken($matches[1]);
    if (!$decoded) {
        http_response_code(401);
        echo json_encode(["error" => "Invalid or expired token"]);
        exit;
    }
    return $decoded;
}
// Protected route
if ($method === "GET" && $uri === "/api/profile") {
    $user = authenticate();
    echo json_encode(["user_id" => $user->sub, "role" => $user->role]);
}

Refresh Tokens

<?php
$refreshTokens = [];  // Store in database in production
function generateRefreshToken(int $userId): string {
    $token = bin2hex(random_bytes(32));
    global $refreshTokens;
    $refreshTokens[$token] = ["user_id" => $userId, "expires" => time() + 86400 * 7];
    return $token;
}
function refreshAccessToken(string $refreshToken): ?string {
    global $refreshTokens;
    if (!isset($refreshTokens[$refreshToken])) return null;
    $data = $refreshTokens[$refreshToken];
    if ($data["expires"] < time()) return null;
    return generateToken(["id" => $data["user_id"], "role" => "user"]);
}

Common Mistakes

1. Storing JWT in localStorage

localStorage is accessible by JavaScript (XSS). Use httpOnly cookies for web apps.

2. Using Weak Secret Key

A weak secret allows attackers to forge tokens. Use a cryptographically random string of at least 256 bits.

3. Not Setting Token Expiration

Tokens that never expire remain valid indefinitely if leaked. Set short expiration (15-60 minutes).

4. Storing Sensitive Data in JWT

JWT payloads are base64 encoded (not encrypted). Never store passwords or credit cards in tokens.

5. Not Validating Token on Every Request

The server must verify the token's signature and expiration on every API call to protected routes.

Practice Questions

1. What are the three parts of a JWT?

Header (algorithm, type), Payload (claims), Signature (verification). Separated by dots: xxxxx.yyyyy.zzzzz.

2. How does JWT authentication work?

User logs in, server creates a signed JWT containing user ID and claims. Client sends it in Authorization header. Server verifies signature on each request.

3. What is the difference between JWT and session auth?

JWT is stateless (no server storage). Sessions store state on the server. JWT scales better horizontally.

4. How do you handle token expiration?

Issue short-lived access tokens (15-60 min) and long-lived refresh tokens (7 days). Use refresh token to get new access tokens.

5. Challenge: Create a PHP function that generates and validates JWTs for user authentication.

<?php
require "vendor/autoload.php";
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$secret = "my-secret-key-12345";
function createJWT(int $userId): string {
    global $secret;
    return JWT::encode(["sub" => $userId, "exp" => time() + 3600], $secret, "HS256");
}
function verifyJWT(string $jwt): ?object {
    global $secret;
    try { return JWT::decode($jwt, new Key($secret, "HS256")); }
    catch (\Exception $e) { return null; }
}
$token = createJWT(42);
$decoded = verifyJWT($token);
echo $decoded->sub;  // 42

FAQ

Is JWT encrypted?

No. JWT is signed (verified) but not encrypted. The payload is base64 encoded, which is easily decoded. Use JWE (JSON Web Encryption) for encrypted tokens.

How do I log out a user with JWT?

JWTs are stateless. Implement a token blacklist or use short expiration times. The client simply discards the token.

What is the best algorithm for JWT?

HS256 (HMAC with SHA-256) for single-server. RS256 (RSA) for multi-service environments where services need to verify without sharing secrets.

Can I include user roles in JWT?

Yes. Add role or permissions array to the payload. Validate claims in your authorization middleware.

What is the difference between JWT and OAuth2?

JWT is a token format. OAuth2 is an authorization framework. OAuth2 can use JWT as the token format.

Mini Project: JWT Auth System

Build a complete JWT authentication system with login and protected routes.

<?php
require "vendor/autoload.php";
header("Content-Type: application/json");
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$secret = "app-secret-key-2026";
$uri = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
if ($_SERVER["REQUEST_METHOD"] === "POST" && $uri === "/login") {
    $data = json_decode(file_get_contents("php://input"), true);
    if ($data["username"] === "admin" && $data["password"] === "secret") {
        $token = JWT::encode(["sub" => 1, "role" => "admin", "exp" => time() + 3600], $secret, "HS256");
        echo json_encode(["token" => $token]);
    } else {
        http_response_code(401);
        echo json_encode(["error" => "Invalid credentials"]);
    }
}

What's Next

PHP Testing PHPUnit PHP Security PHP Upload

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro