Skip to main content

Authentication in Microservices

Understanding OAuth2, JWT, OIDC, API keys, and mTLS for securing microservices at scale.


Table of Contents

  1. Authentication Fundamentals
  2. OAuth 2.0
  3. JWT (JSON Web Tokens)
  4. OpenID Connect (OIDC)
  5. API Keys
  6. Mutual TLS (mTLS)
  7. Service-to-Service Authentication
  8. Best Practices

Authentication Fundamentals

Authentication vs Authorization

Authentication: Verifying identity. "Who are you?"

Authorization: Verifying permissions. "What can you do?"

Example:

Authentication: User provides username/password, proves they're Alice
Authorization: Check if Alice can access resource /admin/users

Always separate these concerns in your architecture.

Authentication Types

User authentication: End users authenticating to access application.

Service authentication: Services authenticating to each other.

API authentication: External clients authenticating to APIs.

Each requires different mechanisms and trade-offs.

Security Requirements

Confidentiality: Credentials not exposed.

Integrity: Credentials not tampered with.

Non-repudiation: Can prove who performed action.

Defense in depth: Multiple layers of security.


OAuth 2.0

What Is OAuth 2.0

OAuth 2.0: Authorization framework allowing applications to obtain limited access to user accounts.

Key concept: Delegate authorization without sharing passwords.

Example: "Login with Google" - App gets access to your Google account without seeing your password.

OAuth 2.0 Roles

Resource Owner: User who owns the data (you).

Client: Application requesting access (mobile app, web app).

Authorization Server: Issues access tokens (Google, Auth0, Keycloak).

Resource Server: API hosting protected resources (your microservice).

OAuth 2.0 Flows

Authorization Code Flow (Most secure):

1. User clicks "Login"
2. Client redirects to Authorization Server
3. User authenticates and consents
4. Auth Server redirects back with authorization code
5. Client exchanges code for access token (backend call)
6. Client uses access token to access Resource Server

Why secure: Access token never exposed to browser.

Use for: Web applications with backend.

Client Credentials Flow (Service-to-service):

1. Service authenticates with client_id and client_secret
2. Auth Server returns access token
3. Service uses token to access other services

Use for: Backend services calling other backend services.

Implicit Flow (Deprecated):

1. User clicks "Login"
2. Client redirects to Authorization Server
3. User authenticates
4. Auth Server redirects back with access token in URL

Why deprecated: Access token exposed in browser history and logs.

Don't use. Use Authorization Code Flow with PKCE instead.

Password Grant (Deprecated):

1. Client collects username/password
2. Client sends credentials to Auth Server
3. Auth Server returns access token

Why deprecated: Client sees user password (defeats OAuth purpose).

Don't use unless absolutely necessary (first-party apps only).

Access Tokens

Opaque tokens:

  • Random string
  • Meaning only known to authorization server
  • Requires token introspection (API call to validate)

JWT tokens:

  • Self-contained (includes claims)
  • Can be validated locally (no API call)
  • Common in microservices

Refresh Tokens

Problem: Access tokens expire (short-lived, e.g., 1 hour).

Solution: Refresh tokens (long-lived, e.g., 30 days).

Flow:

1. User authenticates, receives access token + refresh token
2. Access token expires
3. Client sends refresh token to Auth Server
4. Auth Server issues new access token
5. Repeat

Security:

  • Refresh tokens stored securely (encrypted, httpOnly cookies)
  • Can be revoked if compromised
  • Rotation: Issue new refresh token on each use

OAuth 2.0 Scopes

Scopes: Permissions requested by client.

Example:

Scope: "read:profile write:posts"
Meaning: Read user profile, write posts

Best practices:

  • Principle of least privilege (request minimum scopes)
  • Fine-grained scopes (not "admin" for everything)
  • Document required scopes per endpoint

OAuth 2.0 in Microservices

API Gateway pattern:

1. API Gateway validates OAuth token
2. Gateway extracts user info from token
3. Gateway forwards request to backend services
4. Backend services trust Gateway (don't revalidate)

Distributed validation:

1. Each service validates token independently
2. Cache token validation results
3. Services trust authorization server

Token forwarding:

Client → Service A (validates token)
Service A → Service B (forwards token)
Service B → Service C (forwards token)
Each service validates same user token

Common Pitfalls

Storing tokens in localStorage: Vulnerable to XSS attacks.

  • Use httpOnly cookies instead.

No token expiration: Stolen tokens valid forever.

  • Always set expiration (1-24 hours typical).

Client secret in frontend: Anyone can extract from JavaScript.

  • Never put secrets in frontend code.

No state parameter: Vulnerable to CSRF attacks.

  • Always use state parameter in OAuth flows.

JWT (JSON Web Tokens)

JWT Structure

Three parts separated by dots:

header.payload.signature

Example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Header:

{
"alg": "RS256",
"typ": "JWT"
}

Payload (Claims):

{
"sub": "1234567890",
"name": "John Doe",
"email": "john@example.com",
"role": "admin",
"iat": 1516239022,
"exp": 1516242622
}

Signature:

HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)

Standard Claims

iss (Issuer): Who created the token.

sub (Subject): Who the token is about (user ID).

aud (Audience): Who the token is for (service name).

exp (Expiration): When token expires (Unix timestamp).

iat (Issued At): When token was created.

nbf (Not Before): Token not valid before this time.

jti (JWT ID): Unique token identifier.

Signing Algorithms

Symmetric (HMAC):

  • HS256, HS384, HS512
  • Same secret for signing and verification
  • Fast, simple
  • Secret must be shared (problematic in microservices)

Asymmetric (RSA/ECDSA):

  • RS256, RS384, RS512 (RSA)
  • ES256, ES384, ES512 (ECDSA)
  • Private key signs, public key verifies
  • Public key can be shared safely
  • Preferred for microservices

Recommendation: Use RS256 or ES256 for microservices.

JWT Validation

Must verify:

  1. Signature is valid
  2. Token not expired (check exp)
  3. Token not used before valid time (check nbf)
  4. Issuer is trusted (check iss)
  5. Audience matches (check aud)
  6. Algorithm is expected (no algorithm confusion)

Validation flow:

1. Extract JWT from Authorization header
2. Decode header and payload (don't trust yet)
3. Get public key for issuer
4. Verify signature using public key
5. Check exp, iss, aud claims
6. Extract user info from payload
7. Proceed with authorized request

JWT Best Practices

Keep tokens small:

  • Large tokens increase bandwidth
  • Don't put entire user object in token
  • Include only essential claims (user ID, roles)

Set appropriate expiration:

  • Access tokens: 15 minutes to 1 hour
  • Refresh tokens: Days to weeks
  • Balance security vs user experience

Don't store sensitive data:

  • Tokens are base64 encoded (not encrypted)
  • Anyone can decode and read payload
  • Never put passwords, SSNs, etc. in tokens

Use HTTPS always:

  • Tokens sent in clear text
  • HTTPS encrypts in transit
  • Never send tokens over HTTP

Validate algorithms:

Expected: RS256
Received: HS256
Attack: Attacker uses public key as HMAC secret

Protection: Explicitly check algorithm matches expected

Token Revocation

Problem: JWTs are stateless, can't be revoked easily.

Solutions:

Short expiration:

  • Tokens expire quickly (e.g., 15 minutes)
  • Stolen tokens have limited window

Blacklist:

  • Maintain list of revoked token IDs (jti claim)
  • Check blacklist on each request
  • Defeats stateless benefit

Versioning:

Include user_version in token
Check if user_version changed:
if token.user_version < current_user_version:
reject (user logged out all sessions)

Event-based:

On password change/logout:
Broadcast "invalidate tokens for user X"
Services cache invalidation for duration

JWT in Microservices

Gateway validates, services trust:

API Gateway:
- Validates JWT signature
- Checks expiration
- Extracts user info
- Adds user context to headers

Backend Services:
- Trust gateway headers
- Don't revalidate JWT
- Focus on business logic

Each service validates:

Each Service:
- Validates JWT independently
- Caches public keys (refresh periodically)
- Makes authorization decisions
- More secure but more overhead

OpenID Connect (OIDC)

What Is OIDC

OIDC: Authentication layer on top of OAuth 2.0.

OAuth 2.0: Authorization (what you can access).

OIDC: Authentication (who you are) + Authorization.

Key addition: ID Token (JWT with user identity).

OIDC Tokens

Access Token: Used to access APIs (OAuth 2.0).

ID Token: Contains user identity (OIDC specific).

Refresh Token: Get new tokens (OAuth 2.0).

ID Token example:

{
"iss": "https://accounts.google.com",
"sub": "1234567890",
"aud": "my-app-client-id",
"exp": 1516242622,
"iat": 1516239022,
"name": "John Doe",
"email": "john@example.com",
"email_verified": true,
"picture": "https://example.com/john.jpg"
}

OIDC Flow

1. User clicks "Login"
2. Client redirects to Authorization Server
Scope: "openid profile email"
3. User authenticates
4. Auth Server redirects with authorization code
5. Client exchanges code for tokens:
- Access token
- ID token
- Refresh token
6. Client validates ID token, extracts user info
7. Client uses access token for API requests

UserInfo Endpoint

Alternative to ID Token:

GET /userinfo
Authorization: Bearer {access_token}

Response:
{
"sub": "1234567890",
"name": "John Doe",
"email": "john@example.com",
"email_verified": true
}

Use when:

  • Need latest user info (ID token may be stale)
  • Want smaller ID tokens
  • Need additional claims not in ID token

OIDC Discovery

Well-known endpoint:

GET /.well-known/openid-configuration

Response:
{
"issuer": "https://accounts.example.com",
"authorization_endpoint": "https://accounts.example.com/oauth/authorize",
"token_endpoint": "https://accounts.example.com/oauth/token",
"userinfo_endpoint": "https://accounts.example.com/oauth/userinfo",
"jwks_uri": "https://accounts.example.com/.well-known/jwks.json"
}

Benefits:

  • Auto-configure OIDC clients
  • No hardcoded URLs
  • Easy to update endpoints

OIDC in Practice

Single Sign-On (SSO):

User logs in once
Multiple applications use same OIDC provider
Seamless experience across apps

Social login:

"Login with Google/Facebook/GitHub"
Uses provider's OIDC implementation
No password management for your app

API Keys

What Are API Keys

API Key: Simple authentication mechanism using static token.

Structure:

API-Key: sk_live_abc123def456ghi789jkl

How it works:

  1. Generate unique key for each client
  2. Client includes key in requests
  3. Server validates key against database
  4. Server authorizes based on key permissions

API Key Formats

Prefixed keys:

pk_live_...  (public key for frontend)
sk_live_... (secret key for backend)
pk_test_... (test environment public)
sk_test_... (test environment secret)

Benefits:

  • Identify key type quickly
  • Prevent mixing test/prod keys
  • Aid in key management

API Key Transmission

Authorization header (preferred):

GET /api/resources
Authorization: Bearer sk_live_abc123...

Custom header:

GET /api/resources
X-API-Key: sk_live_abc123...

Query parameter (avoid):

GET /api/resources?api_key=sk_live_abc123...

Why avoid query parameters:

  • Logged in server logs
  • Visible in browser history
  • Sent to analytics
  • Cached by CDNs

API Key Security

Hashing:

Storage: hash(api_key) → store hash
Validation: hash(provided_key) == stored_hash

Never store plain text keys.

Key rotation:

Support multiple active keys per client
Client rotates keys without downtime:
1. Generate new key
2. Deploy new key to systems
3. Delete old key

Rate limiting:

Per-key rate limits prevent abuse
Track usage per key for quotas

Scopes/Permissions:

Each key has associated permissions
Key 1: read:users write:posts
Key 2: read:users (read-only)

API Keys vs OAuth

API Keys:

  • Simple implementation
  • No expiration (long-lived)
  • Hard to revoke (need key rotation)
  • No fine-grained permissions
  • Good for server-to-server

OAuth:

  • Complex implementation
  • Expiring tokens
  • Easy revocation
  • Fine-grained scopes
  • Good for user delegation

Use API keys for:

  • Internal services
  • Simple integrations
  • Machine-to-machine
  • When OAuth overhead not justified

Use OAuth for:

  • User authentication
  • Third-party integrations
  • Fine-grained permissions
  • Token rotation requirements

Mutual TLS (mTLS)

What Is mTLS

TLS: Server proves identity to client (certificate).

mTLS: Both client and server prove identity (mutual authentication).

Use case: Service-to-service authentication in microservices.

How mTLS Works

TLS handshake with mutual authentication:

1. Client initiates connection to Server
2. Server sends its certificate
3. Client validates Server certificate
4. Server requests Client certificate
5. Client sends its certificate
6. Server validates Client certificate
7. Both parties verify certificates
8. Encrypted connection established

Certificate validation:

- Certificate issued by trusted CA
- Certificate not expired
- Certificate not revoked (CRL/OCSP)
- Common Name matches expected identity

Certificate Management

Certificate Authority (CA):

  • Issues certificates
  • Signs certificates with private key
  • Clients/servers trust CA public key

Self-signed vs CA-signed:

Self-signed:

  • Generate your own CA
  • Issue certificates from your CA
  • Good for internal services
  • No cost

CA-signed:

  • Use public CA (Let's Encrypt, DigiCert)
  • Widely trusted
  • Required for public services
  • May have cost

mTLS in Microservices

Service Mesh pattern:

Istio, Linkerd, Consul Connect:
- Automatic mTLS between services
- Sidecar proxies handle TLS
- Certificate rotation automated
- No application code changes

Manual mTLS:

Each service:
- Has its own certificate
- Validates peer certificates
- Application handles TLS
- More control but more work

Certificate Rotation

Problem: Certificates expire.

Solution: Automated rotation.

Rotation strategy:

1. Generate new certificate before expiry
2. Distribute new certificate to services
3. Services accept both old and new (overlap period)
4. Services switch to using new certificate
5. Old certificate expires

Short-lived certificates:

  • 1-7 days validity
  • Automated rotation required
  • Limits blast radius if compromised
  • Standard in service meshes

mTLS Benefits

Strong authentication:

  • Cryptographic proof of identity
  • Difficult to forge
  • No shared secrets

Encryption:

  • All traffic encrypted
  • Man-in-the-middle prevention
  • Data confidentiality

No application changes:

  • With service mesh
  • Transparent to applications
  • Centralized security policy

mTLS Challenges

Certificate management complexity:

  • Issuing certificates
  • Distribution
  • Rotation
  • Revocation

Performance overhead:

  • TLS handshake latency
  • Encryption/decryption CPU cost
  • Typically negligible with modern hardware

Debugging difficulty:

  • Encrypted traffic harder to inspect
  • Need proper logging and tracing

Service-to-Service Authentication

Authentication Patterns

Shared secrets:

Service A → Service B
Authorization: Bearer shared_secret_abc123

Simple but problematic:

  • Secrets must be shared
  • Rotation is difficult
  • No identity verification

JWT tokens:

Service A:
Generate JWT signed with A's private key
Include service identity in claims

Service B:
Verify JWT with A's public key
Check service identity

mTLS:

Both services have certificates
Mutual verification
Strong authentication

OAuth Client Credentials:

Service A → Auth Server (client credentials flow)
Auth Server → Service A (access token)
Service A → Service B (with access token)
Service B validates token

Service Identity

Service name in token:

{
"iss": "service-a",
"aud": "service-b",
"sub": "service-a",
"exp": 1234567890
}

Certificate CN (Common Name):

CN=service-a.default.svc.cluster.local

Service Account:

Kubernetes Service Account
Automatically mounted in pods
Used for identity

Trust Models

All services trust auth server:

Auth Server issues tokens
Services validate tokens with auth server
Centralized trust

Peer-to-peer trust:

Each service has key pair
Services exchange public keys
Direct trust relationships

CA-based trust:

All services trust common CA
CA issues certificates
Services validate certificates
Hierarchical trust

Best Practices

Defense in Depth

Multiple layers:

  1. Authentication (who are you)
  2. Authorization (what can you do)
  3. Rate limiting (slow down attacks)
  4. Input validation (reject bad data)
  5. Encryption (protect in transit)
  6. Monitoring (detect attacks)

No single point of failure.

Principle of Least Privilege

Grant minimum permissions necessary:

  • Service needs read access → give read, not read+write
  • Service needs one endpoint → scope to that endpoint
  • User needs viewer role → don't give admin

Review permissions regularly.

Token Hygiene

Short lifetimes:

  • Access tokens: 15-60 minutes
  • Refresh tokens: Days to weeks
  • Reduces window if compromised

Secure storage:

  • httpOnly cookies (browser)
  • Encrypted at rest (server)
  • Never in localStorage (XSS vulnerable)

Rotation:

  • Rotate refresh tokens on use
  • Rotate secrets periodically
  • Automate rotation

Monitoring and Alerting

Track authentication metrics:

  • Failed login attempts
  • Token validation failures
  • Unusual access patterns
  • Geographic anomalies

Alert on:

  • Spike in failed authentications
  • Token validation errors
  • Credential stuffing attempts
  • Brute force attacks

Security Headers

Set security headers:

Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'self'

Prevents common attacks.

Testing

Test authentication:

  • Valid credentials succeed
  • Invalid credentials fail
  • Expired tokens rejected
  • Revoked tokens rejected
  • Permission boundaries enforced

Penetration testing:

  • Regular security audits
  • Third-party testing
  • Bug bounty programs

Documentation

Document authentication:

  • Which mechanisms used where
  • How to obtain credentials
  • Token format and claims
  • Expiration policies
  • Rotation procedures

Critical for ops team and users.