Skip to main content

Authentication Configuration Guide

Laravel Spectrum automatically detects various authentication methods and properly documents them. It supports common authentication patterns including JWT, Laravel Sanctum, OAuth2, and API Key authentication.

🔐 Supported Authentication Methods

1. Bearer Token Authentication (JWT/Sanctum)

The most common authentication method.

// routes/api.php
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', [UserController::class, 'profile']);
Route::apiResource('posts', PostController::class);
});

// or
Route::middleware('auth:api')->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
});

Configuration (config/spectrum.php):

'authentication' => [
'default' => 'bearer',
'flows' => [
'bearer' => [
'type' => 'http',
'scheme' => 'bearer',
'bearerFormat' => 'JWT',
'description' => 'Enter the token with the `Bearer ` prefix',
],
],
'middleware_map' => [
'auth:sanctum' => 'bearer',
'auth:api' => 'bearer',
'auth' => 'bearer',
],
],

2. API Key Authentication

Sending API keys via headers or query parameters.

// Custom middleware
class ApiKeyAuthentication
{
public function handle($request, Closure $next)
{
$apiKey = $request->header('X-API-Key') ?? $request->query('api_key');

if (!$apiKey || !$this->isValidApiKey($apiKey)) {
return response()->json(['error' => 'Invalid API key'], 401);
}

return $next($request);
}
}

// Usage example
Route::middleware('api-key')->group(function () {
Route::get('/data', [DataController::class, 'index']);
});

Configuration:

'authentication' => [
'flows' => [
'apiKey' => [
'type' => 'apiKey',
'in' => 'header', // or 'query'
'name' => 'X-API-Key',
'description' => 'API key for authentication',
],
],
'middleware_map' => [
'api-key' => 'apiKey',
],
],

3. Basic Authentication

HTTP Basic authentication.

Route::middleware('auth.basic')->group(function () {
Route::get('/admin', [AdminController::class, 'index']);
});

Configuration:

'authentication' => [
'flows' => [
'basic' => [
'type' => 'http',
'scheme' => 'basic',
'description' => 'Basic HTTP authentication',
],
],
'middleware_map' => [
'auth.basic' => 'basic',
],
],

4. OAuth2 Authentication

OAuth2 implementations like Laravel Passport.

Route::middleware('auth:passport')->group(function () {
Route::get('/oauth/user', [OAuthController::class, 'user']);
});

Configuration:

'authentication' => [
'flows' => [
'oauth2' => [
'type' => 'oauth2',
'flows' => [
'authorizationCode' => [
'authorizationUrl' => '/oauth/authorize',
'tokenUrl' => '/oauth/token',
'refreshUrl' => '/oauth/token/refresh',
'scopes' => [
'read' => 'Read access',
'write' => 'Write access',
'admin' => 'Admin access',
],
],
'password' => [
'tokenUrl' => '/oauth/token',
'scopes' => [
'read' => 'Read access',
'write' => 'Write access',
],
],
],
],
],
'middleware_map' => [
'auth:passport' => 'oauth2',
],
],

🎯 Implementation Patterns

Laravel Sanctum

// routes/web.php
Route::post('/login', [AuthController::class, 'login']);
Route::post('/logout', [AuthController::class, 'logout'])
->middleware('auth:sanctum');

// routes/api.php
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', function (Request $request) {
return $request->user();
});
});

API Token Authentication

class AuthController extends Controller
{
public function login(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);

if (!Auth::attempt($request->only('email', 'password'))) {
return response()->json([
'message' => 'Invalid credentials'
], 401);
}

$user = User::where('email', $request->email)->firstOrFail();
$token = $user->createToken('auth-token')->plainTextToken;

return response()->json([
'access_token' => $token,
'token_type' => 'Bearer',
'user' => new UserResource($user),
]);
}

public function logout(Request $request)
{
$request->user()->currentAccessToken()->delete();

return response()->json([
'message' => 'Successfully logged out'
]);
}
}

JWT Authentication (tymon/jwt-auth)

class AuthController extends Controller
{
public function login(Request $request)
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required',
]);

if (!$token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}

return $this->respondWithToken($token);
}

protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth()->factory()->getTTL() * 60,
'user' => new UserResource(auth()->user()),
]);
}

public function refresh()
{
return $this->respondWithToken(auth()->refresh());
}

public function logout()
{
auth()->logout();

return response()->json(['message' => 'Successfully logged out']);
}
}

Custom Authentication Middleware

namespace App\Http\Middleware;

use Closure;
use App\Models\ApiClient;

class CustomApiAuthentication
{
public function handle($request, Closure $next, ...$scopes)
{
// Get authentication credentials from headers
$apiKey = $request->header('X-API-Key');
$apiSecret = $request->header('X-API-Secret');

if (!$apiKey || !$apiSecret) {
return response()->json([
'error' => 'Missing authentication credentials'
], 401);
}

// Verify client
$client = ApiClient::where('key', $apiKey)
->where('is_active', true)
->first();

if (!$client || !hash_equals($client->secret, $apiSecret)) {
return response()->json([
'error' => 'Invalid credentials'
], 401);
}

// Check scopes
if (!empty($scopes) && !$client->hasScopes($scopes)) {
return response()->json([
'error' => 'Insufficient permissions'
], 403);
}

// Request rate limiting
if (!$this->checkRateLimit($client)) {
return response()->json([
'error' => 'Rate limit exceeded'
], 429);
}

$request->merge(['api_client' => $client]);

return $next($request);
}

private function checkRateLimit($client)
{
// Rate limiting logic
return true;
}
}

🛡️ Security Headers

CORS Configuration

// config/cors.php
return [
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:3000')],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
];

Security Middleware

class SecurityHeaders
{
public function handle($request, Closure $next)
{
$response = $next($request);

$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-Frame-Options', 'DENY');
$response->headers->set('X-XSS-Protection', '1; mode=block');
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');

return $response;
}
}

📝 Documentation Generation Configuration

Global Authentication Configuration

// config/spectrum.php
'authentication' => [
// Default authentication method
'default' => 'bearer',

// Routes that don't require authentication
'exclude_patterns' => [
'api/health',
'api/status',
'api/auth/login',
'api/auth/register',
],

// Global security requirements
'global_security' => true,

// Custom security schemes
'custom_schemes' => [
'twoFactor' => [
'type' => 'apiKey',
'in' => 'header',
'name' => 'X-2FA-Code',
'description' => 'Two-factor authentication code',
],
],
],

Route-specific Authentication Configuration

// Using multiple authentication methods
Route::middleware(['auth:sanctum', 'verified', '2fa'])->group(function () {
Route::post('/sensitive-action', [SecureController::class, 'action']);
});

// Authentication options (any of)
Route::middleware('auth:sanctum,api')->group(function () {
Route::get('/flexible-endpoint', [FlexibleController::class, 'index']);
});

💡 Best Practices

1. Environment-specific Configuration

// .env
API_AUTH_DRIVER=sanctum
API_RATE_LIMIT=60
API_TOKEN_LIFETIME=60

// config/auth.php
'guards' => [
'api' => [
'driver' => env('API_AUTH_DRIVER', 'sanctum'),
'provider' => 'users',
],
],

2. Proper Token Management

class TokenService
{
public function generateToken(User $user, string $name = 'api-token'): array
{
// Delete existing tokens (optional)
$user->tokens()->where('name', $name)->delete();

$token = $user->createToken($name, ['*']);

return [
'access_token' => $token->plainTextToken,
'token_type' => 'Bearer',
'expires_at' => $token->accessToken->expires_at,
'abilities' => $token->accessToken->abilities,
];
}

public function revokeToken(User $user, ?string $tokenId = null): void
{
if ($tokenId) {
$user->tokens()->where('id', $tokenId)->delete();
} else {
$user->tokens()->delete();
}
}
}

3. Rate Limiting

// routes/api.php
Route::middleware(['auth:sanctum', 'throttle:api'])->group(function () {
Route::get('/user', [UserController::class, 'show']);
});

// RouteServiceProvider.php
protected function configureRateLimiting()
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by(
$request->user()?->id ?: $request->ip()
);
});

RateLimiter::for('auth', function (Request $request) {
return Limit::perMinute(5)->by($request->ip());
});
}

4. Authentication Error Handling

// app/Exceptions/Handler.php
public function render($request, Throwable $exception)
{
if ($exception instanceof AuthenticationException) {
return response()->json([
'error' => 'Unauthenticated',
'message' => 'Authentication required',
], 401);
}

if ($exception instanceof AuthorizationException) {
return response()->json([
'error' => 'Forbidden',
'message' => 'You do not have permission to access this resource',
], 403);
}

return parent::render($request, $exception);
}

🔍 Troubleshooting

Authentication Not Detected

  1. Check middleware mapping

    // config/spectrum.php
    'middleware_map' => [
    'your-custom-auth' => 'bearer',
    ],
  2. Check route middleware

    php artisan route:list --path=api
  3. Clear cache

    php artisan config:clear
    php artisan route:clear

Multiple Authentication Methods

// config/spectrum.php
'authentication' => [
'flows' => [
'bearer' => [...],
'apiKey' => [...],
],
'middleware_map' => [
'auth:sanctum' => 'bearer',
'api-key' => 'apiKey',
],
// Use different authentication per route
'route_overrides' => [
'api/external/*' => 'apiKey',
'api/internal/*' => 'bearer',
],
],