first php projects
Login Authentication Flow
Store Password Hashes
Create a users table with email, password_hash, and a role or explicit admin flag. Generate the initial hash from a setup script:
<?php
declare(strict_types=1);
echo password_hash('change-this-password', PASSWORD_DEFAULT) . PHP_EOL;
Store the resulting hash, never the plaintext password.
Verify Credentials Without Leaking Accounts
<?php
declare(strict_types=1);
$authenticated = $user !== null
&& password_verify($submittedPassword, $user['password_hash']);
if (!$authenticated) {
throw new RuntimeException('Invalid email or password.');
}
session_regenerate_id(true);
$_SESSION['user_id'] = $user['id'];
Use the same public error for an unknown email and a wrong password. Record failures safely and add rate limiting so repeated guesses become slower or blocked.
Protect Every Admin Route
Create one guard such as requireAdmin(). Call it from product create, edit, and delete handlers before changing data. Hiding navigation links is useful presentation, but it is not authorization.
Logout should clear authenticated session state and invalidate the session cookie according to the session configuration used by the app.
Verify The Session Lifecycle
Check valid credentials, wrong password, unknown email, anonymous product edits, non-admin edits, logout, and repeated failures.
Practice
Practice: Build A Session Login Flow
Implement a password-and-session administrator login for the product manager.
Requirements
- Store password hashes created by
password_hash(). - Verify submitted passwords with
password_verify(). - Regenerate the session ID after successful login.
- Invalidate authenticated session state on logout.
- Do not store plaintext passwords.
- Do not rely on hidden navigation links for access control.
- Use secure cookie settings under HTTPS.
Protect every create, edit, and delete handler server-side. Verify that logout removes access and repeated failed attempts reach the chosen rate-limit path.
Show solution
Generate one development password hash and store it in a seeded user row. Normalize the email for lookup, verify the password with password_verify(), regenerate the session ID on success, and store the user ID in the session.
Use one server-side guard for every administrator action:
<?php
function requireAdmin(?array $currentUser): void
{
if ($currentUser === null || $currentUser['role'] !== 'admin') {
http_response_code(403);
exit('Forbidden');
}
}
Return the same public login failure for unknown users and wrong passwords. Verify session regeneration, rejected anonymous edits, rejected non-admin edits, logout, and rate-limited failures.