A practical guide to API authentication approaches, from simple API keys to JWT tokens and OAuth 2.0 flows. Includes decision trees, security considerations, and configuration examples.
Is the API public (open data)?
├── Yes → No auth needed (but still rate limit!)
└── No → Who are the consumers?
├── Server-to-server (backend services)
│ ├── Internal (same organization) → Mutual TLS or API Keys
│ └── External (partners) → OAuth 2.0 Client Credentials
├── Browser/SPA apps → OAuth 2.0 + PKCE (no client secret!)
├── Mobile apps → OAuth 2.0 + PKCE
└── Mixed (all of the above) → OAuth 2.0 with multiple grant types
| Method | Security | Complexity | Statefulness | Best For |
|---|---|---|---|---|
| API Keys | Low-Medium | Low | Stateless | Server-to-server, internal |
| Basic Auth | Low | Low | Stateless | Development, simple scripts |
| JWT (Bearer) | High | Medium | Stateless | Modern APIs, microservices |
| OAuth 2.0 | High | High | Stateful (auth server) | Public APIs, third-party access |
| Mutual TLS | Very High | High | Stateless | Service mesh, zero-trust |
| HMAC Signatures | High | Medium | Stateless | Webhook verification, AWS-style |
The simplest form of authentication. The client includes a secret key in every request.
Client → Gateway: GET /api/users
X-Api-Key: sk-abc123def456
Gateway: Look up "sk-abc123def456" in key store
→ Found: "Acme Corp Mobile App"
→ Forward request with X-Client-Name: acme-mobile
Gateway → Upstream: GET /api/users
X-Client-Name: acme-mobile
X-Request-Id: uuid-here
BAD: GET /api/users?api_key=sk-abc123 ← Key in URL (logged everywhere)
BAD: Authorization: sk-abc123 ← No "Bearer" prefix (ambiguous)
GOOD: X-Api-Key: sk-abc123 ← Dedicated header
GOOD: Authorization: Bearer sk-abc123 ← Standard header with scheme
JSON Web Tokens carry claims (user ID, roles, permissions) in a cryptographically signed payload. The gateway can verify the token without contacting an auth server.
1. Client → Auth Server: POST /auth/login {email, password}
2. Auth Server → Client: {token: "eyJ...", refresh_token: "eyK..."}
3. Client → Gateway: GET /api/users
Authorization: Bearer eyJ...
4. Gateway: Decode JWT header → check algorithm
Verify signature (HMAC or RSA)
Check exp (not expired?)
Check iss (correct issuer?)
Extract claims (sub, role, tenant_id)
5. Gateway → Upstream: GET /api/users
X-User-Id: user-123
X-User-Role: admin
X-Tenant-Id: acme-corp
Header: {"alg": "RS256", "typ": "JWT", "kid": "key-2024-01"}
Payload: {"iss": "auth.example.com", "sub": "user-123",
"role": "admin", "exp": 1700003600, "iat": 1700000000}
Signature: RSASHA256(base64url(header) + "." + base64url(payload), private_key)
HS256 (HMAC-SHA256):
+ Simple (shared secret)
+ Fast verification
- Secret must be shared between auth server and gateway
- If secret leaks, anyone can forge tokens
- Hard to rotate (all services must update simultaneously)
→ Use for: Internal services you fully control
RS256 (RSA-SHA256):
+ Public key can be distributed freely (JWKS endpoint)
+ Only auth server needs the private key
+ Easy to rotate (publish new public key, keep old for grace period)
- Slower verification (RSA math vs. HMAC)
→ Use for: Production APIs, external consumers, microservices
ES256 (ECDSA-SHA256):
+ Smaller keys and signatures than RSA
+ Same security model as RS256
- Less library support in some languages
→ Use for: Mobile/IoT where bandwidth matters
exp, iss, aud claims. Don't trust the alg header blindly.token_version claim against DB)OAuth 2.0 is not an authentication protocol -- it's an authorization framework. Use OpenID Connect (OIDC) on top of OAuth 2.0 for authentication.
Server-to-server (no user involved):
→ Client Credentials Grant
→ Client authenticates with client_id + client_secret
→ Gets an access token directly
Browser SPA (public client):
→ Authorization Code Grant + PKCE
→ User redirected to auth server login page
→ Auth server returns authorization code
→ Client exchanges code for tokens (with PKCE verifier)
Mobile app (public client):
→ Authorization Code Grant + PKCE (same as SPA)
→ Use deep links for redirect URI
Traditional web app (confidential client):
→ Authorization Code Grant (with client_secret)
→ Server-side code exchange (secret never exposed to browser)
# API Key auth
plugins:
- name: key-auth
config:
key_names: [X-Api-Key, apikey]
hide_credentials: true
# JWT auth
plugins:
- name: jwt
config:
header_names: [Authorization]
claims_to_verify: [exp]# Lambda authorizer (custom auth logic)
securitySchemes:
lambda_authorizer:
type: apiKey
name: Authorization
in: header
x-amazon-apigateway-authorizer:
type: token
authorizerUri: "arn:aws:..."See src/auth_middleware.py for API key authentication implementation.
Part of API Developer Pro. Support: support@datanest.dev
A practical comparison to help you choose the right gateway for your architecture. Covers features, pricing, operational complexity, and migration strategies.
| Factor | Kong (OSS) | Kong Enterprise | AWS API Gateway | Custom (Python/Go) |
|---|---|---|---|---|
| Cost | Free (infra only) | $$$$ | Pay-per-request | Free (dev time) |
| Hosting | Self-managed | Self/Cloud | Fully managed | Self-managed |
| Config | Declarative YAML | YAML + Admin API | OpenAPI + Console | Code |
| Plugins | 100+ community | 100+ enterprise | Limited | Build your own |
| Scale | Millions RPS | Millions RPS | 10K RPS (default) | Depends on impl |
| Learning curve | Medium | Medium | Low | High (but full control) |
| Vendor lock-in | Low | Medium | High | None |
| Best for | Multi-cloud, k8s | Enterprise, support | AWS-native apps | Internal tools, edge cases |
| Feature | Kong OSS | AWS API GW (REST) | AWS API GW (HTTP) | Custom |
|---|---|---|---|---|
| Rate limiting | Plugin | Usage plans | Basic | Build it |
| JWT validation | Plugin | Lambda authorizer | JWT authorizer | Build it |
| API key auth | Plugin | Built-in | Built-in | Build it |
| OAuth 2.0 | Plugin | Cognito integration | Cognito/Lambda | Build it |
| Request transform | Plugin | VTL templates | None | Build it |
| Response caching | Plugin | Built-in (0.5-3600s) | None | Build it |
| WebSocket | Plugin | Built-in | Built-in | Complex |
| gRPC proxy | Plugin | Not supported | Built-in | Build it |
| Load balancing | Built-in | Not applicable | Not applicable | Build it |
| Health checks | Built-in | Not applicable | Not applicable | Build it |
| Circuit breaking | Plugin | Not built-in | Not built-in | Build it |
| Canary releases | Plugin | Canary deployments | Not built-in | Build it |
| Logging | Plugin (many) | CloudWatch | CloudWatch | Build it |
Choose Kong when you need:
Avoid Kong when:
Choose AWS API Gateway when you need:
Avoid AWS API Gateway when:
Build custom when you need:
Avoid custom when:
| Solution | Monthly Cost | Notes |
|---|---|---|
| Kong OSS (self-hosted) | ~$150-300 | 2x t3.medium EC2 + ALB |
| Kong Enterprise | ~$3,000+ | License + infra |
| AWS API GW (REST) | ~$35 | $3.50/million requests |
| AWS API GW (HTTP) | ~$10 | $1.00/million requests |
| Custom (self-hosted) | ~$50-150 | 1x t3.medium EC2 |
| Solution | Monthly Cost | Notes |
|---|---|---|
| Kong OSS (self-hosted) | ~$2,000-5,000 | Auto-scaling group + Redis |
| Kong Enterprise | ~$10,000+ | License + infra |
| AWS API GW (REST) | ~$3,500 | Volume pricing kicks in |
| AWS API GW (HTTP) | ~$1,000 | Much cheaper than REST |
| Custom (self-hosted) | ~$1,000-3,000 | Depends on backend complexity |
1. Export Kong routes as OpenAPI spec
2. Add x-amazon-apigateway-* extensions
3. Replace Kong plugins with equivalent AWS features
4. Replace upstream health checks with ALB health checks
5. Update DNS to point to API Gateway endpoint
1. Export API as OpenAPI spec (API GW console → Export)
2. Strip x-amazon-apigateway-* extensions
3. Convert to Kong declarative config (deck convert)
4. Map Lambda authorizers to Kong JWT/key-auth plugins
5. Set up upstream targets and health checks
6. Update DNS
1. Document all routes, auth rules, and rate limits
2. Implement the core proxy with middleware pipeline
3. Port each plugin/feature one at a time
4. Load test to ensure comparable throughput
5. Shadow traffic (send to both old and new) for validation
6. Cut over DNS
Internet → Kong Gateway → Service A
→ Service B
→ Service C
Best for: Monolithic gateway, centralized policy.
Internet → API GW (Users) → Lambda/ECS
→ API GW (Orders) → Lambda/ECS
→ API GW (Analytics) → Lambda/ECS
Best for: Team autonomy, independent scaling.
Internet → Edge Gateway (Kong/CloudFront)
→ Internal Gateway (Envoy/Custom)
→ Service Mesh (Istio/Linkerd)
→ Services
Best for: Large organizations with different security zones.
| Directory | Gateway | Contents |
|---|---|---|
kong/ | Kong | Declarative configs for services, routes, plugins |
aws/ | AWS API Gateway | OpenAPI spec with extensions, Lambda authorizer |
src/ | Custom Python | Working reverse proxy with middleware pipeline |
configs/ | Custom | Dev and production gateway configurations |
Each implementation demonstrates the same core features (routing, rate limiting, auth, transforms) so you can compare approaches directly.
Part of API Developer Pro. Support: support@datanest.dev
Get the full API Gateway Patterns and unlock everything.
Get the complete guide with every chapter unlocked, including code samples, diagrams, and best practices.
Access all interactive tools with complete data, all workload profiles, and the full scenario library.
Downloadable source code, configuration files, and working examples from every chapter.
Free updates for life. Every new chapter, tool, and improvement included.