API Authentication
API authentication patterns including OAuth 2.0, JWT, and API keys for securing HTTP APIs
You are an expert in API authentication patterns for designing robust APIs.
## Key Points
1. App redirects user to authorization server
2. User authenticates and consents
3. Authorization server redirects back with code
4. App exchanges code for tokens
5. Authorization server returns access + refresh tokens
- Use short-lived access tokens (5-15 minutes) with refresh tokens to limit exposure if a token is compromised.
- Always validate JWT signature, expiration, issuer, and audience claims on every request.
- Store API keys and secrets in a dedicated secrets manager, never in source code or environment files committed to version control.
- Using symmetric signing (HS256) for JWTs in distributed systems where the secret must be shared across services; prefer asymmetric signing (RS256/ES256).
- Treating API keys as user authentication; they identify applications, not individual users, and lack fine-grained permissions on their own.
## Quick Example
```http
GET /v1/data HTTP/1.1
X-API-Key: sk_live_abc123def456
```
```
Header: { "alg": "RS256", "typ": "JWT" }
Payload: { "sub": "user_42", "scope": "read write", "exp": 1700000000 }
Signature: RS256(header + payload, private_key)
```skilldb get api-design-skills/API AuthenticationFull skill: 156 linesAPI Authentication — API Design
You are an expert in API authentication patterns for designing robust APIs.
Core Philosophy
Overview
Authentication verifies the identity of an API consumer. The right pattern depends on who the consumer is (user, service, third-party app) and the security requirements of the system. Most production APIs combine multiple mechanisms.
Core Concepts
API Keys
Simple bearer tokens suitable for server-to-server communication and metering.
GET /v1/data HTTP/1.1
X-API-Key: sk_live_abc123def456
API keys identify the calling application, not a user. They work well for rate limiting and usage tracking but should never be exposed in client-side code.
JWT (JSON Web Tokens)
Self-contained tokens that encode claims and are cryptographically signed.
Header: { "alg": "RS256", "typ": "JWT" }
Payload: { "sub": "user_42", "scope": "read write", "exp": 1700000000 }
Signature: RS256(header + payload, private_key)
GET /v1/users/me HTTP/1.1
Authorization: Bearer eyJhbGciOi...
OAuth 2.0
A delegation framework that lets users grant third-party apps limited access without sharing credentials.
Authorization Code Flow (web apps):
1. App redirects user to authorization server
GET /authorize?response_type=code&client_id=APP&redirect_uri=CALLBACK&scope=read
2. User authenticates and consents
3. Authorization server redirects back with code
GET /callback?code=AUTH_CODE
4. App exchanges code for tokens
POST /token
grant_type=authorization_code&code=AUTH_CODE&client_id=APP&client_secret=SECRET
5. Authorization server returns access + refresh tokens
Client Credentials Flow (service-to-service):
POST /token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=SVC&client_secret=SECRET&scope=read
Implementation Patterns
JWT Validation Middleware
import jwt
from functools import wraps
def require_auth(f):
@wraps(f)
def decorated(request, *args, **kwargs):
token = request.headers.get("Authorization", "").removeprefix("Bearer ")
if not token:
return {"error": "missing_token"}, 401
try:
claims = jwt.decode(token, PUBLIC_KEY, algorithms=["RS256"],
audience="https://api.example.com")
except jwt.ExpiredSignatureError:
return {"error": "token_expired"}, 401
except jwt.InvalidTokenError:
return {"error": "invalid_token"}, 401
request.user = claims
return f(request, *args, **kwargs)
return decorated
Token Refresh
Short-lived access tokens paired with long-lived refresh tokens reduce the blast radius of a leaked token.
POST /token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=REFRESH_TOKEN&client_id=APP
Scope-Based Authorization
Encode permissions as scopes and enforce them per endpoint.
def require_scope(required_scope):
def decorator(f):
@wraps(f)
def wrapper(request, *args, **kwargs):
scopes = request.user.get("scope", "").split()
if required_scope not in scopes:
return {"error": "insufficient_scope"}, 403
return f(request, *args, **kwargs)
return wrapper
return decorator
@require_auth
@require_scope("orders:write")
def create_order(request):
...
Best Practices
- Use short-lived access tokens (5-15 minutes) with refresh tokens to limit exposure if a token is compromised.
- Always validate JWT signature, expiration, issuer, and audience claims on every request.
- Store API keys and secrets in a dedicated secrets manager, never in source code or environment files committed to version control.
Common Pitfalls
- Using symmetric signing (HS256) for JWTs in distributed systems where the secret must be shared across services; prefer asymmetric signing (RS256/ES256).
- Treating API keys as user authentication; they identify applications, not individual users, and lack fine-grained permissions on their own.
Anti-Patterns
Over-engineering for hypothetical scale. Building for millions of users when you have hundreds adds complexity without value. Solve today's problems first.
Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide well wastes time and introduces unnecessary risk.
Premature abstraction. Creating elaborate frameworks and utilities before you have enough concrete cases to know what the abstraction should look like produces the wrong abstraction.
Neglecting error handling at boundaries. Internal code can trust its inputs, but system boundaries (user input, APIs, file I/O) require defensive validation.
Skipping documentation for obvious code. What is obvious to you today will not be obvious to your colleague next month or to you next year.
Install this skill directly: skilldb add api-design-skills
Related Skills
API Documentation
OpenAPI and Swagger documentation practices for generating accurate, maintainable API references
API Error Handling
Error response design and HTTP status code conventions for consistent, actionable API error reporting
API Pagination
Pagination patterns including cursor-based, offset, and keyset pagination for efficient list endpoints
API Versioning
API versioning strategies for evolving APIs without breaking existing consumers
GRAPHQL Design
GraphQL schema design patterns for building flexible, efficient, and evolvable query APIs
REST Design
RESTful API design principles for building consistent, intuitive, and scalable HTTP APIs