In this lesson, we will create the Login Screen for our Student Management mobile application using React Native, TypeScript, Axios, and the PHP REST API created in the previous lessons.
The login screen will collect the user's email and password, send them to the PHP login API, receive the JWT token, and store the authentication information for later protected API requests.
Login Screen
↓
Enter Email
↓
Enter Password
↓
Press Login
↓
Axios POST Request
↓
PHP Login API
↓
Validate User
↓
password_verify()
↓
Generate JWT
↓
Return Token
↓
Store Token
↓
Open App Screen
A basic login screen needs the following components:
The mobile application sends the login information to the PHP login endpoint.
POST /api/login.php
The email and password are sent in the JSON request body.
{
"email": "rahul@example.com",
"password": "123456"
}
The password is sent over HTTPS to the server. The server verifies the password against the stored password hash.
interface LoginRequest {
email: string;
password: string;
}
This interface defines the data that the mobile application sends to the login API.
interface User {
id: number;
name: string;
email: string;
role: string;
}
interface LoginResponse {
success: boolean;
message: string;
token: string;
user: User;
}
The interface represents a successful login response.
import React, {
useState
} from "react";
import {
View,
Text,
TextInput,
Button,
StyleSheet,
Alert,
ActivityIndicator
} from "react-native";
const [email, setEmail] =
useState("");
const [password, setPassword] =
useState("");
const [loading, setLoading] =
useState(false);
const [error, setError] =
useState("");
These states store the form values and the current API request state.
<TextInput
placeholder="Email"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
/>
autoCapitalize="none" is useful for email input because
email addresses normally should not be automatically capitalized.
<TextInput
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
autoCapitalize="none"
/>
The secureTextEntry property hides the password while
the user is typing.
if (!email.trim()) {
setError(
"Email is required"
);
return;
}
if (!password) {
setError(
"Password is required"
);
return;
}
Client-side validation improves the user experience, but server-side validation is still required.
const emailPattern =
/^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (
!emailPattern.test(
email.trim()
)
) {
setError(
"Enter a valid email address"
);
return;
}
const handleLogin =
async () => {
setError("");
// validation
// API request
// token storage
// navigation
};
The login function controls the complete login process.
const response =
await api.post<LoginResponse>(
"/login.php",
{
email: email.trim(),
password: password
}
);
The Axios instance sends the email and password to the PHP login endpoint.
POST /login.php
↓
Read JSON
↓
Validate Email
↓
Validate Password
↓
Find User
↓
password_verify()
↓
Create JWT
↓
Return JSON
The PHP API performs the actual authentication.
The database should contain a password hash rather than a plain text password.
if (
!password_verify(
$password,
$user['password']
)
) {
http_response_code(401);
echo json_encode([
"success" => false,
"message" =>
"Invalid email or password"
]);
exit;
}
After successful password verification, the PHP API creates a JWT and returns it to the mobile application.
{
"success": true,
"message": "Login successful",
"token": "YOUR_JWT_TOKEN",
"user": {
"id": 5,
"name": "Rahul Kumar",
"email": "rahul@example.com",
"role": "student"
}
}
The application needs the token for later protected API requests. AsyncStorage can be used for token persistence in this project.
import AsyncStorage
from "@react-native-async-storage/async-storage";
await AsyncStorage.setItem(
"auth_token",
response.data.token
);
await AsyncStorage.setItem(
"auth_user",
JSON.stringify(
response.data.user
)
);
Objects must be converted into strings before storing them in AsyncStorage.
const handleLogin =
async () => {
setError("");
if (!email.trim()) {
setError(
"Email is required"
);
return;
}
if (!password) {
setError(
"Password is required"
);
return;
}
try {
setLoading(true);
const response =
await api.post<LoginResponse>(
"/login.php",
{
email:
email.trim(),
password
}
);
if (
response.data.success
) {
await AsyncStorage.setItem(
"auth_token",
response.data.token
);
await AsyncStorage.setItem(
"auth_user",
JSON.stringify(
response.data.user
)
);
Alert.alert(
"Success",
"Login successful"
);
// Navigate to
// application screen
}
} catch (error) {
setError(
"Invalid email or password"
);
} finally {
setLoading(false);
}
};
<Button
title={
loading
? "Logging in..."
: "Login"
}
onPress={handleLogin}
/>
The button starts the login process.
{loading && (
<ActivityIndicator />
)}
A loading indicator tells the user that the login request is being processed.
{error !== "" && (
<Text>
{error}
</Text>
)}
The application can display a friendly error message when the login fails.
catch (error) {
if (
axios.isAxiosError(error) &&
error.response
) {
if (
error.response.status === 401
) {
setError(
"Invalid email or password"
);
} else if (
error.response.status === 422
) {
setError(
"Please check your input"
);
} else {
setError(
"Unable to login"
);
}
} else {
setError(
"Network error"
);
}
}
return (
<View>
<Text>
Student Management
</Text>
<Text>
Login
</Text>
<TextInput
placeholder="Email"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
/>
<TextInput
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
autoCapitalize="none"
/>
{error !== "" && (
<Text>
{error}
</Text>
)}
{loading && (
<ActivityIndicator />
)}
<Button
title={
loading
? "Logging in..."
: "Login"
}
onPress={handleLogin}
/>
</View>
);
After successful authentication, the application should move from the authentication flow to the main application flow.
Login Screen
↓
Successful Login
↓
JWT Stored
↓
Auth State Updated
↓
Main App Navigator
↓
Student List / Dashboard
In the next authentication lessons, the login state will be connected to the complete navigation and authentication flow.
Step 1: Start the PHP API.
Step 2: Start the React Native application.
Step 3: Open the Login screen.
Step 4: Enter a registered email.
Step 5: Enter the correct password.
Step 6: Press Login.
Expected Result:
Login successful
↓
JWT received
↓
Token stored
↓
User information stored
↓
Main application opens
React Native
|
| email + password
↓
Axios
|
| POST /login.php
↓
PHP REST API
|
↓
Validate Input
|
↓
MySQL User
|
↓
password_verify()
|
↓
Generate JWT
|
↓
JSON Response
|
↓
React Native
|
↓
AsyncStorage
|
↓
Authentication State
|
↓
Main Application
The Login Screen connects the React Native application to the authentication API created in the backend.
password_verify() checks the password.password_verify().Question: Which function is used by the PHP login API to verify a user's password against the stored password hash?