Lesson 131 of 158 – React Native Protected API
83%

React Native Protected API

A protected API is an API endpoint that can only be accessed after the user has been authenticated. In a React Native application, the JWT token is normally sent with the request, and the PHP REST API verifies the token before returning protected data.

Note: Navigation or hiding a screen in React Native does not protect an API. The PHP server must verify the authentication token and enforce authorization for every protected endpoint.

1. What is a Protected API?

A protected API requires authentication before returning protected information or performing a protected operation.

GET /api/profile.php

The API may require a valid JWT in the Authorization header.

2. Protected API Flow

React Native
      ↓
JWT Token
      ↓
Authorization Header
      ↓
PHP Protected API
      ↓
Verify JWT
      ↓
Identify User
      ↓
Check Permission
      ↓
Return Data

3. Public vs Protected API

Public API:

GET /api/courses.php

Protected API:

GET /api/profile.php
Authorization: Bearer JWT_TOKEN

A protected endpoint requires authentication before processing the request.

4. Authorization Header

React Native can send the JWT using the Authorization header.

Authorization: Bearer YOUR_JWT_TOKEN

The server extracts the token and verifies it.

5. Axios Protected Request

const response =
    await api.get(
        "/profile.php",
        {
            headers: {
                Authorization:
                    `Bearer ${token}`
            }
        }
    );

6. Using an Axios Interceptor

Instead of manually adding the token to every request, an Axios request interceptor can add it automatically.

api.interceptors.request.use(
    async config => {

        const token =
            await AsyncStorage.getItem(
                "authToken"
            );

        if (token) {

            config.headers.Authorization =
                `Bearer ${token}`;
        }

        return config;
    }
);

7. User Interface

interface User {
    id: number;
    name: string;
    email: string;
    role: "admin" | "teacher" | "student";
}

8. Protected Response Interface

interface UserResponse {
    success: boolean;
    message: string;
    user: User;
}

TypeScript can use this interface to describe the response from the protected profile API.

9. Typed Protected Request

const response =
    await api.get<UserResponse>(
        "/profile.php"
    );

console.log(
    response.data.user.name
);

10. PHP Reading the Authorization Header

$headers = getallheaders();

$authorization =
    $headers["Authorization"] ?? "";

if ($authorization === "") {

    http_response_code(401);

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

    exit;
}

11. Extracting the Bearer Token

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

    http_response_code(401);

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

    exit;
}

$token = $matches[1];

12. Verifying the JWT

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

try {

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

} catch (Exception $e) {

    http_response_code(401);

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

    exit;
}

13. Getting the Authenticated User ID

The JWT can contain the user ID in the sub claim.

$userId = $decoded->sub;

The API can use this authenticated identity when retrieving user-specific data.

14. Finding the User in MySQL

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

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

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

Prepared statements should be used for database queries.

15. User Not Found

if (!$user) {

    http_response_code(404);

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

    exit;
}

A valid token does not guarantee that the related database record still exists.

16. Returning Protected User Data

echo json_encode([
    "success" => true,
    "message" => "Profile loaded",
    "user" => [
        "id" => (int)$user["id"],
        "name" => $user["name"],
        "email" => $user["email"],
        "role" => $user["role"]
    ]
]);

Never return the user's password or password hash in the API response.

17. Loading Protected Data in React Native

const loadProfile = async () => {

    try {

        const response =
            await api.get<UserResponse>(
                "/profile.php"
            );

        setUser(
            response.data.user
        );

    } catch (error) {

        console.log(
            "Unable to load profile"
        );
    }
};

18. Protected API Loading State

const [loading, setLoading] =
    useState(false);

const loadProfile = async () => {

    try {

        setLoading(true);

        const response =
            await api.get<UserResponse>(
                "/profile.php"
            );

        setUser(
            response.data.user
        );

    } finally {

        setLoading(false);

    }
};

19. Handling 401 Unauthorized

The API can return HTTP 401 when authentication is missing, invalid, or expired.

if (
    axios.isAxiosError(error) &&
    error.response?.status === 401
) {

    console.log(
        "Please login again"
    );
}

20. Handling 403 Forbidden

HTTP 403 can be used when the user is authenticated but does not have permission to perform an operation.

if (
    axios.isAxiosError(error) &&
    error.response?.status === 403
) {

    console.log(
        "Access denied"
    );
}

21. Authentication vs Authorization

Concept Question Example
Authentication Who are you? Valid JWT
Authorization What can you do? Admin permission

A protected API may require both authentication and authorization.

22. Role-Based Protected API

The server can check the authenticated user's role before allowing access to an admin-only endpoint.

if ($decoded->role !== "admin") {

    http_response_code(403);

    echo json_encode([
        "success" => false,
        "message" => "Access denied"
    ]);

    exit;
}

Authorization must be enforced by the server, not only by the mobile UI.

23. Protected Student API

A protected student endpoint can use the authenticated user's ID to return only data belonging to that user.

SELECT id, name, course
FROM students
WHERE user_id = ?

This prevents the client from simply choosing another user's ID and accessing their private data.

24. Axios Response Interceptor

api.interceptors.response.use(

    response => response,

    async error => {

        if (
            error.response?.status === 401
        ) {

            await AsyncStorage.removeItem(
                "authToken"
            );

            console.log(
                "Authentication expired"
            );
        }

        return Promise.reject(error);
    }
);

25. Protected API Service

interface UserResponse {
    success: boolean;
    message: string;
    user: User;
}

export const getProfile =
    async (): Promise<UserResponse> => {

    const response =
        await api.get<UserResponse>(
            "/profile.php"
        );

    return response.data;
};

The Axios interceptor can add the token automatically.

26. Complete Protected Screen Example

const [user, setUser] =
    useState<User | null>(null);

const loadProfile = async () => {

    try {

        const result =
            await getProfile();

        if (result.success) {

            setUser(result.user);
        }

    } catch (error) {

        if (
            axios.isAxiosError(error) &&
            error.response?.status === 401
        ) {

            console.log(
                "Login required"
            );

        } else {

            console.log(
                "Unable to load profile"
            );
        }
    }
};

useEffect(() => {
    loadProfile();
}, []);

27. Protected API Response Type

interface ApiResponse<T> {
    success: boolean;
    message: string;
    data: T;
}

interface Student {
    id: number;
    name: string;
    course: string;
}

type StudentResponse =
    ApiResponse<Student>;

type StudentListResponse =
    ApiResponse<Student[]>;

Generic response types can be reused across protected API endpoints.

28. Complete Protected API Architecture

React Native
      ↓
Axios
      ↓
Request Interceptor
      ↓
Bearer JWT
      ↓
PHP API
      ↓
Extract Token
      ↓
Verify JWT
      ↓
Identify User
      ↓
Check Authorization
      ↓
Query MySQL
      ↓
JSON Response
      ↓
React Native

29. Complete PHP Protected API Example

<?php

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

require_once "../db.php";

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

$secretKey =
    "YOUR_LONG_SECRET_KEY";

$headers = getallheaders();

$authorization =
    $headers["Authorization"] ?? "";

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

    http_response_code(401);

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

    exit;
}

$token = $matches[1];

try {

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

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

    $stmt->execute([
        $decoded->sub
    ]);

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

    if (!$user) {

        http_response_code(404);

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

        exit;
    }

    echo json_encode([
        "success" => true,
        "message" => "Profile loaded",
        "user" => [
            "id" => (int)$user["id"],
            "name" => $user["name"],
            "email" => $user["email"],
            "role" => $user["role"]
        ]
    ]);

} catch (Exception $e) {

    http_response_code(401);

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

30. Complete React Native Protected API Flow

1. User logs in
2. PHP verifies credentials
3. PHP creates JWT
4. React Native stores JWT
5. Axios reads JWT
6. Request interceptor adds Bearer token
7. Protected API receives request
8. PHP extracts JWT
9. PHP verifies signature and expiration
10. PHP identifies authenticated user
11. PHP checks authorization
12. PHP queries the database
13. PHP returns JSON
14. React Native receives typed response
15. UI displays protected data

Protected APIs are the foundation of authenticated mobile applications. React Native handles the user experience and sends the token, while the PHP backend must perform the actual authentication and authorization checks before returning protected information.

📌 Key Points

  • A protected API requires authentication before accessing protected resources.
  • JWTs can be sent using the Authorization Bearer header.
  • Axios interceptors can automatically attach JWT tokens.
  • The PHP API must verify the JWT on protected requests.
  • The JWT subject can identify the authenticated user.
  • Prepared statements should be used for database queries.
  • HTTP 401 is commonly used for authentication failures.
  • HTTP 403 is commonly used when the authenticated user lacks permission.
  • Authorization must be enforced on the server.
  • Protected APIs should never return passwords or password hashes.

🧠 Quick Quiz

Question: Who must enforce authentication and authorization for a protected REST API?