Overview
SaaS Starter Vue uses Laravel Fortify for authentication, providing:
- Email/password login
- User registration
- Password reset via email
- Email verification
- Two-factor authentication (2FA)
- Password confirmation
Authentication works on both central and tenant domains with isolated user databases.
Authentication Features
Fortify features are configured in config/fortify.php:
// config/fortify.php:146
'features' => [
Features::registration(),
Features::resetPasswords(),
Features::emailVerification(),
Features::twoFactorAuthentication([
'confirm' => true,
'confirmPassword' => true,
]),
],
Login
Central Domain Login
System administrators log in at the central domain:
// routes/web.php:110
Route::get('/login', [AuthenticatedSessionController::class, 'create'])
->middleware(['guest:'.config('fortify.guard')])
->name('login');
Route::post('/login', [AuthenticatedSessionController::class, 'store'])
->middleware(['guest:'.config('fortify.guard')])
->name('login.store');
Login Flow:
Visit Login Page
Navigate to /auth/login on the central domain
Enter Credentials
Provide your email and password
Two-Factor (if enabled)
Enter your 2FA code if two-factor authentication is enabled
Redirect to Dashboard
Successfully authenticated users are redirected to /dashboard
Tenant Domain Login
Tenant users log in at their subdomain:
// routes/tenant.php:28
Route::get('login', [\App\Http\Controllers\Tenant\Auth\LoginController::class, 'create'])
->name('tenant.login');
Route::post('login', [\App\Http\Controllers\Tenant\Auth\LoginController::class, 'store'])
->name('tenant.login.store');
Tenant authentication is completely isolated. Users cannot log in to the central domain with tenant credentials and vice versa.
Rate Limiting
Login attempts are throttled to prevent brute force attacks:
// config/fortify.php:117
'limiters' => [
'login' => 'login',
'two-factor' => 'two-factor',
],
Default: 5 attempts per minute per email/IP combination.
Registration
User Registration
New users can register if the registration feature is enabled:
// routes/web.php:146
Route::get('/register', [RegisteredUserController::class, 'create'])
->middleware(['guest:'.config('fortify.guard')])
->name('register');
Route::post('/register', [RegisteredUserController::class, 'store'])
->middleware(['guest:'.config('fortify.guard')]);
Guest Tenant Registration
Prospective customers can create their own tenant workspace:
// routes/web.php:45
Route::get('guest-register', [GuestRegisterController::class, 'index'])
->middleware(['guest'])
->name('guest-register.index');
Route::post('guest-register', [GuestRegisterController::class, 'store'])
->middleware(['guest', 'throttle:6,1'])
->name('guest-register.store');
Registration Process:
Fill Registration Form
Provide company name, subdomain, owner details, and password
Tenant Provisioning
System automatically creates tenant database and domain
Admin User Creation
Owner account is created with admin privileges
Redirect to Tenant
User is redirected to their new tenant subdomain login page
Guest registration can be disabled via system settings. Check the guest_registration setting in the database.
Password Reset
Request Password Reset
Users can request a password reset link via email:
// routes/web.php:125
Route::get('/forgot-password', [PasswordResetLinkController::class, 'create'])
->middleware(['guest:'.config('fortify.guard')])
->name('password.request');
Route::post('/forgot-password', [PasswordResetLinkController::class, 'store'])
->middleware(['guest:'.config('fortify.guard')])
->name('password.email');
Reset Password Flow
Request Reset Link
User enters their email at /auth/forgot-password
Email Sent
System sends password reset link to the user’s email
Click Reset Link
User clicks the link in their email
Set New Password
User enters and confirms their new password
Password Updated
Password is updated and user can log in with new credentials
// routes/web.php:129
Route::get('/reset-password/{token}', [NewPasswordController::class, 'create'])
->middleware(['guest:'.config('fortify.guard')])
->name('password.reset');
Route::post('/reset-password', [NewPasswordController::class, 'store'])
->middleware(['guest:'.config('fortify.guard')])
->name('password.update');
Email Verification
Verification Required
Many routes require email verification:
Route::get('dashboard', [DashboardController::class, 'index'])
->middleware(['auth', 'verified'])
->name('dashboard');
Verification Process
User Registers
New user creates an account
Verification Email Sent
System automatically sends verification email
Click Verification Link
User clicks the link in their email
Email Verified
User’s email is marked as verified and they gain full access
// routes/web.php:158
Route::get('/email/verify', [EmailVerificationPromptController::class, '__invoke'])
->middleware([config('fortify.auth_middleware', 'auth').':'.config('fortify.guard')])
->name('verification.notice');
Route::get('/email/verify/{id}/{hash}', [VerifyEmailController::class, '__invoke'])
->middleware(['auth', 'signed', 'throttle:6,1'])
->name('verification.verify');
Resend Verification Email
Users can request a new verification email:
// routes/web.php:167
Route::post('/email/verification-notification', [EmailVerificationNotificationController::class, 'store'])
->middleware(['auth', 'throttle:6,1'])
->name('verification.send');
User Model
The User model includes authentication traits:
// app/Models/System/User.php:12
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable, TwoFactorAuthenticatable;
protected $fillable = [
'name',
'email',
'password',
];
protected $hidden = [
'password',
'two_factor_secret',
'two_factor_recovery_codes',
'remember_token',
];
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'two_factor_confirmed_at' => 'datetime',
];
}
}
Password Hashing
Passwords are automatically hashed using the password cast:
No need to manually hash passwords when creating users.
Profile Management
Users can update their name and email:
// routes/web.php:174
Route::put('/user/profile-information', [ProfileInformationController::class, 'update'])
->middleware([config('fortify.auth_middleware', 'auth').':'.config('fortify.guard')])
->name('user-profile-information.update');
Update Password
Authenticated users can change their password:
// routes/web.php:181
Route::put('/user/password', [PasswordController::class, 'update'])
->middleware([config('fortify.auth_middleware', 'auth').':'.config('fortify.guard')])
->name('user-password.update');
Password Confirmation
Sensitive operations require password confirmation:
// routes/web.php:188
Route::get('/user/confirm-password', [ConfirmablePasswordController::class, 'show'])
->middleware([config('fortify.auth_middleware', 'auth').':'.config('fortify.guard')])
->name('password.confirm');
Route::post('/user/confirm-password', [ConfirmablePasswordController::class, 'store'])
->middleware([config('fortify.auth_middleware', 'auth').':'.config('fortify.guard')]);
Use the password.confirm middleware on sensitive routes:
Route::post('/sensitive-action', [Controller::class, 'action'])
->middleware(['auth', 'password.confirm']);
Logout
Users can log out from both central and tenant domains:
// Central domain
// routes/web.php:119
Route::post('/logout', [AuthenticatedSessionController::class, 'destroy'])
->name('logout');
// Tenant domain
// routes/tenant.php:37
Route::post('logout', [\App\Http\Controllers\Tenant\Auth\LoginController::class, 'destroy'])
->name('tenant.logout');
Configuration
Fortify Settings
Key configuration options in config/fortify.php:
The authentication guard to use
Password broker for reset functionality
Field used for authentication (email)
home
string
default:"/dashboard"
Redirect path after successful authentication
Enable view routes for authentication pages
Customizing Redirects
Change the post-login redirect:
// config/fortify.php:76
'home' => '/dashboard',
Security Best Practices
Always use HTTPS in production to protect user credentials during transmission.
- Use Strong Passwords - Enforce password requirements with validation rules
- Enable 2FA - Require two-factor authentication for admin accounts
- Rate Limiting - Prevents brute force attacks (enabled by default)
- Email Verification - Verify user email addresses before granting full access
- HTTPS Only - Never transmit credentials over unencrypted connections
- Password Hashing - Laravel uses bcrypt by default (secure)
Testing Authentication
Use Laravel’s testing helpers:
// Acting as authenticated user
$this->actingAs($user)
->get('/dashboard')
->assertStatus(200);
// Testing login
$this->post('/auth/login', [
'email' => 'user@example.com',
'password' => 'password',
])->assertRedirect('/dashboard');
// Testing guest middleware
$this->get('/dashboard')
->assertRedirect('/auth/login');
Common Issues
Users can't receive password reset emails
Check your mail configuration in .env. For local development, use a service like Mailtrap or Laravel’s log mail driver:
Login redirects to wrong domain
For tenant logins, ensure you’re using the correct login route (tenant.login) instead of the central domain route.
Rate limiting blocking legitimate users
Adjust rate limiting in config/fortify.php or clear rate limits manually during development.