Lesson 68 of 158 – User Login API
68%

User Login API

A user login API allows a mobile application to send a user's email and password to the server. The PHP API checks the user in the database, verifies the password hash, and returns a suitable JSON response. In later lessons, we will use this login process with token and JWT authentication.

Note: Never compare a plain password directly with the password value stored in the database. Use password_verify() to verify the password against its stored hash.

1. What is User Login?

User login is the process of verifying the identity of a registered user.

Mobile App
    ↓
Login Form
    ↓
PHP Login API
    ↓
Find User
    ↓
Verify Password
    ↓
Login Result

2. Login API Flow

A typical login process follows these steps:

  1. User enters email and password.
  2. React Native sends the credentials as JSON.
  3. PHP reads the JSON request.
  4. PHP validates the input.
  5. PHP searches for the user.
  6. PHP verifies the password hash.
  7. The API returns a JSON response.

3. Login Endpoint

A login API can use a POST endpoint.

POST /api/login.php

POST is used because the client is sending login credentials to the server.

4. Login JSON Request

The mobile application can send the email and password as JSON.

{
    "email": "rahul@example.com",
    "password": "MyPassword123"
}

5. Set JSON Response Header

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

The API will use JSON for its response.

6. Create PDO Connection

$pdo = new PDO(
    "mysql:host=localhost;dbname=schooldb",
    "root",
    ""
);

$pdo->setAttribute(
    PDO::ATTR_ERRMODE,
    PDO::ERRMODE_EXCEPTION
);

7. Read Login JSON

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

$data = json_decode(
    $input,
    true
);

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

8. Check JSON Errors

if (json_last_error() !== JSON_ERROR_NONE) {

    http_response_code(400);

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

    exit;
}

9. Get Email and Password

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

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

The null coalescing operator prevents errors when a field is missing.

10. Validate Required Fields

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

    http_response_code(400);

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

    exit;
}

11. Validate Email

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

    http_response_code(422);

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

    exit;
}

12. Find User by Email

The API can search for the registered user using a prepared statement.

$stmt = $pdo->prepare(
    "SELECT *
     FROM users
     WHERE email = ?"
);

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

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

13. Check User Exists

if (!$user) {

    http_response_code(401);

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

    exit;
}

A generic authentication message avoids revealing whether a particular email address is registered.

14. Verify Password

The stored password is a hash. Use password_verify() to compare the entered 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;
}

15. Why Use password_verify()?

The database stores a password hash, not the original password.

Entered Password
       ↓
password_verify()
       ↓
Stored Password Hash
       ↓
Match?
  ┌────┴────┐
 Yes       No
  ↓          ↓
Continue   Reject

The password does not need to be decrypted.

16. Successful Login Response

After successful password verification, the API can return a JSON response.

http_response_code(200);

echo json_encode([
    "success" => true,
    "message" => "Login successful"
]);

In later authentication lessons, this response will be extended with a token.

17. Return Safe User Information

If user information is returned, only send the fields the client needs.

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

Do not include the password hash in the response.

18. Do Not Return Password

Avoid returning the complete database user record if it contains the password hash.

// Avoid

echo json_encode([
    "user" => $user
]);

Instead, select or construct only the safe fields that should be returned.

19. Create an Error Function

function sendError(
    int $status,
    string $message
) {

    http_response_code($status);

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

    exit;
}

20. Use Generic Login Error

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

    sendError(
        401,
        "Invalid email or password"
    );
}

Using one general message for both cases avoids unnecessarily revealing whether an account exists.

21. Handle Database Exceptions

try {

    $stmt = $pdo->prepare(
        "SELECT *
         FROM users
         WHERE email = ?"
    );

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

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

} catch (PDOException $e) {

    error_log(
        $e->getMessage()
    );

    sendError(
        500,
        "Login service unavailable"
    );
}

22. Complete Basic Login Logic

$stmt = $pdo->prepare(
    "SELECT *
     FROM users
     WHERE email = ?"
);

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

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

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

    sendError(
        401,
        "Invalid email or password"
    );
}

http_response_code(200);

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

23. Test Login with Postman

You can test the login API using Postman.

  1. Select POST.
  2. Enter the login API URL.
  3. Open the Body tab.
  4. Select raw.
  5. Select JSON.
  6. Enter the email and password.
  7. Click Send.
{
    "email": "rahul@example.com",
    "password": "MyPassword123"
}

24. React Native Login Request

React Native can send the login information using fetch().

fetch(
    "https://example.com/api/login.php",
    {
        method: "POST",

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

        body: JSON.stringify({
            email: email,
            password: password
        })
    }
)
.then(response => response.json())
.then(data => {

    console.log(data);

});

25. Handle Login Response

fetch(url, options)
.then(async response => {

    const data =
        await response.json();

    if (!response.ok) {

        throw new Error(
            data.message
        );
    }

    return data;

})
.then(data => {

    console.log(
        "Login successful"
    );

})
.catch(error => {

    console.log(
        error.message
    );

});

26. Login Flow for Mobile App

Login Screen
     ↓
Email + Password
     ↓
Fetch / Axios
     ↓
PHP Login API
     ↓
Find User
     ↓
password_verify()
     ↓
Authentication Result
     ↓
JSON Response
     ↓
React Native

Later, the successful response can contain a JWT token for protected API requests.

27. Login Success and Failure

Situation Status Response
Login successful 200 Login successful
Missing data 400 Required fields message
Invalid email format 422 Invalid email
Invalid credentials 401 Invalid email or password
Database problem 500 Server error message

28. Complete User Login API

<?php

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

function sendError(
    int $status,
    string $message
) {

    http_response_code($status);

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

    exit;
}

try {

    $pdo = new PDO(
        "mysql:host=localhost;dbname=schooldb",
        "root",
        ""
    );

    $pdo->setAttribute(
        PDO::ATTR_ERRMODE,
        PDO::ERRMODE_EXCEPTION
    );

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

    $data = json_decode(
        $input,
        true
    );

    if (
        json_last_error() !==
        JSON_ERROR_NONE
    ) {

        sendError(
            400,
            "Invalid JSON data"
        );
    }

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

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

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

        sendError(
            400,
            "Email and password are required"
        );
    }

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

        sendError(
            422,
            "Invalid email address"
        );
    }

    $stmt = $pdo->prepare(
        "SELECT *
         FROM users
         WHERE email = ?"
    );

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

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

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

        sendError(
            401,
            "Invalid email or password"
        );
    }

    http_response_code(200);

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

} catch (PDOException $e) {

    error_log(
        $e->getMessage()
    );

    sendError(
        500,
        "Login service unavailable"
    );
}

?>

29. Login API Best Practices

  • Use POST for login requests.
  • Send login data using HTTPS.
  • Validate email and password input.
  • Use prepared statements to find the user.
  • Use password_verify() to check the password.
  • Never compare the plain password directly with the stored hash.
  • Never return the password or password hash.
  • Use a generic authentication error message.
  • Handle database exceptions safely.
  • Return suitable HTTP status codes.
  • Return consistent JSON responses.
  • Use tokens for protected API access in the next authentication stage.

30. User Login API Summary

The user login API receives email and password from the mobile application. PHP validates the request, searches for the user, verifies the password hash using password_verify(), and returns a JSON response. In the next lessons, this successful login process will be extended with authentication tokens.

React Native
     ↓
POST Login JSON
     ↓
PHP Login API
     ↓
Validate Input
     ↓
Find User
     ↓
password_verify()
     ↓
Success / Failure
     ↓
JSON Response
     ↓
Next: Token Authentication

📌 Key Points

  • A login API verifies a registered user's identity.
  • Login APIs normally use the POST method.
  • JSON can be read using php://input and json_decode().
  • Email and password should be validated.
  • The user can be found using a prepared SQL statement.
  • Passwords should be stored as hashes.
  • password_verify() should be used to verify passwords.
  • Never compare a plain password directly with a stored hash.
  • Do not return passwords or password hashes in API responses.
  • HTTP 401 can be used for invalid authentication credentials.
  • HTTP 400 can be used for missing required input.
  • HTTP 422 can be used for invalid input such as an invalid email.
  • HTTP 500 can be used for unexpected server errors.
  • Database exceptions should be handled safely.
  • React Native can call the login API using Fetch or Axios.
  • Successful login will later be extended with token authentication.

🧠 Quick Quiz

Question: Which PHP function should be used to verify a user's entered password against the password hash stored in the database?