Php Jwt Auth
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
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