> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sinemacula.com/llms.txt
> Use this file to discover all available pages before exploring further.

# JWT and HTTP Basic Guards

> Learn how the JWT bearer-token guard and HTTP Basic guard work in Laravel Authentication, including registration and contextual resolution.

Laravel Authentication ships two guards — `jwt` and `basic` — and both are sessionless. Neither writes a session cookie, reads a remember token, or maintains server-side auth state between requests. On every request the guard reads the credentials from the incoming HTTP request, resolves the identity and principal from your configured provider and resolver, and binds the contextual triple before your application code runs. When the request ends, that state is discarded.

## The JWT guard (`driver: jwt`)

The JWT guard reads the `Authorization: Bearer <token>` header. When a token is present it decodes the JWT, validates the standard claims (`iss`, `aud`, `typ`, `exp`, and a configurable leeway), and then rehydrates the full contextual triple from live database state:

1. The `sub` claim is used to load the identity from the configured provider.
2. If the identity implements `CanBeActive`, the guard calls `isActive()` and rejects the request on `false`.
3. The `pid` claim, if present, is passed to the principal resolver as a hint. The resolver must return a principal whose `getPrincipalIdentifier()` matches the hint exactly — the guard fails closed if the hint resolves to a different principal.
4. The `did` claim, if present and if the identity implements `HasDevices`, is used to load the device record. A `did` that does not resolve to a known device causes the request to be rejected rather than silently downgraded to "no device."
5. All three objects are bound to the guard, making them available via `Auth::identity()`, `Auth::principal()`, and `Auth::device()`.

<Note>
  Access tokens are self-contained and validated without a server-side token store. Revoking a device blocks refresh for that device, but already-issued access tokens remain valid until expiry unless the identity, principal, or device can no longer be rehydrated from live state.
</Note>

The JWT guard also owns the refresh path. Call `$guard->refresh($refreshToken)` to exchange a refresh credential for a new access token and a rotated refresh token. The exchange validates the refresh token's JWT claims, verifies the stored rotation digest with `hash_equals`, atomically rotates the device's refresh key, and re-binds the contextual triple — all in one round trip.

## The HTTP Basic guard (`driver: basic`)

The HTTP Basic guard reads `PHP_AUTH_USER` and `PHP_AUTH_PW` from the active request (surfaced as `$request->getUser()` and `$request->getPassword()`). When both are present it runs them through a timing-safe credential validation path:

1. The username is looked up in the configured provider using the guard's `identifier_field` (defaults to `email`).
2. The entire retrieve-and-validate pipeline runs inside a `Timebox` with a configurable duration (default 400 ms). The elapsed time is uniform whether or not the identifier resolves, preventing user-enumeration via timing side-channel.
3. On valid credentials, the guard checks the identity's active state and resolves a principal via the `PrincipalResolver`.
4. The identity and principal are bound to the guard. Because Basic auth carries no device token, `Auth::device()` always returns `null` for this guard.

<Warning>
  Behind PHP-FPM + nginx, the `Authorization` header is not automatically forwarded into `PHP_AUTH_USER`/`PHP_AUTH_PW`. Without the following nginx directive, the guard sees no credentials and `Auth::user()` always returns `null`:

  ```nginx theme={null}
  location ~ \.php$ {
      fastcgi_pass_header Authorization;
      # …rest of the fastcgi block
  }
  ```

  Apache with `mod_php` populates these variables automatically. This gotcha is specific to FastCGI transports.
</Warning>

## Registering guards in `config/auth.php`

Register either guard exactly as you would any first-party Laravel guard — no service provider call required:

```php theme={null}
'guards' => [
    'api' => [
        'driver'   => 'jwt',
        'provider' => 'users',
    ],
    'cli' => [
        'driver'   => 'basic',
        'provider' => 'users',
    ],
],

'providers' => [
    'users' => [
        'driver' => 'model',
        'model'  => App\Models\User::class,
    ],
],
```

Your identity model implements `Identity` (and optionally `Principal`, `HasPrincipals`, `HasDevices`, `CanBeActive`). The guard infers which capabilities are available at runtime from the interfaces on the resolved model — no configuration flag needed.

## Using guard middleware

Apply guards with standard Laravel middleware exactly as documented in the framework:

```php theme={null}
// Routes that require a valid JWT bearer token
Route::middleware('auth:api')->group(function () {
    Route::get('/profile', ProfileController::class);
});

// Routes that require HTTP Basic credentials
Route::middleware('auth:cli')->group(function () {
    Route::post('/deploy', DeployController::class);
});
```

## How the contextual triple is exposed

Both guards store the resolved identity, principal, and device on the guard's state after a successful authentication. The `Auth` facade forwards contextual accessor calls to the currently active guard:

```php theme={null}
use SineMacula\Laravel\Authentication\Facades\Auth;

Auth::identity();   // Identity|null   — the authenticated subject
Auth::principal();  // Principal|null  — the acting principal
Auth::device();     // Device|null     — the issuing device (null for Basic guard)
Auth::tenant();     // Tenant|null     — the tenant the principal acts within
Auth::type();       // string|null     — the tenant's type string, if declared
```

These reads are synchronous and free — the guard has already done the resolution work during the `user()` call that Laravel triggers on the first `Auth::check()` or route middleware pass.

## Multiple guards with separate trust boundaries

You can register multiple JWT guards, each with its own signing secret, audience claim, and principal resolver. Tokens issued under one guard cannot authenticate against another because the `aud` claim is enforced on every parse:

```php theme={null}
'guards' => [
    'staff' => [
        'driver'   => 'jwt',
        'provider' => 'users',
        'jwt'      => [
            'secret'   => env('STAFF_JWT_SECRET'),
            'audience' => 'staff-api',
        ],
    ],
    'customer' => [
        'driver'   => 'jwt',
        'provider' => 'users',
        'jwt'      => [
            'secret'   => env('CUSTOMER_JWT_SECRET'),
            'audience' => 'customer-api',
        ],
    ],
],
```

Routes opt into a specific boundary via `auth:staff` or `auth:customer` middleware. Tokens minted for the `staff-api` audience are rejected by the `customer` guard, and vice versa. Each guard can also carry its own kid-rotation set for fully independent signing-key lifecycles.

Issue tokens through the guard-scoped JWT service so the audience and signing material match the guard that will later verify them:

```php theme={null}
$accessToken = Auth::jwt('staff')->issueAccessToken($identity, $principal, $device);
```

<CardGroup cols={2}>
  <Card title="JWT configuration" icon="key" href="/configuration/jwt">
    Configure signing secrets, key rotation, TTLs, leeway, and per-guard JWT overrides.
  </Card>

  <Card title="Advanced configuration" icon="sliders" href="/configuration/advanced">
    Per-guard principal resolvers, identifier fields, resolution caching, and timebox tuning.
  </Card>
</CardGroup>
