Implement authentication with Laravel Sanctum and authorization with Policies and Gates. Use when setting up login/registration, implementing API token auth, SPA cookie auth, creating policies, defining gates, or building role-based access control. Triggers on authentication, authorization, login, Sanctum, policy, gate, RBAC, permission, or middleware.
Implement authentication with Laravel Sanctum and authorization with Policies and Gates. Use when setting up login/registration, implementing API token auth, SPA cookie auth, creating policies, defining gates, or building role-based access control. Triggers on authentication, authorization, login, Sanctum, policy, gate, RBAC, permission, or middleware.
Inertia apps use cookie-based session authentication by default. Sanctum provides the
EnsureFrontendRequestsAreStateful middleware that enables this for same-domain SPAs.
Policies encapsulate authorization logic for a specific model. Laravel auto-discovers policies
when they follow the App\Policies\{Model}Policy convention.
Full CRUD Policy Template
// app/Policies/ProjectPolicy.phpnamespaceApp\Policies;
useApp\Enums\Role;
useApp\Models\Project;
useApp\Models\User;
useIlluminate\Auth\Access\HandlesAuthorization;
classProjectPolicy{
useHandlesAuthorization;
/**
* Run before any other check. Return true to grant all, null to continue.
*/publicfunctionbefore(User $user, string$ability): ?bool{
if ($user->role === Role::SuperAdmin) {
returntrue;
}
returnnull; // fall through to specific check
}
publicfunctionviewAny(User $user): bool{
returntrue; // all authenticated users can list
}
publicfunctionview(User $user, Project $project): bool{
return$user->id === $project->user_id
|| $project->team->users->contains($user);
}
publicfunctioncreate(User $user): bool{
return$user->role !== Role::Viewer;
}
publicfunctionupdate(User $user, Project $project): bool{
return$user->id === $project->user_id;
}
publicfunctiondelete(User $user, Project $project): bool{
return$user->id === $project->user_id;
}
publicfunctionrestore(User $user, Project $project): bool{
return$user->id === $project->user_id;
}
publicfunctionforceDelete(User $user, Project $project): bool{
return$user->role === Role::Admin && $user->id === $project->user_id;
}
}
Using Policies in Controllers
// In a controllerpublicfunctionupdate(UpdateProjectRequest $request, Project $project)
{
$this->authorize('update', $project);
// or: Gate::authorize('update', $project);// or in the request: $request->user()->can('update', $project);$project->update($request->validated());
returnback()->with('success', 'Project updated.');
}
// Controller-level authorization via middlewareclassProjectControllerextendsController{
publicfunction__construct()
{
$this->authorizeResource(Project::class, 'project');
}
}
Gates are closures that determine if a user can perform a given action. Use gates for actions
that are not tied to a specific model (e.g., "access admin panel").
// In controllersif (Gate::allows('access-admin')) {
// show admin panel
}
// In Blade/Inertia middlewareGate::authorize('access-admin'); // throws AuthorizationException// Via middleware on routesRoute::get('/admin', AdminController::class)->middleware('can:access-admin');
// User modelclassUserextendsAuthenticatableimplementsMustVerifyEmail{
// ...
}
// RoutesRoute::middleware(['auth', 'verified'])->group(function () {
// Only verified users can access these
});
8. Testing Authentication & Authorization
Testing Login Flow
// tests/Feature/Auth/LoginTest.phpuseApp\Models\User;
it('renders the login page', function () {
$this->get('/login')
->assertOk()
->assertInertia(fn ($page) => $page->component('Auth/Login'));
});
it('authenticates a user with valid credentials', function () {
$user = User::factory()->create();
$this->post('/login', [
'email' => $user->email,
'password' => 'password',
])->assertRedirect('/dashboard');
$this->assertAuthenticated();
});
it('rejects invalid credentials', function () {
$user = User::factory()->create();
$this->post('/login', [
'email' => $user->email,
'password' => 'wrong-password',
]);
$this->assertGuest();
});
Testing Policies
// tests/Unit/Policies/ProjectPolicyTest.phpuseApp\Enums\Role;
useApp\Models\Project;
useApp\Models\User;
useApp\Policies\ProjectPolicy;
it('allows the owner to update a project', function () {
$user = User::factory()->create();
$project = Project::factory()->for($user)->create();
$policy = newProjectPolicy();
expect($policy->update($user, $project))->toBeTrue();
});
it('denies non-owners from updating a project', function () {
$owner = User::factory()->create();
$other = User::factory()->create();
$project = Project::factory()->for($owner)->create();
$policy = newProjectPolicy();
expect($policy->update($other, $project))->toBeFalse();
});
it('grants super admins all abilities via before()', function () {
$admin = User::factory()->create(['role' => Role::SuperAdmin]);
$project = Project::factory()->create();
$policy = newProjectPolicy();
expect($policy->before($admin, 'delete'))->toBeTrue();
});
Testing Middleware Authorization
it('prevents unauthorized access to admin routes', function () {
$user = User::factory()->create(['role' => Role::Viewer]);
$this->actingAs($user)
->get('/admin/users')
->assertForbidden();
});
it('allows admins to access admin routes', function () {
$admin = User::factory()->create(['role' => Role::Admin]);
$this->actingAs($admin)
->get('/admin/users')
->assertOk();
});