--- name: passport-development description: "Develops OAuth2 API authentication with Laravel Passport. Activates when installing or configuring Passport; setting up OAuth2 grants (authorization code, client credentials, personal access tokens, device authorization); managing OAuth clients; protecting API routes with token authentication; defining or checking token scopes; configuring SPA cookie authentication; handling token lifetimes and refresh tokens; or when the user mentions Passport, OAuth2, API tokens, bearer tokens, or API authentication. Make sure to use this skill whenever the user works with OAuth2, API tokens, or third-party API access, even if they don't explicitly mention Passport." license: MIT metadata: author: laravel --- @php /** @var \Laravel\Boost\Install\GuidelineAssist $assist */ @endphp # Passport OAuth2 Authentication ## Documentation First **Always use `search-docs` before writing Passport code.** The documentation covers every grant type, configuration option, and edge case in detail. This skill teaches you how to navigate Passport — the docs have the implementation specifics. ``` search-docs(queries: ["Passport installation"], packages: ["laravel/framework@12.x"]) ``` The Passport docs live under the `laravel/framework` package — not `laravel/passport`. ## When to Apply Activate this skill when: - Installing or configuring Passport - Setting up OAuth2 authorization grants - Creating or managing OAuth clients - Protecting API routes with token authentication - Defining or checking token scopes - Configuring SPA cookie-based authentication - Choosing between Passport and Sanctum ## Passport vs. Sanctum **Passport** is a full OAuth2 server — use it when third-party applications need to consume your API and when you need OAuth2 authorization code grants, client credentials for machine-to-machine auth, or device authorization flow. **Sanctum** is simpler — use it when first-party SPAs, third parties, or mobile apps consume the API but you don't need the full OAuth2 grant flows. ## Installation Three steps are always required: ### 1. Install Passport ```bash {{ $assist->artisanCommand('install:api --passport') }} ``` This publishes migrations, generates encryption keys, and registers routes. ### 2. Configure the User model The User model needs both the `HasApiTokens` trait AND the `OAuthenticatable` interface. Missing the interface is the most common Passport setup mistake — it causes runtime errors that can be confusing to debug. ```php use Laravel\Passport\Contracts\OAuthenticatable; use Laravel\Passport\HasApiTokens; class User extends Authenticatable implements OAuthenticatable { use HasApiTokens; } ``` ### 3. Configure the auth guard The `api` guard must use the `passport` driver in `config/auth.php`. Using `token` or `sanctum` here silently breaks Passport authentication. ```php 'guards' => [ 'api' => [ 'driver' => 'passport', 'provider' => 'users', ], ], ``` ## Choosing a Grant Type Matching the right grant to the use case is the most important Passport decision. Use `search-docs` for implementation details of any grant. | Use Case | Grant Type | Client Flag | |----------|-----------|-------------| | Third-party app accessing user data | Authorization Code | (default) | | Mobile/SPA without client secret | Authorization Code + PKCE | `--public` | | Machine-to-machine, no user context | Client Credentials | `--client` | | User-generated API keys | Personal Access Tokens | `--personal` | | Smart TV, CLI, IoT devices | Device Authorization | `--device` | **Legacy grants** (Password, Implicit) are disabled by default and not recommended. They must be explicitly enabled with `Passport::enablePasswordGrant()` or `Passport::enableImplicitGrant()`. ## Client Management Create clients with the appropriate flag for the grant type: ```bash {{ $assist->artisanCommand('passport:client') }} # Authorization code {{ $assist->artisanCommand('passport:client --public') }} # PKCE (no secret) {{ $assist->artisanCommand('passport:client --client') }} # Client credentials {{ $assist->artisanCommand('passport:client --personal') }} # Personal access tokens {{ $assist->artisanCommand('passport:client --device') }} # Device authorization ``` Additional flags: `--name=`, `--redirect_uri=`, `--provider=`. Client secrets are hashed by default — the plain-text secret is only shown at creation time and cannot be retrieved later. ## Protecting Routes Apply `auth:api` middleware. Clients send tokens via the `Authorization: Bearer ` header. ```php Route::get('/user', function (Request $request) { return $request->user(); })->middleware('auth:api'); ``` ### Scope Enforcement Scope middleware must come alongside `auth:api`: - `CheckToken::using('scope1', 'scope2')` — requires ALL listed scopes - `CheckTokenForAnyScope::using('scope1', 'scope2')` — requires ANY listed scope - `EnsureClientIsResourceOwner::using('scope1')` — restricts to client credential tokens ```php use Laravel\Passport\Http\Middleware\CheckToken; Route::get('/orders', function () { // ... })->middleware(['auth:api', CheckToken::using('orders:read')]); ``` ### Programmatic scope checking ```php if ($request->user()->tokenCan('place-orders')) { // ... } ``` Use `search-docs` for full scope middleware registration and usage patterns. ## Key Configuration Configure in `AppServiceProvider::boot()`. Use `search-docs` for the full list of options. ```php // Token lifetimes (each is independent) Passport::tokensExpireIn(now()->addDays(15)); Passport::refreshTokensExpireIn(now()->addDays(30)); Passport::personalAccessTokensExpireIn(now()->addMonths(6)); // Define scopes Passport::tokensCan([ 'place-orders' => 'Place orders', 'check-status' => 'Check order status', ]); ``` ## SPA Cookie Authentication For first-party SPAs, the `CreateFreshApiToken` middleware issues a `laravel_token` cookie containing an encrypted JWT. The SPA must include CSRF tokens — missing the `X-CSRF-TOKEN` or `X-XSRF-TOKEN` header causes 419 errors. Use `search-docs` for setup details — this feature has specific CSRF and cookie configuration requirements. ## Testing Passport provides helpers to bypass full OAuth flows in tests: ```php Passport::actingAs($user, ['scope1', 'scope2']); Passport::actingAsClient($client, ['scope1']); ``` ## Token Maintenance ```bash {{ $assist->artisanCommand('passport:purge') }} # Purge revoked & expired {{ $assist->artisanCommand('passport:purge --revoked') }} # Only revoked {{ $assist->artisanCommand('passport:purge --expired') }} # Only expired ``` Schedule `passport:purge` for regular expired token clean-up. ## Events All in `Laravel\Passport\Events`: `AccessTokenCreated`, `AccessTokenRevoked`, `RefreshTokenCreated`. ## Common Pitfalls - **Missing `OAuthenticatable` interface** — both the `HasApiTokens` trait and the `OAuthenticatable` interface are required on the User model. Missing the interface causes runtime errors. - **Wrong guard driver** — the `api` guard must use `passport`, not `token` or `sanctum`. This fails silently. - **Token lifetime confusion** — access token, refresh token, and personal access token lifetimes are all independent settings. - **Missing CSRF for SPA cookie auth** — `CreateFreshApiToken` requires CSRF tokens. Use `Passport::ignoreCsrfToken()` only if you understand the security implications. - **Client secrets are hashed** — the plain-text secret is only available at creation time. - **Legacy grants are disabled** — Password and Implicit grants must be explicitly enabled and are not recommended.