- 🛡️ Laravel middleware can make sure only admin users bypass subscription checks and get into protected areas.
- 🔄 Laravel Cashier has methods to handle trials, grace periods, and full subscription states well.
- ⚙️ Custom middleware that mixes role and subscription logic helps with upkeep and security.
- 🧩 Breaking access logic into separate middleware and policies makes systems easier to scale and test.
- 🔐 Wrong role checks can cause big security problems; use safe ways like
isAdmin().
Laravel Middleware: Allow Admin Without Subscription?
In a Laravel SaaS app that uses Laravel Cashier for billing, it is normal to limit features for users who have not paid. But many times, certain roles—like your admin users—should get full access no matter their subscription status. With Laravel middleware, you can choose which parts of your app’s routes to protect. This makes sure regular users must subscribe, but admins can skip those checks clearly and safely.
Understanding Laravel Middleware
Laravel middleware works as a middle step between a request and the code that handles it. Think of it as a gate that checks things. This setup works well for checking user authentication, roles, and subscriptions.
Middleware in Laravel can apply to all requests, or just to specific routes. It runs at different parts of the request. You set it up in app/Http/Kernel.php. Here are key stages:
- Global Middleware: Runs on every web request (for example, to remove extra spaces or change empty strings to nothing).
- Route Middleware: Added to specific routes or groups of routes. This gives you exact control.
Here’s an example idea of how a web request works:
- A user asks for something from a specific web address.
- Laravel sends the request to the right place.
- Middleware runs:
- It checks who the user is.
- It checks the subscription.
- It checks the user’s role.
- If all checks pass, the controller code runs.
- A response goes back. It might go through more middleware (for example, to record the response or handle CORS).
✅ Why use middleware for access control?
- It puts the checking logic in one place, away from controllers.
- You can use it for many different web addresses.
- It makes your app predictable and safe.
📚 Read more in Laravel’s documentation
Conditional Middleware Logic: The Laravel Way
Middleware is very good when decisions depend on what a user is, like their roles or subscriptions. Conditional middleware logic means using if-else statements inside your middleware. This treats different users in different ways based on rules you set.
Common Conditional Scenarios
- Admin users manage the back-end no matter their subscription status.
- Users who are not admins need an active subscription to use special features.
- Users on a trial plan can skip subscription checks for a short time.
- Users in a grace period, who have canceled but still have active access.
Middleware that is set up well handles these cases simply. It follows the correct order:
- Check that the user is logged in.
- See if the user’s role lets them skip subscription checks.
- Check the subscription status using Laravel Cashier helpers.
Keeping things separate makes each middleware clean, easy to build with, and reusable.
Using Laravel Cashier for Subscription Management
Laravel Cashier gives you a simple way to use Stripe and Braintree. It handles things like making subscriptions, changing plans, and checking payment status.
Here are some of the most helpful methods it offers:
$user->subscribed('default');
$user->onTrial('default');
$user->subscription('default')->onGracePeriod();
$user->subscription('default')->cancelled();
Using these methods, you can:
✅ Figure out if access should be given
✅ Decide what message or screen to show (for example, “Your trial has ended”)
✅ Send users to upgrade or billing pages
Cashier States to Know
| Method | What it means |
|---|---|
subscribed('default') |
Says true if the user has an active subscription |
onTrial('default') |
true if the user is in the trial period |
onGracePeriod() |
true if the user’s subscription is canceled but still active |
cancelled() |
Shows a subscription was canceled (after the grace period) |
💡 Cashier simplifies this logic. It makes it easier to manage SaaS billing without writing direct Stripe API calls.
Finding Admin Users Correctly
Before skipping subscription checks, you must know for sure who your admins are. Just writing "user_id === 1" or checking an email like admin@example.com can cause mistakes and security risks.
Better Admin Checks
- ✅ Add a method to the User model:
public function isAdmin()
{
return $this->role === 'admin'; // Or use a list of options/settings for roles
}
- ✅ Use Authorization Gates or Policies, for example:
Gate::define('admin-only', function ($user) {
return $user->hasRole('admin');
});
-
✅ Use Spatie Permissions Package:
- Give roles using
php artisan permission:create-role admin - Use
hasRole()orhasPermissionTo()
- Give roles using
-
✅ Use a list of options or config-driven roles for easier upkeep.
These ways make sure admin checks are always the same and safe in your app.
Making Middleware That Knows Roles
Build a middleware that checks both isAdmin() and Cashier subscription status.
Step 1: Make a Middleware
php artisan make:middleware EnsureUserIsSubscribed
Step 2: Change the Middleware Logic
public function handle($request, Closure $next)
{
$user = auth()->user();
if ($user->isAdmin()) {
return $next($request);
}
if (
$user->subscribed('default') ||
$user->onTrial('default') ||
$user->subscription('default')?->onGracePeriod()
) {
return $next($request);
}
return redirect()->route('subscription.plans');
}
🎯 Main points:
isAdmin()stops all other checks right away.- Users in a grace period or on trial are allowed.
- Users without a subscription are sent to the right place.
Remember to use ?-> to prevent errors if subscription('default') gives no result.
Route Usage: Applying Middleware Simply
Once you have defined the middleware, add it to your Kernel file.
// app/Http/Kernel.php
protected $routeMiddleware = [
'verified_subscriber' => \App\Http\Middleware\EnsureUserIsSubscribed::class,
];
Now add it to routes:
Route::middleware(['auth', 'verified_subscriber'])->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
});
You can also add it right to specific routes:
Route::get('/settings', [SettingsController::class, 'index'])
->middleware(['auth', 'verified_subscriber']);
Handling Special Cases: Trials, Grace Periods, Cancels
SaaS billing has more than just "active" and "inactive" users. There are also trial users, users in grace periods, and users with recently canceled subscriptions. Laravel Cashier makes it easy to handle these states.
✔️ Add trial and grace logic:
if (
$user->isAdmin() ||
$user->subscribed('default') ||
$user->onTrial('default') ||
$user->subscription('default')->onGracePeriod()
) {
return $next($request);
}
This keeps you from accidentally blocking a user who:
- Just started a free trial.
- Is still in an acceptable period after canceling.
- Has admin rights but no subscription.
🧠 Thinking about this helps avoid support emails and upset users.
Middleware Setup and Layering Best Practices
Do not make one large middleware that tries to do everything. Keep different tasks separate for easier upkeep.
🏗️ Suggested Middleware Layers:
- auth – Checks that the user is logged in.
- verified – Makes sure email is confirmed if needed.
- role:admin – For parts that only admins can see.
- subscribed – Your custom middleware to manage subscription rules.
🧩 Use them as needed:
Route::middleware(['auth', 'verified', 'subscribed'])->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
});
📛 Do not do this:
// Controller that does all checks inside
if (!auth()->user()->hasRole('admin') && !auth()->user()->subscribed()) {
abort(403);
}
Instead, let middleware manage access. Keep your controllers focused on business tasks.
Testing and Debugging Custom Middleware
Middleware works well only if you test it. With Laravel's built-in testing tools (PHPUnit), make sure your access rules work in real situations.
✅ Use Feature Tests
public function test_admin_bypasses_subscription()
{
$admin = User::factory()->create(['role' => 'admin']);
$this->actingAs($admin)
->get('/dashboard')
->assertOk();
}
public function test_regular_user_is_redirected_without_subscription()
{
$user = User::factory()->create();
$this->actingAs($user)
->get('/dashboard')
->assertRedirect('/subscription/plans');
}
🧪 Use Laravel’s Subscription Testing Helpers
With Cashier, you can set up fake billing states directly:
$user->trial_ends_at = now()->addDays(7);
// or
$user->subscriptions()->create([...]);
Always test these:
- User not logged in.
- Admin user.
- Trial user.
- Grace period user.
- User with a fully canceled subscription.
Automated tests help stop mistakes in unusual situations.
Security Considerations
Controlling access is a big part of security. Do not take shortcuts that could cause problems.
🚫 Avoid:
- Using user IDs or emails for admin logic.
- Skipping subscription checks by hand in controllers.
✅ Use:
- Clear
isAdmin()checks. - Layered middleware logic.
- Safe role management through trusted packages.
- Always check subscription status unless you have a specific reason not to.
📚 Important: OWASP A05 – Security Misconfiguration
Other Ways to Do It: Policy-Based Checks vs Middleware
For some cases, Laravel’s authorization policies might be better than middleware.
When to use Policies
- Permissions change based on data (for example, Company A vs Company B).
- Different parts of your app need different access rules.
- You need to add Gate/Policy checks to specific model methods (for example,
update()orview()).
👟 Use both for the best results:
- Use middleware for general access control.
- Use policies for checks specific to a resource (for example, permissions for each document, team, or workspace).
📚 More on Authorization Policies
Recap: Simple, Scalable Middleware Design for SaaS Apps
A good middleware setup helps you:
✅ Make subscription rules apply everywhere.
✅ Let admins skip checks safely.
✅ Handle trial and grace periods well.
✅ Keep access rules separate from other business code.
✅ Write tests that cover real situations.
For SaaS platforms made with Laravel, understanding middleware and Laravel Cashier is important for better security and user experiences.
Devsolus Tips: Developer Learnings & Future-proofing
🛠️ Build for now. Plan for growth.
- Always use
isAdmin()instead of fixed checks. - Keep middleware small and focused on one task.
- Use Laravel route groups for a clear structure.
- Stay up to date with new Laravel Cashier releases.
- Write down how you handle unusual situations for your team or in your documents.
💬 Want more Laravel SaaS examples? Follow Devsolus for detailed articles on Laravel tools, multi-tenant plans, and Laravel Cashier setups.
Citations
- Laravel Middleware (https://laravel.com/docs/10.x/middleware)
- Laravel Cashier Documentation (https://laravel.com/docs/10.x/billing)
- Laravel Authorization (https://laravel.com/docs/10.x/authorization)
- OWASP Top 10: A05 – Security Misconfiguration (https://owasp.org/Top10/A05_2021-Security_Misconfiguration/)