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

# OAuth 2.1 Security

> OAuth 2.1 security best practices and implementation details

# OAuth 2.1 Security Implementation

Plugged.in implements **OAuth 2.1** security best practices to ensure the highest level of security for MCP server authentication. This document outlines the security measures in place.

## OAuth 2.1 Overview

OAuth 2.1 is the next evolution of the OAuth 2.0 framework, consolidating security best practices from various OAuth extensions and eliminating insecure patterns.

<Info>
  **Key Difference**: OAuth 2.1 is **NOT** a separate specification, but rather a consolidation of OAuth 2.0 + security best practices (PKCE, token rotation, etc.)
</Info>

***

## Implemented Security Features

### 1. PKCE (Proof Key for Code Exchange)

**RFC 7636 - Mandatory for all OAuth flows**

<CardGroup cols={2}>
  <Card title="Protection Against" icon="shield-halved">
    * Authorization code interception attacks
    * Code injection attacks
    * Man-in-the-middle attacks
  </Card>

  <Card title="Implementation" icon="code">
    * S256 challenge method (SHA-256)
    * 256-bit code verifier entropy
    * Automatic verification on callback
  </Card>
</CardGroup>

**How it works:**

```typescript theme={null}
// 1. Generate code verifier (256 bits of entropy)
const codeVerifier = crypto.randomBytes(32).toString('base64url');

// 2. Generate code challenge (SHA-256 hash)
const codeChallenge = crypto
  .createHash('sha256')
  .update(codeVerifier)
  .digest('base64url');

// 3. Send challenge in authorization request
authUrl.searchParams.set('code_challenge', codeChallenge);
authUrl.searchParams.set('code_challenge_method', 'S256');

// 4. Send verifier in token exchange
tokenParams.set('code_verifier', codeVerifier);
```

***

### 2. State Parameter Integrity Binding

**OAuth 2.1 Best Practice - Prevents PKCE state tampering**

<Warning>
  **Attack Prevented**: Attackers cannot modify stored PKCE state parameters (server UUID, user ID, code verifier) to steal tokens or hijack flows.
</Warning>

**Implementation:**

```typescript theme={null}
// Generate HMAC-SHA256 integrity hash
const integrityHash = crypto
  .createHmac('sha256', secret)
  .update(`${state}|${serverUuid}|${userId}|${codeVerifier}`)
  .digest('hex');

// Store with PKCE state
await db.insert(oauthPkceStatesTable).values({
  state,
  server_uuid: serverUuid,
  user_id: userId,
  code_verifier: codeVerifier,
  integrity_hash: integrityHash, // ✅ OAuth 2.1 enhancement
  expires_at: new Date(Date.now() + 5 * 60 * 1000),
});

// Verify on callback (timing-safe comparison)
if (!verifyIntegrityHash(pkceState)) {
  // Revoke and reject - tampering detected
}
```

**Files:**

* `lib/oauth/integrity.ts` - HMAC generation and verification
* `app/actions/trigger-mcp-oauth.ts` - Hash creation
* `app/api/oauth/callback/route.ts` - Hash verification

***

### 3. Reduced PKCE Expiration (5 Minutes)

**OAuth 2.1 Recommendation - Reduced attack window**

<Accordion title="Why 5 minutes instead of 10?">
  OAuth 2.1 recommends shorter PKCE state expiration to minimize the window for:

  * Replay attacks
  * Code interception attempts
  * State prediction attacks

  **Previous**: 10 minutes (OAuth 2.0 typical)
  **Current**: 5 minutes (OAuth 2.1 recommended)
</Accordion>

```typescript theme={null}
// Updated expiration
expires_at: new Date(Date.now() + 5 * 60 * 1000) // 5 minutes
```

**Files:**

* `db/schema.ts` - Schema documentation
* `app/actions/trigger-mcp-oauth.ts:447` - State creation
* `app/api/oauth/callback/route.ts:166` - Expiration check

***

### 4. Refresh Token Rotation

**OAuth 2.1 Best Practice - Prevents token reuse attacks**

<CardGroup cols={2}>
  <Card title="Single-Use Tokens" icon="rotate">
    Each refresh token can only be used **once**. After use, it's immediately invalidated.
  </Card>

  <Card title="Reuse Detection" icon="triangle-exclamation">
    If a refresh token is reused, **all tokens are revoked** as a security measure.
  </Card>
</CardGroup>

**Implementation Flow:**

```mermaid theme={null}
graph TD
    A[Token Refresh Request] --> B{Check refresh_token_used_at}
    B -->|Already Used| C[SECURITY ALERT: Revoke ALL Tokens]
    B -->|Not Used| D[Mark as Used Immediately]
    D --> E[Exchange with OAuth Server]
    E --> F[Receive New Tokens]
    F --> G[Store New Tokens + Clear used_at Flag]
    G --> H[Success]
    C --> I[Force Re-Authentication]
```

**Code:**

```typescript theme={null}
// 1. Check for token reuse (security measure)
if (tokenRecord.refresh_token_used_at) {
  console.error('[OAuth Security] Refresh token reuse detected!');
  // Revoke ALL tokens as security measure
  await db.delete(mcpServerOAuthTokensTable)
    .where(eq(mcpServerOAuthTokensTable.server_uuid, serverUuid));
  return false;
}

// 2. Mark token as used BEFORE exchange
await db.update(mcpServerOAuthTokensTable)
  .set({ refresh_token_used_at: new Date() })
  .where(eq(mcpServerOAuthTokensTable.server_uuid, serverUuid));

// 3. Exchange for new tokens
const newTokens = await exchangeRefreshToken(refreshToken);

// 4. Store new tokens with cleared used_at flag
await db.update(mcpServerOAuthTokensTable)
  .set({
    access_token_encrypted: encrypt(newTokens.access_token),
    refresh_token_encrypted: encrypt(newTokens.refresh_token),
    refresh_token_used_at: null, // ✅ Fresh token, not used
    updated_at: new Date(),
  })
  .where(eq(mcpServerOAuthTokensTable.server_uuid, serverUuid));
```

**Database Schema:**

```sql theme={null}
ALTER TABLE mcp_server_oauth_tokens
ADD COLUMN refresh_token_used_at TIMESTAMPTZ;
```

**Files:**

* `db/schema.ts:1940` - Schema definition
* `lib/oauth/token-refresh-service.ts:97-104` - Reuse detection
* `lib/oauth/token-refresh-service.ts:128-139` - Mark as used
* `lib/oauth/token-refresh-service.ts:179-190` - Store new tokens

***

### 5. HTTP Basic Authentication for Client Credentials

**RFC 6749 Section 2.3.1 - Prevents credential logging**

<Warning>
  **Security Issue**: Sending `client_secret` in URL-encoded body causes it to be logged in:

  * Proxy access logs
  * WAF logs
  * Server access logs
  * Load balancer logs
</Warning>

**Secure Method:**

```typescript theme={null}
// ✅ CORRECT: HTTP Basic Auth (RFC 6749 Section 2.3.1)
const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
headers['Authorization'] = `Basic ${credentials}`;

// Body contains ONLY refresh_token
const tokenParams = new URLSearchParams({
  grant_type: 'refresh_token',
  refresh_token: refreshToken,
  // NO client_secret here!
});
```

**Files:**

* `lib/oauth/token-refresh-service.ts:134-140` - Token refresh
* `app/api/oauth/callback/route.ts` - Token exchange

***

## Security Enhancements Summary

| Feature              | Standard        | Plugged.in Implementation      | Security Benefit            |
| -------------------- | --------------- | ------------------------------ | --------------------------- |
| **PKCE**             | RFC 7636        | ✅ S256 method, 256-bit entropy | Prevents code interception  |
| **State Integrity**  | OAuth 2.1 BP    | ✅ HMAC-SHA256 binding          | Prevents state tampering    |
| **PKCE Expiration**  | OAuth 2.1 BP    | ✅ 5 minutes (vs 10)            | Reduced attack window       |
| **Token Rotation**   | OAuth 2.1 BP    | ✅ Single-use + reuse detection | Prevents token replay       |
| **Client Auth**      | RFC 6749 §2.3.1 | ✅ HTTP Basic Auth              | Prevents credential logging |
| **User Binding**     | OWASP           | ✅ PKCE state → user\_id FK     | Prevents flow hijacking     |
| **Server Ownership** | Custom          | ✅ Multi-level validation       | Prevents token substitution |
| **Rate Limiting**    | OWASP           | ✅ 10 req/15min on callback     | Prevents brute force        |

***

## Attack Scenarios Prevented

### Before OAuth 2.1 Implementation

❌ **Authorization Code Injection**
→ Attacker could hijack victim's OAuth flow

❌ **Token Reuse Attacks**
→ Stolen refresh tokens could be used indefinitely

❌ **State Parameter Tampering**
→ Attacker could modify PKCE state to steal tokens

❌ **Credential Logging**
→ Client secrets exposed in access logs

❌ **Extended Attack Window**
→ 10-minute PKCE expiration too long

### After OAuth 2.1 Implementation

✅ **All OAuth flows bound to authenticated user**
✅ **Refresh tokens are single-use only**
✅ **HMAC integrity verification prevents tampering**
✅ **Client secrets never logged (HTTP Basic Auth)**
✅ **5-minute PKCE expiration reduces risk**
✅ **Automatic token revocation on reuse**
✅ **Server ownership validation**
✅ **Rate limiting on OAuth endpoints**

***

## Multi-Instance & Production Requirements

### Redis Rate Limiting (CRITICAL)

<Warning>
  **Multi-Instance Deployments**: Redis is **REQUIRED** for distributed rate limiting when running multiple application instances. In-memory rate limiting is NOT SAFE for horizontal scaling.
</Warning>

**Why Redis is Required:**

* Rate limits are enforced **per instance** with in-memory storage
* Attackers can bypass rate limits by distributing requests across instances
* OAuth callback rate limits become ineffective
* Token refresh rate limits become ineffective

**Setup:**

```bash theme={null}
# Install Redis client
npm install redis

# Configure Redis URL
export REDIS_URL="redis://redis-host:6379"

# Production with authentication
export REDIS_URL="redis://user:password@redis-host:6379"
```

**Verification:**

The application will log on startup:

```
✅ [RateLimit] Using Redis backend for distributed rate limiting
```

If you see this warning in production:

```
⚠️  [RateLimit] WARNING: Using in-memory rate limiting in production!
⚠️  [RateLimit] This is NOT SAFE for multi-instance deployments.
```

**Action required**: Configure `REDIS_URL` immediately.

### Performance Optimizations

#### OAuth Config Caching

OAuth configurations are cached for **5 minutes** to reduce database load:

```typescript theme={null}
// Automatic - no configuration needed
// Each instance maintains its own LRU cache (max 500 entries)
// Cache invalidates on config updates
```

**Impact:** Significantly reduces database queries for frequently refreshed tokens.

#### Server Ownership Validation

Optimized from 3 sequential queries to 1 JOIN query:

```sql theme={null}
-- Before: N+1 problem (3 round-trips)
SELECT profile_uuid FROM mcp_servers WHERE uuid = $1;
SELECT project_uuid FROM profiles WHERE uuid = $2;
SELECT user_id FROM projects WHERE uuid = $3;

-- After: Single JOIN query
SELECT projects.user_id
FROM mcp_servers
INNER JOIN profiles ON mcp_servers.profile_uuid = profiles.uuid
INNER JOIN projects ON profiles.project_uuid = projects.uuid
WHERE mcp_servers.uuid = $1;
```

**Impact:** 60-70% latency improvement for token refresh operations.

#### Request Timeouts

All OAuth API calls now have 10-second timeouts:

```typescript theme={null}
await fetch(oauthConfig.token_endpoint, {
  method: 'POST',
  headers,
  body: tokenParams,
  signal: AbortSignal.timeout(10000), // NEW: Prevents hanging requests
});
```

**Impact:** Prevents resource exhaustion from unresponsive OAuth providers.

### IPv6 Support

Metrics endpoint now supports **IPv6 CIDR validation**:

```bash theme={null}
# IPv4 (existing)
METRICS_ALLOWED_IPS="127.0.0.1,::1,172.17.0.0/16"

# IPv6 (NEW)
METRICS_ALLOWED_IPS="127.0.0.1,::1,2001:db8::/32"

# IPv4-mapped IPv6
METRICS_ALLOWED_IPS="127.0.0.1,::1,::ffff:192.0.2.0/24"
```

### Production Security Checklist

<Card title="Pre-Production Checklist" icon="clipboard-check">
  **Rate Limiting:**

  * [ ] Redis configured with `REDIS_URL`
  * [ ] Verify Redis connection on startup
  * [ ] No rate limit warnings in production logs

  **Metrics Security:**

  * [ ] `METRICS_ALLOWED_IPS` restricted to Prometheus IP only
  * [ ] Default `10.0.0.0/8` removed (16.7M IPs)
  * [ ] IPv6 ranges validated if applicable

  **OAuth Configuration:**

  * [ ] All instances share same `NEXTAUTH_SECRET`
  * [ ] All instances connect to same database
  * [ ] OAuth config caching verified (check logs)

  **Performance:**

  * [ ] Database connection pooling configured
  * [ ] Server ownership queries optimized (JOIN)
  * [ ] Request timeouts enabled (10s)
</Card>

### Multi-Instance Deployment

For complete multi-instance deployment guide, see:

<Card title="Multi-Instance Deployment" icon="server" href="/deployment/multi-instance">
  Production-ready horizontal scaling with Redis, load balancing, and high availability
</Card>

***

## Migration Notes

### Database Changes

Two migrations were applied to support OAuth 2.1:

**Migration 0071: State Integrity Hash**

```sql theme={null}
ALTER TABLE oauth_pkce_states
ADD COLUMN integrity_hash TEXT NOT NULL;
```

**Migration 0072: Refresh Token Rotation**

```sql theme={null}
ALTER TABLE mcp_server_oauth_tokens
ADD COLUMN refresh_token_used_at TIMESTAMPTZ;
```

### Backward Compatibility

✅ **Fully Backward Compatible**

* Existing OAuth flows continue to work
* New security features apply to all new flows
* No breaking changes to API
* Automatic migration on application upgrade

***

## Environment Variables

### Required

```bash .env theme={null}
# Use existing NEXTAUTH_SECRET or set dedicated OAuth secret
NEXTAUTH_SECRET=<your-secret-here>

# Or use dedicated OAuth integrity secret
OAUTH_INTEGRITY_SECRET=<generate-with-openssl-rand-base64-32>
```

### Recommended

```bash .env theme={null}
# Enable automatic PKCE cleanup
CRON_SECRET=<your-cron-secret>

# Production deployment flag
CLOUD_DEPLOY=true
```

***

## Monitoring & Observability

### Structured Logging with Loki

Plugged.in uses **structured JSON logging** with Loki for comprehensive OAuth security monitoring. All security events are automatically logged with full context for analysis.

<Info>
  See [Observability Documentation](/observability/overview) for complete setup guide
</Info>

### Security Events Logged

All OAuth operations emit structured JSON logs to Loki:

<CodeGroup>
  ```json Normal OAuth Flow theme={null}
  {
    "level": 30,
    "time": 1699564800000,
    "service_name": "pluggedin-app",
    "event": "oauth_flow_success",
    "provider": "github-mcp-server",
    "duration_ms": 3500,
    "hasRefreshToken": true,
    "msg": "OAuth flow completed successfully"
  }
  ```

  ```json Token Refresh Success theme={null}
  {
    "level": 30,
    "event": "token_refresh_success",
    "serverUuid": "server-uuid-123",
    "duration_ms": 850,
    "hasNewRefreshToken": true,
    "msg": "Token rotation complete"
  }
  ```

  ```json CRITICAL: Token Reuse Detected theme={null}
  {
    "level": 60,
    "event": "oauth_refresh_token_reuse_detected",
    "severity": "critical",
    "userId": "user-id-123",
    "serverUuid": "server-uuid-456",
    "tokenUsedAt": "2024-01-15T10:29:45Z",
    "currentAttempt": "2024-01-15T10:30:00Z",
    "msg": "Refresh token reuse detected - revoking all tokens"
  }
  ```

  ```json CRITICAL: Code Injection Attempt theme={null}
  {
    "level": 60,
    "event": "oauth_code_injection_attempt",
    "severity": "critical",
    "attackerUserId": "attacker-id",
    "victimUserId": "victim-id",
    "state": "[REDACTED]",
    "serverUuid": "server-uuid",
    "msg": "Authorization code injection attack detected"
  }
  ```

  ```json HIGH: Integrity Violation theme={null}
  {
    "level": 50,
    "event": "oauth_integrity_violation",
    "severity": "high",
    "violationType": "hash_mismatch",
    "state": "[REDACTED]",
    "expectedHash": "abc123...",
    "actualHash": "def456...",
    "msg": "PKCE state integrity check failed"
  }
  ```
</CodeGroup>

### LogQL Security Queries

**Critical Security Events (Last Hour):**

```logql theme={null}
{service_name="pluggedin-app"}
  | json
  | severity="critical"
  | event =~ "(token_reuse|code_injection)"
```

**All Integrity Violations:**

```logql theme={null}
{service_name="pluggedin-app"}
  | json
  | event="oauth_integrity_violation"
  | line_format "{{.violationType}}: {{.msg}}"
```

**Token Refresh Failures:**

```logql theme={null}
{service_name="pluggedin-app"}
  | json
  | event="token_refresh_failure"
  | line_format "{{.reason}}: {{.err.message}}"
```

**OAuth Flow Success Rate:**

```logql theme={null}
(
  sum(count_over_time({service_name="pluggedin-app"} | json | event="oauth_flow_success" [5m]))
  /
  sum(count_over_time({service_name="pluggedin-app"} | json | event =~ "oauth_flow_(success|failure)" [5m]))
) * 100
```

### Prometheus Metrics

17 OAuth-specific metrics for real-time monitoring:

<CardGroup cols={2}>
  <Card title="Flow Success Rate" icon="chart-line">
    `oauth_flows_total{status="success"}` / `oauth_flows_total`

    **SLO**: > 95%
  </Card>

  <Card title="Token Reuse Detection" icon="triangle-exclamation">
    `oauth_token_refresh_total{status="reuse_detected"}`

    **Alert**: > 0 (Critical)
  </Card>

  <Card title="Code Injection Attempts" icon="shield-exclamation">
    `oauth_code_injection_attempts_total`

    **Alert**: > 0 (Critical)
  </Card>

  <Card title="Integrity Violations" icon="shield-halved">
    `oauth_integrity_violations_total`

    **Alert**: > 0 (High)
  </Card>
</CardGroup>

**PromQL Alert Queries:**

```promql theme={null}
# P0: Token reuse detected
increase(oauth_token_refresh_total{status="reuse_detected"}[5m]) > 0

# P0: Code injection attempt
increase(oauth_code_injection_attempts_total[5m]) > 0

# P1: OAuth flow success rate below 95%
(
  sum(rate(oauth_flows_total{status="success"}[5m]))
  / sum(rate(oauth_flows_total[5m]))
) < 0.95

# P1: Token refresh p95 duration > 2s
histogram_quantile(0.95,
  sum(rate(oauth_token_refresh_duration_seconds_bucket[5m])) by (le)
) > 2
```

### Grafana Dashboards

Pre-built dashboards available in `/observability/dashboards`:

<CardGroup cols={3}>
  <Card title="OAuth Overview" icon="chart-mixed">
    * Success rates
    * Operations/min
    * Active tokens
    * Error trends
  </Card>

  <Card title="Security Dashboard" icon="shield-halved">
    * Security events timeline
    * Attack heatmap
    * Top attackers
    * Violation breakdown
  </Card>

  <Card title="Performance Dashboard" icon="gauge">
    * Latency percentiles (p50, p95, p99)
    * Throughput by provider
    * Slow operations
    * Discovery performance
  </Card>
</CardGroup>

### Critical Alerts Configuration

Configure these alerts in Grafana:

```yaml theme={null}
groups:
  - name: oauth_critical
    rules:
      # P0 Alerts - Page immediately
      - alert: OAuthTokenReuseDetected
        expr: increase(oauth_token_refresh_total{status="reuse_detected"}[5m]) > 0
        labels:
          severity: critical
          priority: P0
        annotations:
          summary: "OAuth token reuse attack detected"
          description: "Potential replay attack - tokens revoked"

      - alert: OAuthCodeInjectionAttempt
        expr: increase(oauth_code_injection_attempts_total[5m]) > 0
        labels:
          severity: critical
          priority: P0
        annotations:
          summary: "OAuth code injection attempt"
          description: "Authorization code injection attack in progress"

      # P1 Alerts - Notify team
      - alert: OAuthFlowSuccessRateLow
        expr: |
          (sum(rate(oauth_flows_total{status="success"}[5m]))
           / sum(rate(oauth_flows_total[5m]))) < 0.95
        for: 5m
        labels:
          severity: high
          priority: P1
        annotations:
          summary: "OAuth flow success rate below 95%"

      - alert: OAuthIntegrityViolations
        expr: increase(oauth_integrity_violations_total[15m]) > 5
        labels:
          severity: high
          priority: P1
        annotations:
          summary: "Multiple OAuth integrity violations detected"
```

### Metrics to Monitor

<ResponseField name="OAuth Flow Success Rate" type="percentage" required>
  **SLO**: > 95%

  **Metric**: `oauth_flows_total{status="success"}` / `oauth_flows_total`

  **Alert if**: \< 95% for 5 minutes
</ResponseField>

<ResponseField name="Token Refresh Success Rate" type="percentage" required>
  **SLO**: > 99%

  **Metric**: `oauth_token_refresh_total{status="success"}` / `oauth_token_refresh_total`

  **Alert if**: \< 99% for 5 minutes
</ResponseField>

<ResponseField name="Token Reuse Detection" type="counter" required>
  **Expected**: 0

  **Metric**: `oauth_token_refresh_total{status="reuse_detected"}`

  **Alert if**: > 0 (IMMEDIATE - Critical)
</ResponseField>

<ResponseField name="Code Injection Attempts" type="counter" required>
  **Expected**: 0

  **Metric**: `oauth_code_injection_attempts_total`

  **Alert if**: > 0 (IMMEDIATE - Critical)
</ResponseField>

<ResponseField name="Integrity Violations" type="counter">
  **Expected**: \~0

  **Metric**: `oauth_integrity_violations_total`

  **Alert if**: > 5 per 15 minutes (High)
</ResponseField>

<ResponseField name="Token Refresh p95 Latency" type="duration">
  **SLO**: \< 2 seconds

  **Metric**: `histogram_quantile(0.95, oauth_token_refresh_duration_seconds_bucket)`

  **Alert if**: > 2s for 5 minutes
</ResponseField>

### Incident Response Playbooks

<AccordionGroup>
  <Accordion title="Token Reuse Detected (P0)">
    **Severity**: Critical - Immediate action required

    **Indicators**:

    * `oauth_refresh_token_reuse_detected` event in logs
    * `oauth_token_refresh_total{status="reuse_detected"}` > 0

    **Automatic Actions**:

    * ✅ All tokens for server immediately revoked
    * ✅ User forced to re-authenticate
    * ✅ Security event logged with full context

    **Manual Response**:

    1. Check logs for `serverUuid` and `userId`
    2. Review user's recent OAuth activity
    3. Check for concurrent requests (race condition vs. attack)
    4. If attack: Block user IP, audit account
    5. If race condition: Review application logs, check for retry logic issues

    **Loki Query**:

    ```logql theme={null}
    {service_name="pluggedin-app"}
      | json
      | event="oauth_refresh_token_reuse_detected"
      | line_format "User: {{.userId}}, Server: {{.serverUuid}}, Used: {{.tokenUsedAt}}"
    ```
  </Accordion>

  <Accordion title="Code Injection Attempt (P0)">
    **Severity**: Critical - Immediate action required

    **Indicators**:

    * `oauth_code_injection_attempt` event in logs
    * `oauth_code_injection_attempts_total` > 0

    **Automatic Actions**:

    * ✅ Authorization code rejected
    * ✅ Security event logged with attacker/victim details

    **Manual Response**:

    1. Identify attacker from logs: `attackerUserId`
    2. Identify victim: `victimUserId`
    3. Block attacker account immediately
    4. Notify victim user of attempted account compromise
    5. Audit all OAuth flows for both users in last 24h
    6. Check for pattern of attacks (same IP, same target servers)

    **Loki Query**:

    ```logql theme={null}
    {service_name="pluggedin-app"}
      | json
      | event="oauth_code_injection_attempt"
      | line_format "Attacker: {{.attackerUserId}}, Victim: {{.victimUserId}}"
    ```
  </Accordion>

  <Accordion title="Integrity Violations (P1)">
    **Severity**: High - Investigate within 1 hour

    **Indicators**:

    * `oauth_integrity_violation` events in logs
    * `oauth_integrity_violations_total` increasing

    **Possible Causes**:

    * State tampering attempt
    * Database corruption
    * Application bug (HMAC calculation mismatch)

    **Manual Response**:

    1. Check `violationType`: hash\_mismatch, state\_reuse, user\_mismatch
    2. Review affected `serverUuid` and `userId`
    3. If single occurrence: Likely user error or network issue
    4. If multiple from same user: Possible attack or client issue
    5. If widespread: Check for application deployment issues

    **Loki Query**:

    ```logql theme={null}
    {service_name="pluggedin-app"}
      | json
      | event="oauth_integrity_violation"
      | line_format "[{{.violationType}}] {{.msg}}"
    ```
  </Accordion>

  <Accordion title="Low Success Rate (P1)">
    **Severity**: High - Investigate within 1 hour

    **Indicators**:

    * OAuth success rate \< 95%
    * Increased error rates in dashboards

    **Possible Causes**:

    * OAuth server downtime
    * Network issues
    * Configuration changes
    * High user error rate (expired states, etc.)

    **Manual Response**:

    1. Check recent deployments
    2. Review error distribution by type
    3. Check OAuth server status
    4. Review network connectivity
    5. Check for expired PKCE states (>5 minutes)

    **PromQL Query**:

    ```promql theme={null}
    topk(10, sum by (event) (
      rate(oauth_flows_total{status="failure"}[5m])
    ))
    ```
  </Accordion>
</AccordionGroup>

### Sensitive Data Redaction

All sensitive OAuth data is automatically redacted in logs:

<Warning>
  **Redacted Fields** (never logged in plaintext):

  * `access_token`
  * `refresh_token`
  * `code_verifier`
  * `client_secret`
  * `authorization_code`
</Warning>

Example redacted log:

```json theme={null}
{
  "event": "token_refresh_success",
  "access_token": "[REDACTED]",
  "refresh_token": "[REDACTED]",
  "serverUuid": "visible-server-id",
  "msg": "Token successfully refreshed"
}
```

***

## Testing OAuth 2.1 Compliance

### Test State Integrity

```bash theme={null}
# Should fail - tampering detected
curl "https://app.plugged.in/api/oauth/callback?code=valid&state=tampered" \
  -H "Cookie: session_token"

# Expected: oauth_error=integrity_violation
```

### Test Refresh Token Reuse

```bash theme={null}
# 1. Valid refresh
curl -X POST /api/refresh-token -d "server_uuid=..."

# 2. Attempt reuse (should revoke all tokens)
curl -X POST /api/refresh-token -d "server_uuid=..."

# Expected: All tokens revoked, forced re-auth
```

### Test PKCE Expiration

```bash theme={null}
# Wait 6 minutes after initiating OAuth
curl "https://app.plugged.in/api/oauth/callback?code=valid&state=expired"

# Expected: oauth_error=state_expired
```

***

## References

<CardGroup cols={2}>
  <Card title="RFC 7636" icon="book" href="https://www.rfc-editor.org/rfc/rfc7636.html">
    PKCE - Proof Key for Code Exchange
  </Card>

  <Card title="RFC 6749" icon="book" href="https://www.rfc-editor.org/rfc/rfc6749.html">
    OAuth 2.0 Authorization Framework
  </Card>

  <Card title="OAuth 2.1 Draft" icon="book" href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1">
    OAuth 2.1 Specification (Draft)
  </Card>

  <Card title="Security BCP" icon="shield-halved" href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics">
    OAuth 2.0 Security Best Practices
  </Card>
</CardGroup>

***

## Summary

Plugged.in implements **industry-leading OAuth 2.1 security** with:

✅ **5 Core Security Enhancements** beyond OAuth 2.0
✅ **8 Attack Scenarios** prevented
✅ **Zero Breaking Changes** for existing integrations
✅ **Comprehensive Monitoring** and alerting
✅ **Full RFC Compliance** with OAuth 2.1 draft

Your MCP server authentication is **enterprise-grade secure**.
