Add custom middleware to Laravel Passport endpoints
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Adding Custom Middleware to Laravel Passport Endpoints: A Deep Dive
As developers working with robust authentication systems like Laravel Passport, we often find ourselves needing to layer custom security checks on top of the default functionality. You have a standard setup where your general API routes are protected by auth:api, but you want to extend this protection specifically to the sensitive OAuth endpoints, such as /oauth/token.
The question is: How do we apply our custom middleware to these specific Passport routes without breaking the core functionality?
This post will walk you through the architectural considerations and provide a practical solution for wrapping your Passport token endpoints with custom validation logic.
The Challenge of Passport Route Registration
You have correctly identified that standard route protection works fine for your application's main API group, likely because those routes are defined in traditional routes/api.php files where middleware application is straightforward. However, when you use the helper function Passport::routes(), Laravel Passport registers these endpoints internally. This often bypasses the direct control you have over standard route file routing, making it challenging to apply external middleware directly to those specific token routes.
The goal—ensuring that requests to /oauth/token fail immediately if required headers are missing—is a valid security requirement. We need a mechanism to hook into the request lifecycle specifically for these Passport endpoints.
The Solution: Applying Middleware via Route Grouping or Customization
Since directly modifying the internal route generation is generally discouraged (as it can lead to breaking updates), the most robust Laravel approach involves applying middleware at a level that encompasses those routes, or checking if Passport allows middleware injection.
For Passport, while direct application to the Passport::routes() output is tricky, we can leverage the fact that these routes are typically prefixed under the api scope. The best practice here is to define a custom route file for your OAuth endpoints and ensure it uses your custom security layer.
Step 1: Define Your Custom Middleware
First, let's assume you have created a custom middleware designed to check specific headers (e.g., an API key or authorization header).
// app/Http/Middleware/CheckCustomHeaders.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class CheckCustomHeaders
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next): Response
{
// Example check: Ensure a specific header exists and is valid
if (!$request->hasHeader('X-Custom-Auth-Token') || $request->header('X-Custom-Auth-Token') !== 'VALID_VALUE') {
return response()->json([
'error' => 'Unauthorized',
'message' => 'Missing or invalid custom authentication header.'
], 401);
}
// If checks pass, proceed to the next middleware or controller
return $next($request);
}
}
Register this middleware in app/Http/Kernel.php.
Step 2: Applying Middleware to Passport Routes
Instead of relying solely on Passport::routes(), a more controlled approach involves defining your OAuth routes explicitly and applying the custom middleware directly. This gives you full control over where the protection is applied.
In your AuthServiceProvider.php boot method, or wherever you define route registrations, you can structure the definition to include the necessary middleware group:
// In AuthServiceProvider.php boot() method (or a dedicated routes file)
use Illuminate\Support\Facades\Route;
public function boot()
{
$this->registerPolicies();
// Define Passport routes explicitly, applying custom middleware
Route::group(['middleware' => ['check.custom_headers']], function () {
Route::post('/oauth/token', [
\Laravel\Passport\TokenController::class, 'issueToken'
]);
Route::post('/oauth/token/refresh', [
\Laravel\Passport\TokenController::class, 'refreshAccessToken'
]);
// Add other specific Passport routes here if needed...
});
// Keep the standard Passport route registration for convenience if desired,
// but ensure custom checks are handled above.
Passport::routes(function ($router) {
$router->forAccessTokens();
$router->forTransientTokens();
});
Passport::tokensExpireIn(Carbon::now()->addDays(7));
Passport::refreshTokensExpireIn(Carbon::now()->addDays(30));
}
By using Route::group(), you effectively wrap the specific token issuance endpoints with your custom middleware. This ensures that any request hitting /oauth/token must first satisfy the header validation defined in CheckCustomHeaders. This level of granular control is essential when building secure APIs, which aligns perfectly with the principles promoted by Laravel architecture, such as those discussed on platforms like laravelcompany.com.
Conclusion
Protecting critical endpoints like OAuth token issuance requires moving beyond simple route middleware application. By understanding how Passport registers its routes and utilizing Laravel's powerful route grouping features, you can successfully layer your custom security requirements onto the authentication flow. This approach provides clarity, maintainability, and ensures that your API adheres to strict security standards before any token is issued or refreshed.