Lesson 72 of 158 – JWT Login API
72%

JWT Login API

In this lesson, we will create a JWT-based login API using PHP and MySQL. The user will send an email and password to the API. The API will verify the credentials and, if they are correct, generate a JWT and return it to the React Native application.

Note: This lesson focuses on the complete login flow: React Native → PHP Login API → MySQL → Password Verification → JWT → React Native.

1. What is a JWT Login API?

A JWT login API is an API endpoint that authenticates a user and returns a JSON Web Token after successful login.

POST /api/jwt_login.php

Email + Password
       ↓
PHP Login API
       ↓
Verify User
       ↓
Generate JWT
       ↓
Return JWT

2. JWT Login Flow

React Native
     ↓
POST Login Request
     ↓
PHP JWT Login API
     ↓
Find User
     ↓
Verify Password
     ↓
Create JWT
     ↓
JSON Response
     ↓
React Native
     ↓
Store Token

3. Login API URL

Suppose our login API is located at:

http://localhost/api/jwt_login.php

For a real mobile application, the API should be hosted on a server using HTTPS.

4. HTTP Method

Login data should be sent using the HTTP POST method.

POST /api/jwt_login.php

The email and password are sent inside the request body as JSON.

5. Login Request JSON

{
    "email": "student@example.com",
    "password": "123456"
}

The PHP API will read this JSON data and validate the supplied values.

6. Set JSON Response Header

The API should tell the client that its response is JSON.

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

7. Read JSON Request Body

PHP can read the raw request body using php://input.

$input = file_get_contents(
    "php://input"
);

$data = json_decode(
    $input,
    true
);

The second argument true converts the JSON object into a PHP associative array.

8. Get Email and Password

$email =
    trim($data['email'] ?? '');

$password =
    $data['password'] ?? '';

Using the null coalescing operator prevents errors when a field is missing.

9. Validate Login Data

The API should check that both email and password have been provided.

if (
    $email === '' ||
    $password === ''
) {

    http_response_code(422);

    echo json_encode([
        "success" => false,
        "message" =>
            "Email and password are required"
    ]);

    exit;
}

10. Validate Email Format

PHP provides filter_var() for validating an email address.

if (
    !filter_var(
        $email,
        FILTER_VALIDATE_EMAIL
    )
) {

    http_response_code(422);

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

    exit;
}

11. Find User in MySQL

After validation, the API searches for the user using the supplied email address.

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

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

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

12. Check Whether User Exists

if (!$user) {

    http_response_code(401);

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

    exit;
}

A generic error message avoids revealing whether a particular email exists in the database.

13. Verify Password

The stored password should be a password hash. Use password_verify() to compare the submitted password with the stored hash.

if (
    !password_verify(
        $password,
        $user['password']
    )
) {

    http_response_code(401);

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

    exit;
}

14. Never Return the Password

The password and password hash should never be returned to the mobile application.

Return only the information the client actually needs.

{
    "id": 101,
    "name": "Rahul",
    "email": "student@example.com"
}

15. Create JWT Payload

After successful password verification, we can prepare the JWT payload.

$payload = [
    "sub" => (string)$user['id'],
    "email" => $user['email'],
    "iat" => time(),
    "exp" => time() + 3600
];

Here the token is configured to expire after one hour for demonstration.

16. Meaning of JWT Payload Fields

Field Purpose
sub User identifier
email User email
iat Token issued time
exp Token expiration time

17. JWT Secret Key

For an HMAC-based JWT such as HS256, the server needs a secret key.

$secretKey =
    "YOUR_SECURE_SERVER_SECRET";
Important: Never put the JWT secret key inside the React Native application. It must remain on the server.

18. Install a JWT Library

A PHP project should normally use a well-maintained JWT library rather than implementing all JWT cryptographic operations manually.

One commonly used PHP package is:

firebase/php-jwt

If Composer is available, the package can be installed with:

composer require firebase/php-jwt

19. Include Composer Autoload

After installing the JWT package with Composer, include the Composer autoload file.

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

The exact path depends on the location of your PHP API file.

20. Import JWT Classes

use Firebase\JWT\JWT;

The JWT class can then be used to encode the payload and create the signed token.

21. Encode the JWT

After creating the payload and selecting the secret key, the JWT can be encoded.

$token = JWT::encode(
    $payload,
    $secretKey,
    'HS256'
);

The result is the JWT that can be returned to the mobile application.

22. Return JWT from API

http_response_code(200);

echo json_encode([
    "success" => true,
    "message" => "Login successful",
    "token" => $token,
    "user" => [
        "id" => $user['id'],
        "name" => $user['name'],
        "email" => $user['email']
    ]
]);

23. Complete Login Response

A successful response can look like this:

{
    "success": true,
    "message": "Login successful",
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "user": {
        "id": 101,
        "name": "Rahul",
        "email": "student@example.com"
    }
}

24. React Native Login Request

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

        headers: {
            "Content-Type":
                "application/json",
            "Accept":
                "application/json"
        },

        body: JSON.stringify({
            email: email,
            password: password
        })
    }
);

const data =
    await response.json();

console.log(data);

25. Read JWT in React Native

After a successful login, the application can read the returned token.

if (data.success) {

    const token = data.token;

    console.log(token);

}

The token should then be stored using an appropriate secure storage strategy for the mobile application.

26. Use JWT for Protected API

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

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

const data =
    await response.json();

The protected API will verify the JWT before returning private data.

27. Handle Invalid Login

If the credentials are incorrect, the API should return an appropriate authentication error.

http_response_code(401);

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

exit;

The React Native application can display a suitable message to the user.

28. Complete PHP JWT Login API

<?php

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

require_once '../db.php';

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

use Firebase\JWT\JWT;

$secretKey =
    "YOUR_SECURE_SERVER_SECRET";

try {

    $input =
        file_get_contents(
            "php://input"
        );

    $data =
        json_decode(
            $input,
            true
        );

    $email =
        trim($data['email'] ?? '');

    $password =
        $data['password'] ?? '';

    if (
        $email === '' ||
        $password === ''
    ) {

        http_response_code(422);

        echo json_encode([
            "success" => false,
            "message" =>
                "Email and password are required"
        ]);

        exit;
    }

    if (
        !filter_var(
            $email,
            FILTER_VALIDATE_EMAIL
        )
    ) {

        http_response_code(422);

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

        exit;
    }

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

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

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

    if (
        !$user ||
        !password_verify(
            $password,
            $user['password']
        )
    ) {

        http_response_code(401);

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

        exit;
    }

    $now = time();

    $payload = [
        "sub" => (string)$user['id'],
        "email" => $user['email'],
        "iat" => $now,
        "exp" => $now + 3600
    ];

    $token = JWT::encode(
        $payload,
        $secretKey,
        'HS256'
    );

    http_response_code(200);

    echo json_encode([
        "success" => true,
        "message" =>
            "Login successful",
        "token" => $token,
        "user" => [
            "id" => $user['id'],
            "name" => $user['name'],
            "email" => $user['email']
        ]
    ]);

} catch (PDOException $e) {

    http_response_code(500);

    echo json_encode([
        "success" => false,
        "message" =>
            "Database error"
    ]);

} catch (Throwable $e) {

    http_response_code(500);

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

}

?>

29. Test JWT Login API with Postman

Step 1: Select POST.

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

Step 2: Select:

Body
→ raw
→ JSON

Step 3: Send:

{
    "email": "student@example.com",
    "password": "123456"
}

Step 4: A successful response should contain:

{
    "success": true,
    "token": "eyJhbGciOiJIUzI1NiIs..."
}

Copy the token and use it in the Authorization header when testing a protected API.

30. JWT Login API Summary

The JWT login API receives the user's email and password, validates the input, finds the user in MySQL, verifies the password hash, creates a JWT after successful authentication, and returns the token as JSON. React Native can then use that token when accessing protected APIs.

React Native
     ↓
POST /jwt_login.php
     ↓
JSON Email + Password
     ↓
Validate Input
     ↓
Find User
     ↓
password_verify()
     ↓
Create JWT
     ↓
Return JSON
     ↓
React Native
     ↓
Store JWT
     ↓
Protected API

📌 Key Points

  • JWT login is used to authenticate users and issue a JSON Web Token.
  • The login API should use the POST method.
  • Login data can be sent as JSON.
  • PHP can read JSON using php://input.
  • Email should be validated before database lookup.
  • Users should be found using prepared statements.
  • Passwords should be checked using password_verify().
  • Passwords and password hashes should never be returned.
  • A JWT can contain claims such as sub, iat, and exp.
  • The JWT signing secret must remain on the server.
  • A JWT library should be used for cryptographic operations.
  • firebase/php-jwt is a commonly used PHP JWT package.
  • The JWT can be returned to React Native as JSON.
  • React Native can send the JWT using the Authorization Bearer header.
  • Invalid credentials should normally result in HTTP 401.
  • The next lesson will cover JWT verification.

🧠 Quick Quiz

Question: What should a successful JWT login API normally return to the mobile application?