Every app needs to know who the user is and what they may do, and getting either wrong is a security hole.
Authentication answers "who are you?". The fastest start is a starter kit (React, Vue or Livewire), which generates login, registration, password reset and email verification. Underneath they use simple APIs you can also call yourself:
Auth::attempt(['email' => $e, 'password' => $p], $remember)checks credentials (passwords are hashed with bcrypt via thehashedcast on the User model).- After a successful login call
$request->session()->regenerate()to prevent session fixation; on logoutAuth::logout(), then invalidate the session and regenerate the CSRF token. auth()->user()or$request->user()returns the current user;Auth::check()tells you if anyone is logged in.- Protect routes with the
authmiddleware; guests are redirected to theloginroute.
For APIs and SPAs, Sanctum issues API tokens or uses cookie-based SPA authentication.
Authorization answers "may you do this?".
- Gates are closures for general abilities:
Gate::define('view-reports', fn (User $u) => $u->is_admin);. - Policies group rules for one model:
php artisan make:policy PostPolicy --model=Postcreates methods likeupdate(User $user, Post $post). They're discovered automatically by naming convention.
Check permissions with Gate::authorize('update', $post) (throws a 403), $user->can('update', $post), the can:update,post middleware, or @can in Blade. Always authorize on the server, even if the UI hides the button.
Example
<?php
// app/Policies/PostPolicy.php
class PostPolicy
{
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id;
}
}
class SessionController extends Controller
{
public function store(Request $request)
{
$credentials = $request->validate(['email' => 'required|email', 'password' => 'required']);
if (! Auth::attempt($credentials, $request->boolean('remember'))) {
return back()->withErrors(['email' => 'Invalid credentials.'])->onlyInput('email');
}
$request->session()->regenerate();
return redirect()->intended('/dashboard');
}
}
// PostController@update: Gate::authorize('update', $post); // 403 if not allowed
// routes/web.php: Route::put('/posts/{post}', ...)->middleware(['auth', 'can:update,post']);
// Blade: @can('update', $post) <a href="...">Edit</a> @endcanRemember
- Auth::attempt logs in; regenerate the session afterwards
- Policies hold per-model rules; gates hold general abilities
- Authorize on the server with Gate::authorize, can middleware or $user->can
Watch out for
- Hiding an Edit button with @can but not authorizing in the controller, so a crafted request still works.