-
Notifications
You must be signed in to change notification settings - Fork 0
Authentication
A complete, session-based auth system — login, logout, registration, and password recovery by email. Lives in the repo from the start but stays inert: no route is registered by default, so it costs nothing until you uncomment its routes in routes.php.
| Piece | File |
|---|---|
| Mail sending (SMTP) |
config/mail.php — Mail::send($to, $subject, $html)
|
| Session auth helpers |
auth.php — auth_check(), auth_user(), auth_login(), auth_logout(), auth_require()
|
| User model | models/UserModel.php |
| Password reset tokens | models/PasswordResetModel.php |
| Controller | controllers/AuthController.php |
| Profile editing | controllers/ProfileController.php |
| Views |
views/auth/*.php, views/profile/edit.php
|
1. Install PHPMailer
composer require phpmailer/phpmailer2. Create the tables (MySQL/MariaDB shown; see the SQL comments for PostgreSQL types):
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE password_resets (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL,
token VARCHAR(255) NOT NULL,
expires_at TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);If you register the routes without these tables, you get a clear 500 (or the full exception in debug mode) the moment a query touches users — that's the intended signal this step was skipped.
3. Wire it into the bootstrap (core/App.php, alongside the other config/ requires):
require_once __DIR__ . '/../config/mail.php';
require_once __DIR__ . '/../auth.php';4. Configure SMTP in .env:
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=no-reply@example.com
MAIL_FROM_NAME="${APP_NAME}"5. Uncomment the routes in routes.php:
'GET /login' => 'AuthController@showLogin',
'POST /login' => 'AuthController@login',
'POST /logout' => 'AuthController@logout',
'GET /register' => 'AuthController@showRegister',
'POST /register' => 'AuthController@register',
'GET /forgot-password' => 'AuthController@showForgot',
'POST /forgot-password' => 'AuthController@sendResetLink',
'GET /reset-password/{token}' => 'AuthController@showReset',
'POST /reset-password/{token}' => 'AuthController@resetPassword',
'GET /profile' => 'ProfileController@edit',
'POST /profile' => 'ProfileController@update',class DashboardController extends Controller
{
public function index(): void
{
auth_require(); // redirects to /login if not authenticated
$user = auth_user();
$this->view('dashboard/index', ['user' => $user]);
}
}auth_require() saves the current URL before redirecting, so login sends the user back to where they wanted to go.
includes/head.php already renders a user dropdown (avatar, profile link, logout) when auth_check() is true, and a "Log In" button otherwise — no extra wiring once the routes are active.
- User submits their email at
/forgot-password. - A random token is generated, SHA-256-hashed, stored with a 1-hour expiry. The plain token is emailed (not the hash) — standard practice, so a leaked table alone can't be used to reset accounts.
- The same "link sent" message shows whether or not the email exists, to avoid leaking registered emails.
-
/reset-password/{token}validates the token before allowing a new password.
/profile lets a logged-in user change their name and, optionally, their password (current password required) — not their email, by design, to keep this simple. Email changes typically need their own re-verification flow.
Remove the two require_once lines from App.php, comment/delete the routes in routes.php, and (optionally) delete the files listed above. Nothing else in the project depends on them.