> ## 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.

# Local OAuth Provider

> Configure FrontMCP's built-in OAuth 2.1 authorization server

FrontMCP includes a built-in OAuth 2.1 authorization server for self-contained authentication scenarios.

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

## Basic Configuration

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@FrontMcp({
  info: { name: 'MyServer', version: '1.0.0' },
  auth: {
    mode: 'orchestrated',
    type: 'local',
    consent: { enabled: true },
  },
})
export class Server {}
```

## Configuration Options

| Option               | Type                        | Default              | Description                      |
| -------------------- | --------------------------- | -------------------- | -------------------------------- |
| `consent`            | `ConsentConfig`             | `{ enabled: false }` | Consent UI configuration         |
| `sessionMode`        | `'stateful' \| 'stateless'` | `'stateful'`         | Session management strategy      |
| `tokenStorage`       | `object`                    | `{ type: 'memory' }` | Token storage backend            |
| `allowDefaultPublic` | `boolean`                   | `false`              | Allow unauthenticated access     |
| `anonymousScopes`    | `string[]`                  | `['anonymous']`      | Scopes for anonymous sessions    |
| `incrementalAuth`    | `IncrementalAuthConfig`     | `{ enabled: true }`  | Progressive authorization config |

***

## OAuth Endpoints

Local mode exposes standard OAuth 2.1 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    | Get user profile            |
| `/.well-known/oauth-authorization-server` | GET    | Server metadata             |
| `/.well-known/jwks.json`                  | GET    | Public signing keys         |

### Authorization Request

```
GET /oauth/authorize
  ?response_type=code
  &client_id=your-client-id
  &redirect_uri=http://localhost:3000/callback
  &scope=openid profile
  &state=random-state
  &code_challenge=base64url(sha256(verifier))
  &code_challenge_method=S256
```

### Token Exchange

```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=authorization-code
&redirect_uri=http://localhost:3000/callback
&client_id=your-client-id
&code_verifier=original-verifier
```

### OAuth Callback Flow (`/oauth/callback`)

The callback endpoint processes the user's response from the authorization page and creates an authorization code. It executes through the following stages:

| Stage                     | Description                                               |
| ------------------------- | --------------------------------------------------------- |
| `parseInput`              | Extract query parameters (email, pending\_auth\_id, etc.) |
| `validatePendingAuth`     | Verify pending auth record and validate providers         |
| `handleIncrementalAuth`   | Process progressive authorization for apps                |
| `handleFederatedAuth`     | Chain through federated identity providers                |
| `createAuthorizationCode` | Generate authorization code with consent data             |
| `redirectToClient`        | Redirect user back to client with code                    |

#### Callback Query Parameters

| Parameter         | Description                                         |
| ----------------- | --------------------------------------------------- |
| `pending_auth_id` | Reference to pending authorization record           |
| `email`           | User's email address                                |
| `name`            | User's display name (optional)                      |
| `incremental`     | Set to `true` for progressive auth (optional)       |
| `app_id`          | Target app for incremental authorization (optional) |
| `federated`       | Set to `true` for federated login (optional)        |
| `providers`       | Selected provider IDs (optional, array or string)   |
| `tools`           | Selected tool IDs for consent (optional, array)     |

#### Federated Provider Chaining

When `federated=true` and providers are selected:

1. A federated session is created to track progress
2. PKCE is generated for the first provider
3. User is redirected to the first provider's authorization URL
4. After each provider completes, the chain continues to the next
5. Once all providers complete, an authorization code is created

<Warning>
  Selected provider IDs are validated against the pending authorization's allowed providers. Invalid provider IDs are rejected with a 400 error.
</Warning>

#### Error Handling

* Missing `pending_auth_id`: Returns 400 with error page
* Expired authorization: Returns 400 with "request expired" message
* Invalid provider selection: Returns 400 with "invalid provider selection" message
* Missing required fields: Returns 500 with error page

***

## Key Management

### Auto-Generated Keys

By default, FrontMCP generates RS256 keys at startup:

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
auth: {
  mode: 'orchestrated',
  type: 'local',
  // Keys auto-generated
}
```

<Info>
  Auto-generated keys are lost on restart. Existing tokens become invalid.
</Info>

### Persistent Keys

Provide keys for stable token validation:

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
auth: {
  mode: 'orchestrated',
  type: 'local',
  local: {
    signKey: {
      kty: 'RSA',
      kid: 'my-key-id',
      alg: 'RS256',
      n: '...',  // RSA modulus
      e: 'AQAB',
      d: '...',  // Private exponent
      // ... other JWK parameters
    },
  },
}
```

### ES256 Keys

Use ES256 for smaller tokens:

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
auth: {
  mode: 'orchestrated',
  type: 'local',
  local: {
    signKey: {
      kty: 'EC',
      crv: 'P-256',
      x: '...',
      y: '...',
      d: '...',
    },
  },
}
```

***

## Dynamic Client Registration

DCR allows clients to register programmatically. DCR is enabled by default in development.

### Registration Request

```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
POST /oauth/register
Content-Type: application/json

{
  "redirect_uris": ["http://localhost:3000/callback"],
  "client_name": "My Application",
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}
```

### Registration Response

```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
{
  "client_id": "generated-uuid",
  "client_id_issued_at": 1234567890,
  "redirect_uris": ["http://localhost:3000/callback"],
  "client_name": "My Application",
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}
```

<Warning>
  DCR is intended for development only. In production, pre-register clients. DCR only allows localhost redirect URIs by default.
</Warning>

***

## Per-App Configuration

Configure local auth per app with `splitByApp: true`:

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@App({
  name: 'Billing',
  auth: {
    mode: 'orchestrated',
    type: 'local',
    consent: { enabled: true },
  },
})
export class BillingApp {}

@App({
  name: 'Analytics',
  auth: {
    mode: 'orchestrated',
    type: 'local',
  },
})
export class AnalyticsApp {}

@FrontMcp({
  info: { name: 'Suite', version: '1.0.0' },
  apps: [BillingApp, AnalyticsApp],
  splitByApp: true,
})
export class Server {}
```

***

## Token Storage

### In-Memory (Development)

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
auth: {
  mode: 'orchestrated',
  type: 'local',
  tokenStorage: { type: 'memory' },
}
```

### Redis (Production)

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
auth: {
  mode: 'orchestrated',
  type: 'local',
  tokenStorage: {
    type: 'redis',
    config: {
      host: process.env.REDIS_HOST!,
      port: parseInt(process.env.REDIS_PORT || '6379'),
      password: process.env.REDIS_PASSWORD,
      keyPrefix: 'myapp:auth:',
    },
  },
}
```

***

## Consent Configuration

Enable and customize the consent UI:

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
auth: {
  mode: 'orchestrated',
  type: 'local',
  consent: {
    enabled: true,
    groupByApp: true,           // Group tools by app
    showDescriptions: true,     // Show tool descriptions
    allowSelectAll: true,       // Allow selecting all tools
    requireSelection: true,     // Require at least one tool
    rememberConsent: true,      // Remember for future sessions
    excludedTools: ['health'],  // Always available tools
    defaultSelectedTools: [],   // Pre-selected tools
    customMessage: 'Select the tools you want to grant access to.',
  },
}
```

***

## Complete Example

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { FrontMcp } from '@frontmcp/sdk';

@FrontMcp({
  info: { name: 'MyServer', version: '1.0.0' },
  auth: {
    mode: 'orchestrated',
    type: 'local',
    consent: { enabled: true },
    sessionMode: 'stateful',
    tokenStorage: {
      type: 'redis',
      config: {
        host: process.env.REDIS_HOST!,
        port: parseInt(process.env.REDIS_PORT || '6379'),
        password: process.env.REDIS_PASSWORD,
      },
    },
    refresh: {
      enabled: true,
      skewSeconds: 60,
    },
    local: {
      signKey: JSON.parse(process.env.JWT_SIGNING_KEY!),
    },
    incrementalAuth: {
      enabled: true,
      allowSkip: true,
      skippedAppBehavior: 'require-auth',
    },
  },
})
export class Server {}
```

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="Tokens invalid after restart">
    Auto-generated keys are lost on restart. Either:

    * Provide persistent keys via `local.signKey`
    * Use Redis for token storage
    * Accept that users must re-authenticate
  </Accordion>

  <Accordion title="PKCE verification failed">
    Ensure you're using S256 challenge method and the code\_verifier matches the original code\_challenge.
  </Accordion>

  <Accordion title="Session not found across instances">
    When running multiple server instances, use Redis for token storage to share session state.
  </Accordion>
</AccordionGroup>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Remote OAuth" icon="cloud" href="/frontmcp/authentication/remote">
    Connect to external identity providers
  </Card>

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

  <Card title="Production Checklist" icon="clipboard-check" href="/frontmcp/authentication/production">
    Security requirements for deployment
  </Card>

  <Card title="Tokens & Sessions" icon="key" href="/frontmcp/authentication/token">
    Configure token lifetimes
  </Card>
</CardGroup>
