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

# Authorization Modes

> Deep dive into public, transparent, local, and remote authentication modes

FrontMCP's four authentication modes address different deployment scenarios. Understanding when to use each mode is critical for both security and developer experience.

## Mode Overview

The `auth.mode` literal accepts one of four values: `'public'`, `'transparent'`, `'local'`, `'remote'`.

```mermaid theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
graph TB
    subgraph "Authentication Modes"
        Public["mode: 'public'<br/>Anonymous Access"]
        Transparent["mode: 'transparent'<br/>Pass-through Tokens"]
        Local["mode: 'local'<br/>Built-in OAuth Server"]
        Remote["mode: 'remote'<br/>OAuth Server proxying to upstream IdP"]
    end
```

## Mode Comparison

| Feature                | Public            | Transparent       | Local                           | Remote                          |
| ---------------------- | ----------------- | ----------------- | ------------------------------- | ------------------------------- |
| Token Required         | No                | Yes (external)    | Yes (FrontMCP-issued)           | Yes (FrontMCP-issued)           |
| User Identity          | Anonymous         | From upstream IdP | From login form                 | From upstream IdP               |
| Token Signing          | HS256 (symmetric) | Upstream IdP      | HS256 (symmetric, `JWT_SECRET`) | HS256 (symmetric, `JWT_SECRET`) |
| Session Management     | Minimal           | Pass-through      | Full control                    | Full control                    |
| Multi-provider         | No                | Single provider   | Multiple via apps               | Multiple via apps               |
| Progressive Auth       | No                | No                | Yes                             | Yes                             |
| Tool-authz enforcement | No                | No                | Optional                        | Optional                        |

***

## Public Mode

No authentication required. All requests receive an anonymous session.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const auth: AuthOptionsInput = {
  mode: 'public',
  sessionTtl: 3600, // 1 hour
  anonymousScopes: ['anonymous'],
};
```

### How It Works

```mermaid theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
sequenceDiagram
    participant Client
    participant FrontMCP

    Client->>FrontMCP: Request without token
    FrontMCP->>FrontMCP: Generate anonymous JWT
    FrontMCP-->>Client: Response + anonymous session
```

### Configuration Options

| Option                   | Type                | Default         | Description                           |
| ------------------------ | ------------------- | --------------- | ------------------------------------- |
| `sessionTtl`             | `number`            | `3600`          | Session lifetime in seconds           |
| `anonymousScopes`        | `string[]`          | `['anonymous']` | Scopes assigned to anonymous sessions |
| `publicAccess.tools`     | `string[] \| 'all'` | `'all'`         | Tools accessible without auth         |
| `publicAccess.prompts`   | `string[] \| 'all'` | `'all'`         | Prompts accessible without auth       |
| `publicAccess.rateLimit` | `number`            | `60`            | Rate limit per IP per minute          |

### Use Cases

<CardGroup cols={2}>
  <Card title="Development" icon="code">
    Rapid prototyping without auth setup overhead
  </Card>

  <Card title="Public APIs" icon="globe">
    Endpoints that don't require user identity
  </Card>
</CardGroup>

<Warning>
  **Do NOT use public mode when you need:**

  * User identity tracking
  * Audit trails
  * Access control per user
  * Compliance requirements
</Warning>

***

## Transparent Mode

Pass-through tokens from an external identity provider. FrontMCP validates tokens but doesn't issue them.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const auth: AuthOptionsInput = {
  mode: 'transparent',
  provider: 'https://auth.example.com',
  providerConfig: {
    jwksUri: 'https://auth.example.com/.well-known/jwks.json',
  },
  expectedAudience: 'https://api.myservice.com',
  requiredScopes: ['openid'],
  allowAnonymous: false,
};
```

### How It Works

```mermaid theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
sequenceDiagram
    participant Client
    participant FrontMCP
    participant IdP as Identity Provider

    Client->>IdP: Authenticate
    IdP-->>Client: Access Token (JWT)
    Client->>FrontMCP: Request + Bearer token
    FrontMCP->>IdP: Fetch JWKS (cached)
    FrontMCP->>FrontMCP: Verify JWT signature
    FrontMCP->>FrontMCP: Validate claims (iss, aud, exp, scopes)
    FrontMCP-->>Client: Authorized response
```

### Configuration Options

| Option                             | Type                 | Default         | Description                                                                                                                             |
| ---------------------------------- | -------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `provider`                         | `string`             | Required        | Base URL of the IdP                                                                                                                     |
| `providerConfig.jwksUri`           | `string`             | Auto-discovered | Custom JWKS endpoint                                                                                                                    |
| `providerConfig.jwks`              | `JSONWebKeySet`      | -               | Inline JWKS for offline verification                                                                                                    |
| `providerConfig.additionalIssuers` | `string[]`           | -               | Extra `iss` values to trust beyond `provider` (e.g. a gateway that re-mints tokens). Trusted verbatim — set only to issuers you control |
| `providerConfig.verifyIssuer`      | `boolean`            | `true`          | Validate the token `iss` claim. Setting `false` **disables issuer checking entirely** — see the security note below                     |
| `expectedAudience`                 | `string \| string[]` | Resource URL    | Required audience claim value(s) — typically the protected resource URL. Defaults to the derived resource URL when omitted              |
| `requiredScopes`                   | `string[]`           | `[]`            | Scopes that must be present                                                                                                             |
| `allowAnonymous`                   | `boolean`            | `false`         | Allow requests without tokens                                                                                                           |

### Claim validation

A valid signature only proves the IdP's key signed the token — not that the token
was minted for **this** server. Because every service behind the same IdP shares
the same signing keys, FrontMCP enforces two claims on top of the signature:

* **Issuer (`iss`)** — must equal `provider` (matched with or without a trailing
  slash) or one of `providerConfig.additionalIssuers`. Enabled by default. This
  stops a token minted by the same IdP for a **different issuer** from being
  replayed here.
* **Audience (`aud`)** — if the token carries an `aud`, it must match
  `expectedAudience` (or the derived resource URL). This stops a token issued for
  **service A** from being replayed against **service B**. Tokens with no `aud`
  claim are accepted for IdP compatibility; set `expectedAudience` and issue
  audience-bound tokens for the strictest posture.

<Warning>
  **`providerConfig.verifyIssuer: false` disables issuer validation entirely.**
  Any token signed by a key in the provider JWKS is then accepted regardless of
  its `iss`. Only use it for a trusted gateway that re-mints tokens under an
  issuer you cannot enumerate with `additionalIssuers`, and always pair it with a
  strict `expectedAudience`. Whenever the issuer set is known, prefer
  `additionalIssuers` over disabling the check.
</Warning>

### Provider Examples

<Tabs>
  <Tab title="Auth0">
    ```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    auth: {
      mode: 'transparent',
      provider: 'https://your-tenant.auth0.com',
      // JWKS discovered automatically from /.well-known/jwks.json
      expectedAudience: 'https://api.yourservice.com',
    }
    ```
  </Tab>

  <Tab title="Okta">
    ```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    auth: {
      mode: 'transparent',
      provider: 'https://your-org.okta.com/oauth2/default',
      expectedAudience: 'api://default',
    }
    ```
  </Tab>

  <Tab title="Azure AD">
    ```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    auth: {
      mode: 'transparent',
      provider: 'https://login.microsoftonline.com/{tenant}/v2.0',
      providerConfig: {
        jwksUri: 'https://login.microsoftonline.com/{tenant}/discovery/v2.0/keys',
      },
      expectedAudience: 'api://{client-id}',
    }
    ```
  </Tab>
</Tabs>

### Use Cases

<CardGroup cols={2}>
  <Card title="Existing IdP Integration" icon="plug">
    Your organization already uses Auth0, Okta, or similar
  </Card>

  <Card title="Single Provider" icon="1">
    All users authenticate through one identity provider
  </Card>
</CardGroup>

<Warning>
  **Do NOT use transparent mode when you need:**

  * Multiple identity providers
  * Progressive authorization (add apps over time)
  * Server-side token storage with silent refresh
  * Custom token claims
</Warning>

***

## Local Mode

FrontMCP acts as a full OAuth 2.1 authorization server with a built-in login form. Self-contained auth server with built-in user management.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const auth: AuthOptionsInput = {
  mode: 'local',
  // 'memory' (default) is lost on restart. Use { sqlite: { path } } for
  // single-node persistence or { redis: ... } for multi-instance.
  tokenStorage: 'memory',
};
```

Local mode signs the tokens it issues with **HS256** using the `JWT_SECRET` environment variable (no RSA/EC key pair). See [Local OAuth](/frontmcp/authentication/local) for token signing, persistent `tokenStorage` (memory / sqlite / redis), the `requireEmail` / `anonymousSubject` single-operator options, and tunnel/issuer configuration.

Local mode also orchestrates **multiple upstream OAuth providers** out of the box. Declare a `providers` array (GitHub, Slack, Jira, …) and FrontMCP federates them at `/oauth/authorize`, refuses to mint a JWT until `federatedAuth.minProviders` (default `1`) are linked, stores each provider's tokens encrypted server-side, and exposes them to tools via `this.orchestration.getToken(id)`:

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const auth: AuthOptionsInput = {
  mode: 'local',
  providers: [
    { id: 'github', authorizeUrl: '…', tokenUrl: '…', clientId: '…', scopes: ['repo'] },
    { id: 'slack', authorizeUrl: '…', tokenUrl: '…', clientId: '…' },
  ],
  federatedAuth: { minProviders: 1 }, // no JWT until ≥1 linked
};
```

See [Multi-Provider Orchestration](/frontmcp/authentication/local#multi-provider-orchestration-providers) for the full provider schema, the `minProviders` / `requiredProviders` gate, and the `this.orchestration` tool API.

<Warning>
  The built-in login page accepts any email format without validation and is intended for development. Replace with a real identity provider for production use.
</Warning>

## Remote Mode

FrontMCP acts as an OAuth 2.1 authorization server that **proxies user authentication to an upstream IdP**. End users never submit credentials to FrontMCP directly — they're redirected to the upstream IdP, and FrontMCP exchanges the resulting upstream code/tokens before issuing its own session token to the MCP client.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const auth: AuthOptionsInput = {
  mode: 'remote',
  provider: 'https://auth.example.com',
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',
  scopes: ['openid', 'profile', 'email'],
  consent: { enabled: true },
};
```

### How It Works

```mermaid theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
sequenceDiagram
    participant User
    participant Client
    participant FrontMCP as FrontMCP Auth Server
    participant IdP as Upstream IdP
    participant Store as Authorization Store

    Note over Client: Generate PKCE<br/>code_verifier & code_challenge

    Client->>FrontMCP: GET /oauth/authorize<br/>response_type=code<br/>client_id, redirect_uri<br/>code_challenge (S256)<br/>scope, state

    FrontMCP->>Store: Store pending authorization
    FrontMCP-->>User: 302 Redirect to upstream IdP<br/>(authorize URL + upstream PKCE)

    User->>IdP: Authenticate (credentials, MFA, …)
    IdP-->>FrontMCP: Redirect back with upstream auth code
    FrontMCP->>IdP: POST /token (exchange upstream code)
    IdP-->>FrontMCP: { id_token, access_token, refresh_token }

    FrontMCP->>Store: Create FrontMCP authorization code<br/>(60s TTL, single-use)
    FrontMCP-->>Client: Redirect to redirect_uri<br/>?code=xxx&state=yyy

    Client->>FrontMCP: POST /oauth/token<br/>grant_type=authorization_code<br/>code, redirect_uri<br/>client_id, code_verifier

    FrontMCP->>Store: Get & validate code
    FrontMCP->>FrontMCP: Verify PKCE<br/>SHA256(code_verifier) == code_challenge
    FrontMCP->>Store: Mark code as used
    FrontMCP->>FrontMCP: Sign FrontMCP session token (JWT)<br/>using identity from upstream id_token
    FrontMCP->>Store: Store refresh token (links to upstream tokens)
    FrontMCP-->>Client: { access_token, refresh_token, expires_in }
```

### Configuration Options

| Option               | Type                                                             | Default                      | Description                                                                                                                             |
| -------------------- | ---------------------------------------------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `consent`            | `ConsentConfig`                                                  | `undefined` (block optional) | Tool-selection consent screen + call-time enforcement (see Local docs). When the block is present, inner `enabled` defaults to `false`. |
| `tokenStorage`       | `'memory' \| { redis: RedisConfig } \| { sqlite: SqliteConfig }` | `'memory'`                   | Storage backend (memory / sqlite / redis)                                                                                               |
| `allowDefaultPublic` | `boolean`                                                        | `false`                      | Allow unauthenticated requests                                                                                                          |
| `federatedAuth`      | `FederatedAuthConfig`                                            | -                            | Federated auth state validation                                                                                                         |
| `incrementalAuth`    | `IncrementalAuthConfig`                                          | `undefined` (block optional) | Progressive authorization. When the block is present, inner `enabled` defaults to `true`.                                               |

### Consent Configuration

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
consent: {
  enabled: true,
  groupByApp: true,
  showDescriptions: true,
  allowSelectAll: true,
  requireSelection: true,
  excludedTools: ['ping'],          // always available, never gated
  defaultSelectedTools: ['create-note'], // pre-checked on the screen
}
```

When `consent.enabled` is `true`, the local flow renders an **interactive tool-selection screen** after login and **enforces the selection at call time**: the chosen tool ids are embedded in the token's `consent` claim and a `tools/call` to an unselected tool is rejected with `TOOL_NOT_CONSENTED` (JSON-RPC `-32003`). The flags `groupByApp`, `showDescriptions`, `allowSelectAll`, `requireSelection`, `customMessage`, `excludedTools`, and `defaultSelectedTools` are all honored. See [Local OAuth → Consent](/frontmcp/authentication/local#consent-and-tool-authorization) for the full table.

<Note>
  Tokens minted without consent (consent disabled, or created via the test/programmatic factory) carry no `consent` claim and are unaffected — all tools remain callable. `rememberConsent` (default `true`) persists each user's per-client selection and reuses it on the next login, re-prompting only when a NEW tool appears; set it `false` to always re-show the screen.
</Note>

### Incremental Authorization

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
incrementalAuth: {
  enabled: true,
  allowSkip: true,                    // Allow skipping app auth
  showAllAppsAtOnce: true,            // Show all apps in one page
  skippedAppBehavior: 'require-auth', // 'anonymous' or 'require-auth'
}
```

### Federated Authentication Configuration

Configure how multi-provider (federated) authentication is gated and validated.

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
federatedAuth: {
  stateValidation: 'strict',     // 'strict' (recommended) or 'format'
  minProviders: 1,               // no JWT until ≥1 provider linked
  requiredProviders: ['github'], // these ids must all be linked
}
```

| Option              | Values                 | Description                                                                                                |
| ------------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------- |
| `stateValidation`   | `'strict' \| 'format'` | `'strict'`: Full state match (recommended). `'format'`: Only validates state format                        |
| `minProviders`      | `number`               | Minimum providers that must be linked before a JWT is minted (default `1` when `providers` are configured) |
| `requiredProviders` | `string[]`             | Provider ids that must all be linked before a JWT is minted                                                |

### OAuth Endpoints

Local and remote modes expose standard OAuth endpoints:

| Endpoint                                  | Method | Description                 |
| ----------------------------------------- | ------ | --------------------------- |
| `/oauth/authorize`                        | GET    | Start authorization flow    |
| `/oauth/token`                            | POST   | Exchange code for tokens    |
| `/oauth/register`                         | POST   | Dynamic Client Registration |
| `/oauth/userinfo`                         | GET    | User profile information    |
| `/.well-known/oauth-authorization-server` | GET    | Server metadata             |
| `/.well-known/jwks.json`                  | GET    | Public signing keys         |

### Use Cases

<CardGroup cols={2}>
  <Card title="Multi-Provider Federation" icon="object-group">
    Combine multiple IdPs under one session (Slack + GitHub + custom)
  </Card>

  <Card title="Progressive Authorization" icon="chart-line">
    Users authorize apps incrementally as needed
  </Card>

  <Card title="Full Token Control" icon="sliders">
    Custom token lifetimes, scopes, and refresh behavior
  </Card>

  <Card title="Tool-Authorization Enforcement" icon="list-check">
    Gate tools via the interactive consent picker (shipped) and enforce the selection at call time
  </Card>
</CardGroup>

<Warning>
  **Do NOT use `local` or `remote` mode when:**

  * You only have one IdP and don't need federation (use `transparent` instead)
  * You want to minimize auth complexity
  * Running multiple instances without Redis
</Warning>

***

## Mode Selection Flowchart

```mermaid theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
graph TD
    A[Start] --> B{Need user identity?}
    B -->|No| C[Public Mode]
    B -->|Yes| D{Multiple providers?}
    D -->|No| E{Need progressive auth<br/>or consent UI?}
    D -->|Yes| F[Local/Remote Mode]
    E -->|No| G[Transparent Mode]
    E -->|Yes| F
    F --> H{Have upstream IdP?}
    H -->|Yes| I[Remote Mode]
    H -->|No| J[Local Mode]
```

***

## Security Comparison

| Security Aspect        | Public                   | Transparent           | Local                    | Remote                                            |
| ---------------------- | ------------------------ | --------------------- | ------------------------ | ------------------------------------------------- |
| Token Verification     | None                     | Against upstream JWKS | HS256 symmetric secret   | HS256 symmetric secret (FrontMCP-issued session)  |
| PKCE Support           | N/A                      | Depends on IdP        | Always S256              | Always S256 (downstream); upstream depends on IdP |
| Refresh Token Rotation | N/A                      | Depends on IdP        | Always rotated           | Depends on upstream IdP                           |
| Signing Key            | `JWT_SECRET` (symmetric) | Upstream-managed      | `JWT_SECRET` (symmetric) | `JWT_SECRET` (symmetric) + upstream JWKS          |
| Tool-authz enforcement | No                       | No                    | Optional                 | Optional                                          |
| Session Revocation     | N/A                      | N/A                   | Supported                | Supported                                         |

***

## Token Verification Flow

```mermaid theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
sequenceDiagram
    participant Client
    participant FrontMCP
    participant JwksService

    Client->>FrontMCP: Request + Bearer token

    alt Public/Local/Remote Mode
        Note over FrontMCP: Verify HS256 signature<br/>with JWT_SECRET (symmetric)
        FrontMCP->>FrontMCP: Verify JWT locally
    else Transparent Mode
        FrontMCP->>JwksService: Get provider JWKS (cached)
        Note over JwksService: Fetch from jwksUri or<br/>discover via .well-known
        FrontMCP->>FrontMCP: Verify JWT against provider
    end

    alt Valid Token
        FrontMCP->>FrontMCP: Extract claims (sub, scopes, etc.)
        FrontMCP-->>Client: Authorized response
    else Invalid Token
        FrontMCP-->>Client: 401 Unauthorized<br/>WWW-Authenticate header
    end
```

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Remote OAuth" icon="cloud" href="/frontmcp/authentication/remote">
    Configure upstream IdP integration
  </Card>

  <Card title="Local OAuth" icon="server" href="/frontmcp/authentication/local">
    Set up self-contained authentication
  </Card>

  <Card title="Progressive Authorization" icon="forward" href="/frontmcp/authentication/progressive">
    Implement incremental app authorization
  </Card>

  <Card title="Production Deployment" icon="rocket" href="/frontmcp/authentication/production">
    Security checklist for production
  </Card>
</CardGroup>
