Skip to content

OAuth and Security

Chris & Mike edited this page Mar 31, 2026 · 7 revisions

OAuth and Security

postgres-mcp provides enterprise-grade security through simple bearer token authentication, OAuth 2.1 authentication, scope-based access control, and supply chain security.


Simple Bearer Token

Lightweight authentication for development or single-tenant deployments. No external identity provider needed.

node dist/cli.js --transport http --port 3000 --auth-token my-secret --postgres "postgres://..."

# Or via environment variable
export MCP_AUTH_TOKEN=my-secret
node dist/cli.js --transport http --port 3000 --postgres "postgres://..."

Docker:

docker run --rm -p 3000:3000 \
  -e POSTGRES_URL=postgres://user:pass@host:5432/db \
  -e MCP_AUTH_TOKEN=my-secret-token \
  writenotenow/postgres-mcp:latest \
  --transport http --port 3000

Clients must include Authorization: Bearer <token> on all requests. /health and / are exempt. Unauthenticated requests receive 401 with WWW-Authenticate: Bearer headers per RFC 6750.

Priority: When both --auth-token and --oauth-enabled are set, OAuth 2.1 takes precedence. The bearer token middleware is skipped when OAuth is active.


OAuth 2.1 Authentication

When using HTTP transport, OAuth 2.1 protects your MCP endpoints.

Configuration

CLI Options:

node dist/cli.js \
  --transport http \
  --port 3000 \
  --postgres "postgres://user:pass@localhost:5432/db" \
  --oauth-enabled \
  --oauth-issuer http://localhost:8080/realms/postgres-mcp \
  --oauth-audience postgres-mcp-client

Environment Variables (Required):

Variable Description
OAUTH_ENABLED Enable OAuth (true)
OAUTH_ISSUER OAuth provider URL
OAUTH_AUDIENCE Expected audience claim

Environment Variables (Optional - auto-discovered):

Variable Description
OAUTH_JWKS_URI JSON Web Key Set endpoint
OAUTH_CLOCK_TOLERANCE Token clock skew tolerance (seconds)

OAuth Scopes

Access control is managed through OAuth scopes in the token:

Access Level Scopes

Scope Access Level
read Read-only queries (SELECT, EXPLAIN)
write Read + write operations (INSERT, UPDATE, DELETE)
admin Full administrative access (includes Code Mode)
full Grants all access

Resource-Level Scopes

Scope Pattern Description
db:{name} Access to specific database
schema:{name} Access to specific schema
table:{schema}:{table} Access to specific table

Example Token Scopes

{
  "scope": "read write db:production schema:public"
}

Code Mode Security

Code Mode (pg_execute_code) requires the admin scope and enforces:

  • Rate limiting: 60 executions/minute
  • Blocked operations: require(), process, eval(), filesystem, network
  • Isolation: Sandboxed V8 context

See Code-Mode for full documentation.


Audit Trail

Write and admin tool invocations are logged to a structured JSONL audit trail with OAuth identity, timing, and outcome. Enable with --audit-log <path> or AUDIT_LOG_PATH (use stderr for containers). Use --audit-redact to omit tool arguments.

See Audit Trail for full documentation including JSONL format, scope mapping, container mode, and log shipping integrations.


RFC Compliance

This implementation follows current OAuth standards:

RFC Description
RFC 9728 OAuth 2.1 Protected Resource Metadata
RFC 8414 OAuth 2.1 Authorization Server Metadata
RFC 7591 OAuth 2.1 Dynamic Client Registration

The server exposes metadata at /.well-known/oauth-protected-resource.


Keycloak Integration

When using Keycloak as your OAuth provider:

  1. Create a realm for postgres-mcp
  2. Create a client with the appropriate scopes
  3. Important: Add an Audience mapper to include the correct aud claim

Adding Audience Mapper:

  1. Go to Client → Client Scopes → Dedicated Scope
  2. Add Mapper → Audience
  3. Set the audience value to match OAUTH_AUDIENCE

HTTP Transport Security

When using HTTP transport, the server applies multiple layers of protection:

Feature Description
7 Security Headers X-Content-Type-Options, X-Frame-Options, CSP, Cache-Control, Referrer-Policy (no-referrer), Permissions-Policy, opt-in HSTS
Server Timeouts Configurable timeouts prevent Slowloris DoS (via MCP_REQUEST_TIMEOUT and MCP_HEADERS_TIMEOUT)
DNS Rebinding Enforces validateHostHeader to strictly match permissible client origins
Rate Limiting 100 requests/minute per IP with Retry-After header on 429 responses
Health Check Bypass /health always serves regardless of rate-limit state (monitoring-safe)
Trust Proxy Opt-in trustProxy for correct X-Forwarded-For IP extraction behind reverse proxies
Wildcard Subdomain CORS Pattern-based origin matching (e.g., *.example.com)
Body Size Limit Configurable max request body size (default: 1 MB)
Cross-Protocol Guard SSE session IDs rejected on /mcp and vice versa

Supply Chain Security

Docker Image Security

Docker images include security features:

Feature Description
Build Provenance Cryptographic proof of build process
SBOM Software Bill of Materials
Attestations Verifiable build integrity
Non-root Runs as non-privileged user

SHA-Pinned Images

For maximum security, use SHA-pinned images:

# Find SHA tags at Docker Hub
docker pull writenotenow/postgres-mcp:sha256-<manifest-digest>

# Or use direct digest
docker pull writenotenow/postgres-mcp@sha256:<manifest-digest>

Best Practices

Credential Management

  • Never commit credentials to version control
  • Use environment variables for secrets
  • Consider secret managers (Vault, AWS Secrets Manager)

Network Security

  • Use TLS/SSL for database connections
  • Enable OAuth or --auth-token for HTTP transport
  • Restrict network access with firewalls

Access Control

  • Use minimal scopes (principle of least privilege)
  • Enable resource-level scopes for multi-tenant environments
  • Audit scope assignments regularly

Related

Clone this wiki locally