In this lesson, we will connect all authentication parts of our Student Management mobile application.
We will understand how registration, login, JWT authentication, AsyncStorage, Axios, protected APIs, Auth Context, navigation, persistent login, and logout work together.
Registration
↓
Login
↓
PHP API
↓
Verify Password
↓
Create JWT
↓
React Native
↓
Store JWT
↓
Auth Context
↓
Protected Navigation
↓
Protected API
↓
JWT Verification
↓
User Data
↓
Logout
Authentication answers:
"Who is the user?"
Authorization answers:
"What is this user allowed to do?"
Authentication
→ Login
→ JWT
→ User identity
Authorization
→ Role
→ Permissions
→ Protected actions
Registration Screen
↓
Name + Email + Password
↓
POST /register.php
↓
PHP Validation
↓
password_hash()
↓
MySQL INSERT
↓
HTTP 201
↓
Registration Successful
Registration creates the user's account. Login is performed separately to obtain the authentication token.
Login Screen
↓
Email + Password
↓
POST /login.php
↓
Find User
↓
password_verify()
↓
Create JWT
↓
Return Token
↓
React Native
interface LoginRequest {
email: string;
password: string;
}
TypeScript ensures that the login request contains the expected fields.
interface User {
id: number;
name: string;
email: string;
role: string;
}
interface LoginResponse {
success: boolean;
message: string;
token: string;
user: User;
}
const response =
await api.post<LoginResponse>(
"/login.php",
{
email,
password
}
);
const token =
response.data.token;
After successful login, the PHP API creates a JSON Web Token.
JWT
├── Header
├── Payload
└── Signature
The token represents the authenticated session information required by the protected API.
import AsyncStorage
from "@react-native-async-storage/async-storage";
await AsyncStorage.setItem(
"auth_token",
response.data.token
);
The token can be stored locally so that authentication can continue when the application is reopened.
await AsyncStorage.setItem(
"auth_user",
JSON.stringify(response.data.user)
);
Objects must be converted to JSON strings before storing them in AsyncStorage.
interface AuthState {
user: User | null;
token: string | null;
loading: boolean;
isAuthenticated: boolean;
}
Auth state allows the application to know whether a user is logged in.
interface AuthContextType {
user: User | null;
token: string | null;
login: (
token: string,
user: User
) => Promise<void>;
logout: () => Promise<void>;
loading: boolean;
}
Auth Context provides authentication information throughout the React Native application.
const AuthProvider =
({ children }: any) => {
const [user, setUser] =
useState<User | null>(null);
const [token, setToken] =
useState<string | null>(null);
const [loading, setLoading] =
useState(true);
return (
<AuthContext.Provider
value={{
user,
token,
loading
}}
>
{children}
</AuthContext.Provider>
);
};
const login =
async (
newToken: string,
newUser: User
) => {
await AsyncStorage.setItem(
"auth_token",
newToken
);
await AsyncStorage.setItem(
"auth_user",
JSON.stringify(newUser)
);
setToken(newToken);
setUser(newUser);
};
When the application starts, it can check AsyncStorage for a saved token and user.
const token =
await AsyncStorage.getItem(
"auth_token"
);
const userData =
await AsyncStorage.getItem(
"auth_user"
);
if (token && userData) {
setToken(token);
setUser(
JSON.parse(userData)
);
}
JSON.parse converts the stored JSON string back into a JavaScript object.
api.interceptors.request.use(
async config => {
const token =
await AsyncStorage.getItem(
"auth_token"
);
if (token) {
config.headers.Authorization =
`Bearer ${token}`;
}
return config;
}
);
The interceptor automatically adds the JWT to protected requests.
const response =
await api.get(
"/students.php"
);
The application does not need to manually add the Authorization header every time if the Axios interceptor handles it.
$authHeader =
$_SERVER['HTTP_AUTHORIZATION']
?? '';
if (
!preg_match(
'/Bearer\s+(.+)/',
$authHeader,
$matches
)
) {
http_response_code(401);
echo json_encode([
"success" => false,
"message" =>
"Authentication required."
]);
exit;
}
$token = $matches[1];
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$decoded = JWT::decode(
$token,
new Key(
$secretKey,
'HS256'
)
);
The server verifies the token before allowing access to protected resources.
$userId =
$decoded->sub;
$userEmail =
$decoded->email;
$userRole =
$decoded->role;
The JWT payload can contain information used to identify the authenticated user and determine permissions.
if ($userRole !== 'admin') {
http_response_code(403);
echo json_encode([
"success" => false,
"message" =>
"Permission denied."
]);
exit;
}
Authentication alone does not give every user permission to perform every action.
if (loading) {
return <LoadingScreen />;
}
if (!user) {
return <AuthNavigator />;
}
return <AppNavigator />;
The root navigation can decide which navigation stack should be displayed.
AppNavigator
├── Student List
├── Student Details
├── Add Student
├── Edit Student
├── Search Student
├── Profile
└── Logout
These screens are available after successful authentication.
Logout
↓
Remove JWT
↓
Remove User
↓
Clear Auth State
↓
Auth Navigator
↓
Login Screen
await AsyncStorage.removeItem(
"auth_token"
);
await AsyncStorage.removeItem(
"auth_user"
);
setToken(null);
setUser(null);
const loginUser =
async (
email: string,
password: string
) => {
const response =
await api.post<LoginResponse>(
"/login.php",
{
email,
password
}
);
const token =
response.data.token;
const user =
response.data.user;
await AsyncStorage.setItem(
"auth_token",
token
);
await AsyncStorage.setItem(
"auth_user",
JSON.stringify(user)
);
setToken(token);
setUser(user);
};
App Starts
↓
Check AsyncStorage
↓
JWT Found?
↓
Yes
↓
Validate Token / Profile
↓
Valid?
├── Yes → AppNavigator
└── No → Remove Token
↓
Login Screen
No Token
↓
Login Screen
The application can verify the stored authentication before showing protected screens.
React Native
│
├── Login Screen
│
├── Registration Screen
│
├── Auth Context
│
├── AsyncStorage
│
├── Axios
│
└── Navigation
│
↓
PHP REST API
│
├── Registration
├── Login
├── JWT Verification
├── Student CRUD
├── Search
└── Profile
│
↓
MySQL
│
users
students
1. User registers
↓
2. Password is hashed
↓
3. User logs in
↓
4. password_verify()
↓
5. PHP creates JWT
↓
6. React Native receives JWT
↓
7. JWT stored locally
↓
8. Auth Context stores user
↓
9. AppNavigator opens
↓
10. Axios sends Bearer token
↓
11. PHP verifies JWT
↓
12. Protected API executes
↓
13. User logs out
↓
14. Local token removed
↓
15. Login screen appears
The authentication flow connects all major authentication components of our Student Management mobile application.
password_verify().Question: Where should the JWT be verified before allowing access to a protected REST API?