Laravel Missing Authorization: How to Add Policy and Gate Checks to Controllers
Controllers with store, update, and destroy actions but no authorization let any authenticated user modify any record. Add policies to enforce ownership.
The Problem
Authentication verifies who a user is. Authorization verifies what they can do. A controller that checks authentication (auth middleware) but not authorization lets any logged-in user create, edit, or delete any record — including other users' data. This is called an Insecure Direct Object Reference (IDOR) and is consistently in the OWASP Top 10. If your PostController lets user A update user B's posts, you have a missing authorization vulnerability.
How to Fix
-
1
Create a policy for each model
Generate policies for models that need access control:php artisan make:policy PostPolicy --model=Post// app/Policies/PostPolicy.php public function update(User $user, Post $post): bool { return $user->id === $post->user_id; }public function delete(User $user, Post $post): bool { return $user->id === $post->user_id; }public function create(User $user): bool { return true; // Any authenticated user can create } -
2
Apply authorization in controllers
Use the authorize() method or Gate facade:
// Using authorize() — throws 403 if denied public function update(Request $request, Post $post) { $this->authorize('update', $post); // ... update logic }// Using middleware for all actions public function __construct() { $this->authorizeResource(Post::class, 'post'); }// Using Gate directly if (Gate::denies('update', $post)) { abort(403); } -
3
Scope queries to the authenticated user
In addition to policies, scope database queries to prevent data leakage:
// WRONG — returns any user's post $post = Post::findOrFail($id);// RIGHT — only returns the authenticated user's post $post = auth()->user()->posts()->findOrFail($id);// Or use a global scope class Post extends Model { protected static function booted() { static::addGlobalScope('owned', function (Builder $builder) { if (auth()->check()) { $builder->where('user_id', auth()->id()); } }); } }
How to Verify
Test by logging in as User A and trying to access User B's resources:
# Get User B's post ID, then try to update it as User A
curl -X PUT https://yourapp.com/api/posts/123 \
-H 'Authorization: Bearer USER_A_TOKEN' \
-d 'title=Hacked'
This should return 403 Forbidden, not 200 OK. Run php artisan stackshield:scan --check=SS053 to find controllers missing authorization.
Prevention
Use authorizeResource() in controller constructors for automatic policy mapping. Add authorization checks to your code review checklist. Write feature tests that verify User A cannot access User B's resources. Use StackShield to scan for controllers missing authorization.
Frequently Asked Questions
Is auth middleware enough for authorization?
No. Auth middleware only verifies the user is logged in. It does not check whether that specific user has permission to perform the requested action on the requested resource. You need both: auth middleware (who) and policies/gates (what).
Should I use policies or gates?
Use policies when authorization logic is tied to a specific model (PostPolicy for Post). Use gates for general abilities not tied to a model (Gate::define('manage-settings')). Most CRUD controllers should use policies.
Related Security Terms
Related Guides
Laravel Mass Assignment Vulnerability: How to Protect Eloquent Models with $fillable and $guarded
Eloquent models without $fillable or $guarded allow attackers to set any database column through request input, including is_admin, role, or email_verified_at.
Fix Missing CSRF Protection in Laravel: @csrf, VerifyCsrfToken & API Routes
Laravel forms without @csrf tokens are vulnerable to cross-site request forgery. Learn how to add CSRF protection, configure VerifyCsrfToken exceptions, and handle CSRF for API routes.
Laravel Request Validation: How to Stop Using Raw $request Input in Controllers
Controllers using $request->input() or $request->all() without validation expose your app to type juggling, injection, and data corruption attacks.
Detect This Automatically with StackShield
StackShield continuously monitors your Laravel application from the outside and alerts you when security issues are found. No installation required.
Start Free Trial