Authentication System
Rhapsody provides a complete, self-contained authentication system built with
Doctrine ORM and the User entity. It handles
registration, login, logout, and password reset flows out of the box.
The authentication system is optional. You are not required to use it. You can build your own authentication system, or extend the built-in one.
Installation
Before using the authentication system, you must create the required database tables. Run the following command from your project root:
php rhapsody auth:install
This command creates the users and password_resets tables
based on the core User entity definition.
Running auth:install is required before any authentication
routes will work. The command will skip tables that already exist, so it's safe to run
multiple times.
How It Works
The authentication system is self-contained within the core framework:
- Controller:
Rhapsody\Core\Controllers\AuthController - Entity:
Rhapsody\Core\Entities\User - Views:
@core/auth/*.twig(login, register, forgot-password, reset-password) - Routes: Registered automatically from the core routes file
The following routes are available out of the box:
GET /login → Show login form
POST /login → Handle login
GET /register → Show registration form
POST /register → Handle registration
POST /logout → Log out
GET /forgot-password → Show forgot password form
POST /forgot-password → Send reset link
GET /reset-password/{token} → Show reset password form
POST /reset-password → Handle password reset
Using the Built-in Auth System
To use the default authentication system, simply ensure the routes are registered
(they are automatically loaded from the core) and point your users to /login
and /register.
In your templates, you can check if a user is logged in using the auth global:
{{% if auth.check %}}
Welcome back, {{ auth.user.name }}!
<a href="{{ base_url }}/logout">Logout</a>
{{% else %}}
<a href="{{ base_url }}/login">Login</a>
<a href="{{ base_url }}/register">Register</a>
{{% endif %}}
Protect routes with the auth middleware:
Router::get('/dashboard', [DashboardController::class, 'index'])->middleware('auth');
Extending the Built-in Auth System
If you want to add custom functionality (e.g., additional fields, custom validation, or custom email templates), you can extend the core controller:
// app/Controllers/AuthController.php
namespace App\Controllers;
use Rhapsody\Core\Controllers\AuthController as BaseAuthController;
use Rhapsody\Core\Request;
use Rhapsody\Core\Response;
class AuthController extends BaseAuthController
{
public function showRegisterForm(): Response
{
// Add custom data to the registration view
return $this->view('auth/register.twig', [
'terms_accepted' => false,
]);
}
public function register(Request $request): Response
{
// Add custom validation before parent registration
// Or override the entire registration logic
// return parent::register($request);
}
}
Then update your routes/web.php to point to your custom controller:
Router::get('/register', [App\Controllers\AuthController::class, 'showRegisterForm']);
Router::post('/register', [App\Controllers\AuthController::class, 'register']);
Extending the User Entity
To add custom fields to the user (e.g., phone, avatar), you can
extend the core entity:
// app/Entities/User.php
namespace App\Entities;
use Doctrine\ORM\Mapping as ORM;
use Rhapsody\Core\Entities\User as BaseUser;
#[ORM\Entity]
#[ORM\Table(name: 'users')]
class User extends BaseUser
{
#[ORM\Column(type: 'string', nullable: true)]
private ?string $phone = null;
public function getPhone(): ?string
{
return $this->phone;
}
public function setPhone(?string $phone): void
{
$this->phone = $phone;
}
}
Note: If you create your own App\Entities\User entity, it will
override the core User entity. Make sure your entity contains
all the fields the auth system expects, or extend the base entity as shown above.
Building Your Own Auth System (Alternative)
If the built-in authentication system doesn't meet your needs, you can build your own from scratch. This is recommended if you need:
- Integration with external authentication providers (LDAP, OAuth)
- Non-email based login (e.g., username only)
- Custom session management
- Different user storage (e.g., Redis, external API)
To build your own system, you can either:
-
Create a completely separate auth controller –
Create your own
LoginControllerandRegisterControllerinapp/Controllers/and use your own routes. - Create independent login/register controllers – This is the recommended approach as it keeps responsibilities separated.
// app/Controllers/LoginController.php
namespace App\Controllers;
use Rhapsody\Core\BaseController;
use Rhapsody\Core\Request;
use Rhapsody\Core\Response;
use Rhapsody\Core\Session;
class LoginController extends BaseController
{
public function showForm(): Response
{
return $this->view('auth/custom-login.twig');
}
public function login(Request $request): Response
{
// Your custom login logic here
// Validate credentials, set session, etc.
}
public function logout(): Response
{
Session::destroy();
return redirect('/login');
}
}
Then register your routes in routes/web.php:
Router::get('/login', [LoginController::class, 'showForm']);
Router::post('/login', [LoginController::class, 'login']);
Router::post('/logout', [LoginController::class, 'logout']);
Customizing Views
To override the default authentication views, create matching templates in your active theme:
views/themes/your-theme/auth/login.twig
views/themes/your-theme/auth/register.twig
views/themes/your-theme/auth/forgot-password.twig
views/themes/your-theme/auth/reset-password.twig
The framework will automatically use your theme's templates instead of the core ones if you have set up your own theme; otherwise make sure that your view files extend @core/layouts/main.twig. See the themes documentation for more details on using core and custom themes.
Security Features
The built-in authentication system includes the following security measures:
- Password hashing: Passwords are hashed using
password_hash()with BCRYPT - CSRF protection: All forms include a CSRF token field
- Session regeneration: Session ID is regenerated on login
- Password reset tokens: Tokens are hashed at rest (SHA-256) and time-limited (60 minutes)
- No user enumeration: Password reset responses are identical whether or not the email exists
- Single-use tokens: Reset tokens are deleted after successful use
- Old token invalidation: Requesting a new reset link invalidates all previous tokens
Check the FAQ or open an issue on GitHub.