0

Passport — full OAuth2 server in Laravel

Advanced5 min read·lv-16-004
security

Concept

Custom guards allow implementing non-standard authentication mechanisms — API key authentication, JWT verification, or authenticating against an external system (LDAP, SSO provider).

Guard contract: Illuminate\Contracts\Auth\Guard. Two key methods: user() (return the authenticated user or null) and check() (return bool). For stateful guards: login(), logout(), attempt().

StatefulGuard: Extends Guard with attempt(), login(), loginUsingId(), once(), logout(). The session guard implements this.

RequestGuard: The simplest guard — a closure-based guard registered with Auth::viaRequest(). Takes the request and returns a user model or null. Perfect for stateless API key guards.

UserProvider: Handles finding users from the database. The default EloquentUserProvider finds by credentials. Custom providers implement Illuminate\Contracts\Auth\UserProvider: retrieveById(), retrieveByToken(), updateRememberToken(), retrieveByCredentials(), validateCredentials().

Registering custom guards: In AuthServiceProvider::boot(): Auth::extend('custom-driver', fn($app, $name, $config) => new CustomGuard(...)).

Registering custom providers: Auth::provider('custom-provider', fn($app, $config) => new CustomProvider(...)).

Code Example

php
<?php
// Simple closure-based API key guard — in AuthServiceProvider::boot()
use Illuminate\Support\Facades\Auth;

Auth::viaRequest('api-key', function(\Illuminate\Http\Request $request): ?User {
    $apiKey = $request->header('X-API-Key') ?? $request->query('api_key');
    if (!$apiKey) return null;
    return User::where('api_key', hash('sha256', $apiKey))->first();
});

// config/auth.php — register the guard
'guards' => [
    'api-key' => [
        'driver'   => 'api-key',   // matches Auth::viaRequest name
        'provider' => 'users',
    ],
],

// Use in routes
Route::middleware('auth:api-key')->get('/api/data', function() {
    return response()->json(['user' => auth('api-key')->user()]);
});

// Full custom Guard class — for complex stateless guards
class JwtGuard implements \Illuminate\Contracts\Auth\Guard
{
    use \Illuminate\Auth\GuardHelpers;

    public function __construct(
        private \Illuminate\Contracts\Auth\UserProvider $provider,
        private \Illuminate\Http\Request $request,
    ) {}

    public function user(): ?User
    {
        if ($this->user !== null) return $this->user;

        $token = $this->request->bearerToken();
        if (!$token) return null;

        try {
            $payload = \Firebase\JWT\JWT::decode($token, new \Firebase\JWT\Key(config('app.key'), 'HS256'));
            return $this->user = $this->provider->retrieveById($payload->sub);
        } catch (\Exception) {
            return null;
        }
    }

    public function validate(array $credentials = []): bool
    {
        return $this->provider->retrieveByCredentials($credentials) !== null;
    }
}

// Register in AuthServiceProvider
Auth::extend('jwt', function($app, $name, $config) {
    return new JwtGuard(
        Auth::createUserProvider($config['provider']),
        $app['request']
    );
});