Skip to main content

OAuth Logs & LogQL Queries

Plugged.in uses structured JSON logging with automatic redaction and Loki integration for powerful log aggregation and querying.

Log Structure

All OAuth logs follow a consistent JSON structure:
{
  "level": 30,
  "time": 1699564800000,
  "service_name": "pluggedin-app",
  "version": "2.14.0",
  "trace_id": "abc123def456",
  "event": "oauth_token_refresh_success",
  "serverUuid": "server-uuid-123",
  "userId": "user-id-456",
  "duration_ms": 850,
  "msg": "Token refresh succeeded"
}

Log Fields

level
number
required
Log level: 10=trace, 20=debug, 30=info, 40=warn, 50=error, 60=fatal
time
number
required
Unix timestamp in milliseconds
service_name
string
required
Always “pluggedin-app” for the main application
version
string
required
Application version (from package.json)
trace_id
string
Unique trace ID for correlating related logs across operations
event
string
required
Event type (e.g., “oauth_token_refresh_success”, “oauth_integrity_violation”)
msg
string
required
Human-readable log message
err
object
Error details including stack trace (only for errors)

OAuth Event Types

Flow Events

// OAuth flow initiation
{
  "event": "oauth_flow_initiated",
  "provider": "github-mcp-server",
  "serverUuid": "...",
  "discoveryMethod": "rfc9728"
}

// OAuth flow success
{
  "event": "oauth_flow_success",
  "provider": "github-mcp-server",
  "duration_ms": 3500,
  "hasRefreshToken": true
}

// OAuth flow failure
{
  "event": "oauth_flow_failure",
  "provider": "github-mcp-server",
  "duration_ms": 1200,
  "error": "invalid_grant"
}

PKCE Events

// PKCE state created
{
  "event": "pkce_state_created",
  "state": "[REDACTED]",
  "serverUuid": "...",
  "expiresIn": 300
}

// PKCE validation success
{
  "event": "pkce_validation_success",
  "state": "[REDACTED]",
  "serverUuid": "...",
  "userId": "..."
}

// PKCE validation failure
{
  "event": "pkce_validation_failure",
  "state": "[REDACTED]",
  "reason": "expired",
  "expiredAt": "2024-01-15T10:30:00Z"
}

Token Events

// Token refresh initiated
{
  "event": "token_refresh_initiated",
  "serverUuid": "...",
  "userId": "...",
  "tokenExpired": true
}

// Token refresh success
{
  "event": "token_refresh_success",
  "serverUuid": "...",
  "duration_ms": 850,
  "hasNewRefreshToken": true
}

// Token rotation complete
{
  "event": "token_rotation_complete",
  "serverUuid": "...",
  "hasNewRefreshToken": true
}

Security Events

// Refresh token reuse detected (CRITICAL)
{
  "level": 60,
  "event": "oauth_refresh_token_reuse_detected",
  "userId": "...",
  "serverUuid": "...",
  "tokenUsedAt": "2024-01-15T10:29:45Z",
  "currentAttempt": "2024-01-15T10:30:00Z",
  "severity": "critical"
}

// Code injection attempt (CRITICAL)
{
  "level": 60,
  "event": "oauth_code_injection_attempt",
  "attackerUserId": "...",
  "victimUserId": "...",
  "state": "[REDACTED]",
  "serverUuid": "...",
  "severity": "critical"
}

// Integrity violation (HIGH)
{
  "level": 50,
  "event": "oauth_integrity_violation",
  "violationType": "hash_mismatch",
  "state": "[REDACTED]",
  "expectedHash": "...",
  "actualHash": "...",
  "severity": "high"
}

// Ownership violation (HIGH)
{
  "level": 50,
  "event": "oauth_ownership_violation",
  "userId": "...",
  "serverUuid": "...",
  "expectedUserId": "...",
  "actualUserId": "...",
  "severity": "high"
}

Cleanup Events

// PKCE cleanup
{
  "event": "pkce_cleanup_completed",
  "deletedCount": 15,
  "cutoffTime": "2024-01-15T10:20:00Z"
}

// Server PKCE cleanup
{
  "event": "pkce_server_cleanup_completed",
  "serverUuid": "...",
  "deletedCount": 3
}

LogQL Queries

Basic Queries

All OAuth events from last hour:
{service_name="pluggedin-app"} |= "oauth"
OAuth events for specific server:
{service_name="pluggedin-app"}
  | json
  | serverUuid="server-uuid-123"
  | event =~ "oauth_.*"
OAuth events by user:
{service_name="pluggedin-app"}
  | json
  | userId="user-id-456"
  | event =~ "oauth_.*"

Security Monitoring

Critical security events (P0):
{service_name="pluggedin-app"}
  | json
  | severity="critical"
  | event =~ "(token_reuse|code_injection)"
All integrity violations:
{service_name="pluggedin-app"}
  | json
  | event="oauth_integrity_violation"
Failed PKCE validations:
{service_name="pluggedin-app"}
  | json
  | event="pkce_validation_failure"
  | line_format "{{.reason}}: {{.msg}}"
Ownership violations by user:
{service_name="pluggedin-app"}
  | json
  | event="oauth_ownership_violation"
  | line_format "User {{.userId}} attempted access to server {{.serverUuid}}"

Performance Analysis

Token refresh operations over 2 seconds:
{service_name="pluggedin-app"}
  | json
  | event="token_refresh_success"
  | duration_ms > 2000
  | line_format "Slow refresh: {{.duration_ms}}ms for server {{.serverUuid}}"
OAuth flow duration distribution:
sum by (provider) (
  count_over_time(
    {service_name="pluggedin-app"}
    | json
    | event="oauth_flow_success"
    [1h]
  )
)
Average token refresh time:
avg_over_time(
  {service_name="pluggedin-app"}
  | json
  | event="token_refresh_success"
  | unwrap duration_ms
  [5m]
) / 1000

Error Tracking

OAuth errors in last 24h:
{service_name="pluggedin-app"}
  | json
  | level >= 50
  | event =~ "oauth_.*"
Token refresh failures with reasons:
{service_name="pluggedin-app"}
  | json
  | event="token_refresh_failure"
  | line_format "{{.reason}}: {{.err.message}}"
Discovery failures by method:
sum by (discoveryMethod) (
  count_over_time(
    {service_name="pluggedin-app"}
    | json
    | event="oauth_discovery_failure"
    [1h]
  )
)

Trace Correlation

Complete OAuth flow by trace_id:
{service_name="pluggedin-app"}
  | json
  | trace_id="abc123def456"
  | event =~ "oauth_.*"
Related operations for a server:
{service_name="pluggedin-app"}
  | json
  | serverUuid="server-uuid-123"
  | event =~ "(oauth_|pkce_|token_)"

Alerting Queries

Token reuse in last 5 minutes (CRITICAL ALERT):
count_over_time(
  {service_name="pluggedin-app"}
  | json
  | event="oauth_refresh_token_reuse_detected"
  [5m]
) > 0
High rate of code injection attempts (>10/hour):
count_over_time(
  {service_name="pluggedin-app"}
  | json
  | event="oauth_code_injection_attempt"
  [1h]
) > 10
OAuth flow success rate below 95%:
(
  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]))
) < 0.95
Token refresh failures spiking (>20/min):
rate(
  {service_name="pluggedin-app"}
  | json
  | event="token_refresh_failure"
  [1m]
) * 60 > 20

Log Aggregation Patterns

Count Events by Type

sum by (event) (
  count_over_time(
    {service_name="pluggedin-app"}
    | json
    | event =~ "oauth_.*"
    [1h]
  )
)

Top Error Messages

topk(10,
  sum by (msg) (
    count_over_time(
      {service_name="pluggedin-app"}
      | json
      | level >= 50
      | event =~ "oauth_.*"
      [24h]
    )
  )
)

Users with Most OAuth Activity

topk(20,
  sum by (userId) (
    count_over_time(
      {service_name="pluggedin-app"}
      | json
      | event =~ "oauth_.*"
      [24h]
    )
  )
)

Servers Requiring Most Token Refreshes

topk(10,
  sum by (serverUuid) (
    count_over_time(
      {service_name="pluggedin-app"}
      | json
      | event="token_refresh_success"
      [24h]
    )
  )
)

Sensitive Data Redaction

Sensitive fields are automatically redacted in logs:
Never log these fields in plaintext:
  • access_token
  • refresh_token
  • code_verifier
  • client_secret
  • authorization_code
Example redacted log:
{
  "event": "token_refresh_success",
  "access_token": "[REDACTED]",
  "refresh_token": "[REDACTED]",
  "serverUuid": "visible-server-id"
}

Best Practices

Use Trace IDs

Correlate related operations across services using trace_id field

Filter by Time Range

Always specify time ranges to avoid scanning entire log history

Index by Service

Use service_name label for efficient querying in multi-service deployments

Alert on Security Events

Set up Grafana alerts for critical security events (token reuse, code injection)

Monitor Performance

Track p50, p95, p99 for token refresh duration to detect degradation

Example Grafana Queries

Panel: OAuth Flow Success Rate (Last 24h)

Query:
(
  sum(count_over_time({service_name="pluggedin-app"} | json | event="oauth_flow_success" [$__range]))
  /
  sum(count_over_time({service_name="pluggedin-app"} | json | event =~ "oauth_flow_(success|failure)" [$__range]))
) * 100
Panel Type: Stat Unit: Percent (0-100)

Panel: Top OAuth Errors (Last 6h)

Query:
topk(5,
  sum by (msg) (
    count_over_time(
      {service_name="pluggedin-app"}
      | json
      | level >= 50
      | event =~ "oauth_.*"
      [6h]
    )
  )
)
Panel Type: Bar Chart

Panel: Security Events Timeline

Query:
{service_name="pluggedin-app"}
  | json
  | severity =~ "(critical|high)"
  | event =~ "(token_reuse|code_injection|integrity_violation|ownership_violation)"
  | line_format "[{{.severity}}] {{.event}}: {{.msg}}"
Panel Type: Logs

Troubleshooting

Check environment:
# Ensure NODE_ENV is NOT 'development' for JSON logs
NODE_ENV=production npm start
Development mode uses pino-pretty for human-readable logs.
Verify redaction is enabled in lib/observability/logger.ts:
const redactPaths = [
  'access_token',
  'refresh_token',
  'code_verifier',
  'client_secret',
  'authorization_code'
];
  1. Reduce time range
  2. Add more specific filters before parsing JSON
  3. Use indexed labels (service_name) first
  4. Consider using metric queries instead for aggregations
Ensure trace_id is generated for each request:
import { generateTraceId } from '@/lib/observability/logger';
const traceId = generateTraceId();

Next Steps