Authentication Setup
Scaffold a complete authentication system with login, register, dashboard, settings, and role management using a single command.
Overview
The auth:setup command generates a fully-functional authentication system into your project, including:
- User model with Doctrine ORM, password hashing, and remember tokens
- Login & Register controllers and pages
- Dashboard at
/v1/dashboardwith sidebar layout - Settings page with profile and password management
- Role management (if authorization module is installed)
- Database migrations for users and roles tables
- Dark-themed UI matching the starter template
Quick Start (Auto Setup)
The fastest way to get authentication running. When you add the database module, you'll be offered an auto setup option:
php craftsman add database
After the database module installs, you'll see:
$ php craftsman add database
Adding module: database
Running: composer require zephyrphp/database
...
Module 'database' has been installed and enabled.
Would you like to configure the database connection now? (yes/no) [yes]: yes
...
Authentication System
---------------------
Set up a complete auth system with:
- User model with roles
- Login & Register pages
- Dashboard (/v1/dashboard)
- Settings page (profile, password, roles)
How would you like to proceed?:
[0] Auto setup (Recommended) — Install auth + authorization and scaffold everything
[1] Manual — I'll add modules myself later
Choosing Auto setup will:
- Install the
zephyrphp/authmodule - Install the
zephyrphp/authorizationmodule - Run
auth:setupto scaffold everything
Auto setup installs both authentication and authorization modules, then scaffolds the entire auth system. You'll have a working login, register, and dashboard in under a minute.
Manual Setup
If you prefer step-by-step control, install modules individually:
# Step 1: Add the database module (if not already added)
php craftsman add database
# Step 2: Add the auth module
php craftsman add auth
# Step 3: (Optional) Add authorization for role management
php craftsman add authorization
# Step 4: Run the scaffold
php craftsman auth:setup
auth:setup Command
The scaffolding command that generates all authentication files.
php craftsman auth:setup
Prerequisites
Before running auth:setup, ensure you have:
- Database module installed and enabled:
php craftsman add database - Auth module installed and enabled:
php craftsman add auth - Database connection configured:
php craftsman db:setup
Authentication Type Selection
The wizard asks which authentication type you want:
Authentication Type
-------------------
Select authentication type:
[0] Session-based authentication (traditional)
[1] JWT Token authentication (API)
[2] Both (Session + JWT)
| Type | Best For | How It Works |
|---|---|---|
| Session | Web apps, server-rendered pages | Stores user ID in server-side session, cookie-based |
| JWT | APIs, SPAs, mobile apps | Stateless tokens in Authorization header |
| Both | Full-stack apps with API | Session for web, JWT for API endpoints |
Generated Files
The command generates the following files into your project, using your app's namespace (detected from composer.json):
Models
| File | Description |
|---|---|
app/Models/User.php |
Doctrine entity extending Model, implements AuthenticatableInterface |
app/Models/Role.php (if authorization) |
Role entity with name, slug, description |
Controllers
| File | Methods |
|---|---|
app/Controllers/Auth/LoginController.php |
showLoginForm(), login(), logout() |
app/Controllers/Auth/RegisterController.php |
showRegisterForm(), store() |
app/Controllers/Dashboard/DashboardController.php |
index() |
app/Controllers/Dashboard/SettingsController.php |
index(), updateProfile(), updatePassword(), updateRoles() |
Twig Templates
| File | Description |
|---|---|
pages/auth/login.twig | Login form with email, password, remember me |
pages/auth/register.twig | Registration form with name, email, password, confirmation |
pages/layouts/dashboard.twig | Dashboard layout with sidebar, topbar, user info |
pages/dashboard/index.twig | Dashboard home with welcome message and stats |
pages/dashboard/settings.twig | Settings with Profile, Password, and Roles tabs |
Other Files
| File | Description |
|---|---|
public/assets/css/dashboard.css | Dashboard and auth page styles (dark theme) |
database/migrations/..._create_users_table.php | Users table migration |
database/migrations/..._create_roles_tables.php (if authorization) | Roles + role_user pivot table migration |
routes/web.php (appended) | Auth and dashboard route definitions |
Generated Routes
The following routes are appended to your routes/web.php:
Authentication Routes
| Method | URI | Action | Middleware |
|---|---|---|---|
| GET | /login |
Show login form | GuestMiddleware |
| POST | /login |
Authenticate user | GuestMiddleware |
| GET | /register |
Show register form | GuestMiddleware |
| POST | /register |
Create account | GuestMiddleware |
| POST | /logout |
Logout user | — |
Dashboard Routes
All dashboard routes are grouped under /v1/dashboard and protected by AuthMiddleware:
| Method | URI | Action |
|---|---|---|
| GET | /v1/dashboard |
Dashboard home |
| GET | /v1/dashboard/settings |
Settings page |
| POST | /v1/dashboard/settings/profile |
Update profile (name, email) |
| POST | /v1/dashboard/settings/password |
Change password |
| POST | /v1/dashboard/settings/roles |
Update roles (if authorization) |
// routes/web.php (generated)
use App\Controllers\Auth\LoginController;
use App\Controllers\Auth\RegisterController;
use App\Controllers\Dashboard\DashboardController;
use App\Controllers\Dashboard\SettingsController;
use ZephyrPHP\Middleware\AuthMiddleware;
use ZephyrPHP\Middleware\GuestMiddleware;
// Guest routes
Route::get('/login', [LoginController::class, 'showLoginForm'])->middleware([GuestMiddleware::class]);
Route::post('/login', [LoginController::class, 'login'])->middleware([GuestMiddleware::class]);
Route::get('/register', [RegisterController::class, 'showRegisterForm'])->middleware([GuestMiddleware::class]);
Route::post('/register', [RegisterController::class, 'store'])->middleware([GuestMiddleware::class]);
Route::post('/logout', [LoginController::class, 'logout']);
// Dashboard (authenticated)
Route::group(['prefix' => '/v1/dashboard', 'middleware' => [AuthMiddleware::class]], function () {
Route::get('/', [DashboardController::class, 'index']);
Route::get('/settings', [SettingsController::class, 'index']);
Route::post('/settings/profile', [SettingsController::class, 'updateProfile']);
Route::post('/settings/password', [SettingsController::class, 'updatePassword']);
});
User Model
The generated User model extends ZephyrPHP\Database\Model and implements the authentication interface:
<?php
namespace App\Models;
use Doctrine\ORM\Mapping as ORM;
use ZephyrPHP\Database\Model;
use ZephyrPHP\Auth\AuthenticatableInterface;
use ZephyrPHP\Auth\Authenticatable;
use ZephyrPHP\Authorization\Traits\HasRoles; // if authorization module
#[ORM\Entity]
#[ORM\Table(name: 'users')]
#[ORM\HasLifecycleCallbacks]
class User extends Model implements AuthenticatableInterface
{
use Authenticatable;
use HasRoles; // if authorization module
#[ORM\Column(type: 'string', length: 255)]
protected string $name = '';
#[ORM\Column(type: 'string', length: 180, unique: true)]
protected string $email = '';
#[ORM\Column(type: 'string', length: 255)]
protected string $password = '';
#[ORM\Column(type: 'string', length: 100, nullable: true)]
protected ?string $rememberToken = null;
// Getters and setters generated automatically...
}
The generated code uses your project's namespace from composer.json. If your app is named MyApp, the User model will use namespace MyApp\Models; instead of App\Models.
Using Authentication
Check if Authenticated
<?php
use ZephyrPHP\Auth\Auth;
// In a controller
if (Auth::check()) {
$user = Auth::user();
}
// Using helpers
if (auth()->check()) {
$user = user();
}
Login with Credentials
<?php
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
// Success - user is now authenticated
return $this->redirect('/v1/dashboard');
}
// Failed - credentials don't match
In Twig Templates
<a href="/login">Login</a>
Manual Login/Logout
<?php
// Login a user directly (without credentials)
Auth::login($user);
// Login with remember me
Auth::login($user, remember: true);
// Login by ID
Auth::loginUsingId($userId);
// Logout
Auth::logout();
Role Management
If the authorization module is installed, the User model includes the HasRoles trait and a Roles tab appears on the settings page.
HasRoles Trait Methods
<?php
$user = Auth::user();
// Check if user has a role
if ($user->hasRole('admin')) { ... }
// Check for any/all roles
$user->hasAnyRole(['admin', 'editor']);
$user->hasAllRoles(['admin', 'editor']);
// Assign a role
$role = Role::findOneBy(['name' => 'admin']);
$user->assignRole($role);
$user->save();
// Remove a role
$user->removeRole('admin');
$user->save();
// Get all role names
$roleNames = $user->getRoleNames(); // ['admin', 'editor']
// Sync roles (replace all)
$user->syncRoles([$adminRole, $editorRole]);
$user->save();
Authorization with Gates
<?php
use ZephyrPHP\Authorization\Gate;
// Define abilities
Gate::define('edit-post', function($user, $post) {
return $user->getId() === $post->getUserId();
});
// Check abilities
if (Gate::allows('edit-post', $post)) { ... }
if (Gate::denies('edit-post', $post)) { ... }
// Authorize (throws exception if denied)
Gate::authorize('edit-post', $post);
// Using helpers
if (can('edit-post', $post)) { ... }
Protecting Routes
Authentication Middleware
The framework provides built-in middleware. No need to create your own:
<?php
use ZephyrPHP\Middleware\AuthMiddleware;
use ZephyrPHP\Middleware\GuestMiddleware;
// Protect a single route
Route::get('/profile', [ProfileController::class, 'show'])
->middleware([AuthMiddleware::class]);
// Protect a group
Route::group(['prefix' => '/admin', 'middleware' => [AuthMiddleware::class]], function () {
Route::get('/users', [AdminController::class, 'users']);
Route::get('/reports', [AdminController::class, 'reports']);
});
// Guest-only (redirect logged-in users away)
Route::get('/login', [LoginController::class, 'showForm'])
->middleware([GuestMiddleware::class]);
Authorization Middleware
<?php
use ZephyrPHP\Middleware\CanMiddleware;
// Check ability
Route::get('/posts/{id}/edit', [PostController::class, 'edit'])
->middleware([new CanMiddleware('edit-post', 'post')]);
// Rate limiting
use ZephyrPHP\Middleware\RateLimitMiddleware;
Route::post('/login', [LoginController::class, 'login'])
->middleware([RateLimitMiddleware::loginAttempts(5)]);
Customization
Adding Custom User Fields
Edit the generated app/Models/User.php to add fields:
<?php
// Add to User model
#[ORM\Column(type: 'string', length: 20, nullable: true)]
protected ?string $phone = null;
#[ORM\Column(type: 'boolean')]
protected bool $isActive = true;
#[ORM\Column(type: 'string', nullable: true)]
protected ?string $avatar = null;
// Add getters/setters...
Custom Login Validation
<?php
// In LoginController::login()
if (Auth::attempt($credentials)) {
$user = Auth::user();
// Only allow active users
if (!$user->isActive) {
Auth::logout();
$this->flash('errors', ['email' => 'Your account has been deactivated.']);
$this->back();
return;
}
$this->redirect('/v1/dashboard');
}
Extending the Dashboard
Add new pages to the dashboard by creating controllers and updating the routes and sidebar layout:
// Add to routes/web.php inside the dashboard group:
Route::group(['prefix' => '/v1/dashboard', 'middleware' => [AuthMiddleware::class]], function () {
Route::get('/', [DashboardController::class, 'index']);
Route::get('/settings', [SettingsController::class, 'index']);
// ... existing routes ...
// Add your own:
Route::get('/posts', [PostController::class, 'index']);
Route::get('/analytics', [AnalyticsController::class, 'index']);
});
Then add navigation links in pages/layouts/dashboard.twig:
<nav class="sidebar-nav">
<ul>
<li><a href="/v1/dashboard">Dashboard</a></li>
<li><a href="/v1/dashboard/posts">Posts</a></li>
<li><a href="/v1/dashboard/analytics">Analytics</a></li>
<li><a href="/v1/dashboard/settings">Settings</a></li>
</ul>
</nav>
After Setup
Once auth:setup has completed:
- Create the database tables:
php craftsman db:schema - Start the dev server:
php craftsman serve - Visit
/registerto create your first account - Visit
/v1/dashboardto see the dashboard
Troubleshooting
Login Always Fails
# Ensure passwords are hashed with Hash::make()
$user->setPassword(Hash::make($password));
# NOT plain text:
$user->setPassword($password);
# Check database has users
SELECT * FROM users;
Session Lost After Login
# Check .env has session config
SESSION_DRIVER=file
SESSION_LIFETIME=120
# Ensure session module is enabled in config/modules.php
'session' => true,
Infinite Redirect Loop
# Login and register routes MUST use GuestMiddleware
# so authenticated users are redirected away from login page
# instead of being redirected back in a loop.
# Check routes/web.php has:
Route::get('/login', [...])->middleware([GuestMiddleware::class]);
Next Steps
- Learn about Middleware for request filtering
- Read the Security guide for authorization, encryption, and best practices
- Explore Module Management to add more features
- Return to Craftsman CLI Overview