Lesson 73 of 158 – JWT Verification
73%

JWT Verification

JWT verification is the process of checking whether a JSON Web Token is valid, correctly signed, and acceptable for the requested API operation. After a user logs in and receives a JWT, the React Native application sends that token with protected API requests. The server must verify the token before returning protected data.

Note: Never trust a JWT just because it can be decoded. The server must verify its signature and validate important claims such as expiration before using the token for authentication.

1. What is JWT Verification?

JWT verification checks whether a received JWT is valid and can be trusted by the API.

React Native
     ↓
Protected API
     ↓
Receive JWT
     ↓
Verify JWT
     ↓
Valid?
 ┌───┴───┐
Yes      No
 ↓        ↓
Allow    401

2. Why JWT Verification is Required

A client can send any string as a token. The API must not assume that the token is valid.

  • Check the token signature.
  • Check the token expiration.
  • Check relevant claims.
  • Identify the authenticated user.
  • Reject invalid tokens.
  • Protect private API resources.

3. JWT Verification Flow

Authorization Header
        ↓
Extract JWT
        ↓
Decode and Verify
        ↓
Verify Signature
        ↓
Check Claims
        ↓
Valid JWT?
   ┌────┴────┐
  Yes       No
   ↓         ↓
Identify   HTTP 401
 User
   ↓
Protected Data

4. JWT Authorization Header

The mobile application commonly sends the JWT through the Authorization header using the Bearer scheme.

Authorization: Bearer YOUR_JWT_TOKEN

The protected PHP API reads this header before attempting verification.

5. Read Authorization Header in PHP

$authorization =
    $_SERVER['HTTP_AUTHORIZATION'] ?? '';

if ($authorization === '') {

    http_response_code(401);

    echo json_encode([
        "success" => false,
        "message" => "Token required"
    ]);

    exit;
}

6. Extract Bearer Token

The API needs to separate the token from the word Bearer.

if (
    !preg_match(
        '/Bearer\s+(.+)/i',
        $authorization,
        $matches
    )
) {

    http_response_code(401);

    echo json_encode([
        "success" => false,
        "message" =>
            "Invalid authorization header"
    ]);

    exit;
}

$token = trim($matches[1]);

7. Install JWT Library

PHP applications should normally use a maintained JWT library instead of implementing cryptographic JWT operations manually.

For this lesson, we use:

firebase/php-jwt

Install it with Composer:

composer require firebase/php-jwt

8. Include Composer Autoload

After installing the package, load Composer's autoloader.

require_once
    __DIR__ . '/vendor/autoload.php';

The exact relative path depends on your project structure.

9. Import JWT Classes

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

The JWT class is used for JWT operations and Key is used to specify the verification key and algorithm.

10. JWT Secret Key

For an HS256 JWT, the same secret used to sign the token must be available to the server that verifies the token.

$secretKey =
    "YOUR_SECURE_SERVER_SECRET";
Important: Never expose this secret key to the React Native application or place it in public source code.

11. Decode and Verify JWT

With the Firebase PHP JWT library, a token can be decoded and its signature can be verified using the secret key.

$decoded = JWT::decode(
    $token,
    new Key($secretKey, 'HS256')
);

If verification succeeds, the decoded token data can be used according to the application's authentication rules.

12. Invalid JWT

If the JWT is malformed, has an invalid signature, or otherwise fails verification, the API should reject the request.

http_response_code(401);

echo json_encode([
    "success" => false,
    "message" => "Invalid token"
]);

exit;

13. Catch JWT Exceptions

JWT decoding and verification can throw exceptions when the token cannot be accepted.

try {

    $decoded = JWT::decode(
        $token,
        new Key($secretKey, 'HS256')
    );

} catch (Throwable $e) {

    http_response_code(401);

    echo json_encode([
        "success" => false,
        "message" => "Invalid token"
    ]);

    exit;
}

14. Expiration Verification

JWT libraries can validate registered claims such as the exp claim when decoding a token.

$payload = [
    "sub" => "101",
    "iat" => time(),
    "exp" => time() + 3600
];

If the token is used after its expiration time, it should not be accepted.

15. Get User ID from JWT

Suppose the login API places the user ID inside the sub claim.

$decoded = JWT::decode(
    $token,
    new Key($secretKey, 'HS256')
);

$userId = $decoded->sub;

The user ID can then be used to retrieve permitted information from the database.

16. Query User After Verification

$stmt = $pdo->prepare(
    "SELECT id, name, email
     FROM users
     WHERE id = ?
     LIMIT 1"
);

$stmt->execute([$userId]);

$user = $stmt->fetch(
    PDO::FETCH_ASSOC
);

Prepared statements should be used when querying the database.

17. User Not Found

A token may be correctly signed but the referenced user may no longer exist. The API should handle this situation.

if (!$user) {

    http_response_code(401);

    echo json_encode([
        "success" => false,
        "message" => "User not found"
    ]);

    exit;
}

18. Return Protected Data

After successful JWT verification, the API can return protected data.

echo json_encode([
    "success" => true,
    "message" => "Authenticated",
    "user" => $user
]);

19. Authentication Failure Status

When the request does not contain valid authentication credentials, HTTP 401 Unauthorized is commonly used.

http_response_code(401);

echo json_encode([
    "success" => false,
    "message" => "Authentication required"
]);

20. JWT Verification vs Decoding

Decoding Verification
Reads token contents Checks token authenticity
Does not prove the token is trustworthy Checks the cryptographic signature
Payload can be decoded Signature must be valid
Not enough for authentication Required for authentication
Important: Never use merely decoded JWT data as proof of authentication without verifying the token.

21. Verify JWT Before Database Access

A protected API should authenticate the request before using token claims to identify the user or access protected resources.

Request
  ↓
Extract Token
  ↓
Verify JWT
  ↓
Read User ID
  ↓
Database Query
  ↓
Return Protected Data

22. JWT Verification with React Native

React Native does not normally verify the server's JWT signature for ordinary API authentication. It sends the token to the server, and the server verifies it.

React Native
     ↓
Authorization Header
     ↓
PHP API
     ↓
JWT Verification
     ↓
Protected Data

23. React Native Protected Request

const response = await fetch(
    "https://example.com/api/profile.php",
    {
        method: "GET",

        headers: {
            "Authorization":
                "Bearer " + token,
            "Accept":
                "application/json"
        }
    }
);

const data =
    await response.json();

console.log(data);

24. Handle 401 in React Native

If the server returns 401, the mobile application can treat the user as unauthenticated and redirect to the login screen when appropriate.

if (response.status === 401) {

    console.log(
        "Token invalid or expired"
    );

    // Navigate to Login
}

25. JWT Verification and Authorization

Verifying a JWT proves that the request is authenticated. It does not automatically mean that the user can perform every operation.

JWT Verification
       ↓
Identify User
       ↓
Check Permission
       ↓
Allowed?
 ┌─────┴─────┐
Yes          No
 ↓            ↓
Allow        403

26. JWT Verification Security Rules

  • Always verify the JWT signature.
  • Check token expiration.
  • Use the expected signing algorithm.
  • Keep signing secrets private.
  • Use HTTPS.
  • Do not trust decoded payload data without verification.
  • Do not place JWTs in URLs.
  • Do not expose secret keys to React Native.
  • Use appropriate authorization checks after authentication.
  • Return safe error messages to clients.

27. Complete JWT Verification Flow

React Native
      ↓
Protected API Request
      ↓
Authorization: Bearer JWT
      ↓
PHP API
      ↓
Extract JWT
      ↓
JWT::decode()
      ↓
Verify Signature
      ↓
Check exp
      ↓
Read sub
      ↓
Find User
      ↓
Check Authorization
      ↓
Return Protected Data

28. Complete PHP JWT Verification Example

<?php

header(
    "Content-Type: application/json"
);

require_once '../db.php';

require_once
    __DIR__ . '/vendor/autoload.php';

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

$secretKey =
    "YOUR_SECURE_SERVER_SECRET";

try {

    $authorization =
        $_SERVER['HTTP_AUTHORIZATION'] ?? '';

    if (
        !preg_match(
            '/Bearer\s+(.+)/i',
            $authorization,
            $matches
        )
    ) {

        http_response_code(401);

        echo json_encode([
            "success" => false,
            "message" => "Token required"
        ]);

        exit;
    }

    $token = trim($matches[1]);

    $decoded = JWT::decode(
        $token,
        new Key($secretKey, 'HS256')
    );

    $userId = $decoded->sub ?? null;

    if (!$userId) {

        http_response_code(401);

        echo json_encode([
            "success" => false,
            "message" =>
                "Invalid token payload"
        ]);

        exit;
    }

    $stmt = $pdo->prepare(
        "SELECT id, name, email
         FROM users
         WHERE id = ?
         LIMIT 1"
    );

    $stmt->execute([$userId]);

    $user =
        $stmt->fetch(
            PDO::FETCH_ASSOC
        );

    if (!$user) {

        http_response_code(401);

        echo json_encode([
            "success" => false,
            "message" => "User not found"
        ]);

        exit;
    }

    http_response_code(200);

    echo json_encode([
        "success" => true,
        "message" =>
            "JWT verified successfully",
        "user" => $user
    ]);

} catch (Throwable $e) {

    http_response_code(401);

    echo json_encode([
        "success" => false,
        "message" => "Invalid or expired token"
    ]);

}

?>

29. Test JWT Verification in Postman

Step 1: Login using the JWT login API.

POST
http://localhost/api/jwt_login.php

Step 2: Copy the JWT from the response.

Step 3: Open the protected API.

GET
http://localhost/api/profile.php

Step 4: Add the header:

Authorization:
Bearer YOUR_JWT_TOKEN

Step 5: Send the request.

If the JWT is valid, the API should return the protected data. If the JWT is invalid or expired, the API should return an authentication error.

30. JWT Verification Summary

JWT verification is a critical part of protecting REST APIs. The server receives a JWT from the client, extracts it from the Authorization header, verifies its signature using the expected key and algorithm, checks relevant claims such as expiration, identifies the user, and then allows the protected operation when authentication is successful.

JWT
 ↓
Extract
 ↓
Verify Signature
 ↓
Check Expiration
 ↓
Read User ID
 ↓
Find User
 ↓
Check Permission
 ↓
Return Protected Data

📌 Key Points

  • JWT verification checks whether a JWT can be trusted.
  • The server should verify the JWT signature before using its claims for authentication.
  • JWTs are commonly sent using the Authorization Bearer header.
  • firebase/php-jwt can be used to verify JWTs in PHP.
  • The secret key must remain on the server.
  • JWT::decode() can decode and verify a JWT when used with the appropriate verification key.
  • The expected signing algorithm should be explicitly configured.
  • JWT expiration should be checked.
  • The sub claim can be used to identify the authenticated user.
  • The server can query the database after successfully verifying the token.
  • Invalid or expired tokens should normally result in HTTP 401.
  • JWT verification and authorization are different steps.
  • React Native sends the JWT to the API; the server normally performs the authentication verification.
  • Decoded JWT data should not be trusted until the token has been verified.
  • The next lesson will cover protected APIs using JWT authentication.

🧠 Quick Quiz

Question: What is the main purpose of JWT verification?