Proving who someone is. Registration, login, password hashing, sessions, and logout. No OAuth, no magic packages – just the secure defaults PHP gives you.
Password hashing
Never store plain-text passwords. Use password_hash() and password_verify(). PHP picks a sensible algorithm (currently bcrypt/argon2).
// On registration:
$hash = password_hash($password, PASSWORD_DEFAULT);
// On login:
if (!password_verify($password, $user['password_hash'])) {
$errors['email'] = 'Invalid credentials.';
}
Use the same error message for ‘unknown email’ and ‘wrong password’ – do not tell attackers which emails exist.
Session-based login
session_start();
function login(array $user): void
{
session_regenerate_id(true);
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_name'] = $user['name'];
}
function logout(): void
{
$_SESSION = [];
if (ini_get('session.use_cookies')) {
$p = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000, $p['path'], $p['domain'], $p['secure'], $p['httponly']);
}
session_destroy();
}
function currentUserId(): ?int
{
return isset($_SESSION['user_id']) ? (int) $_SESSION['user_id'] : null;
}
Registration flow
- Validate email format and password length
- Check email is not already taken (prepared statement)
- Hash password, insert user row
- Optionally log them in immediately or send to login page
Protecting routes
function requireAuth(): void
{
if (!currentUserId()) {
$_SESSION['flash'] = 'Please log in.';
header('Location: /login');
exit;
}
}
Call requireAuth() at the top of controllers that need a logged-in user.
Session cookie settings
In production, set session.cookie_httponly (default on in modern PHP), session.cookie_secure on HTTPS, and a reasonable session.gc_maxlifetime. Regenerate the session ID on login to reduce session fixation risk.
Authentication is solved problems territory. Use PHP’s built-ins, do not roll your own crypto, and keep sessions server-side.

