TS
Thomas Schmitz

Freiberuflicher IT-Berater, Softwareentwickler & DevOps. Praxisnahe Artikel zu API-Design, Architektur und Cloud-Workflows.

Serie: API-Design · Teil 9 von 22

API-Design Teil 9: Authentifizierung (AuthN)

OAuth2, API Keys oder mTLS? JWT oder opaque Tokens? Wie du das richtige Auth-Modell wählst und sicher implementierst.

Praxisnah Checkliste

Authentifizierung beantwortet eine Frage: Wer bist du? Nicht was du darfst – das ist Autorisierung. Aber ohne zuverlässige Identität ist jede Berechtigungsprüfung wertlos.

Die Wahl des Auth-Modells beeinflusst Security, Developer Experience und Betriebsaufwand. Dieser Artikel hilft dir, zwischen OAuth2, API Keys und mTLS zu entscheiden – und erklärt, warum JWT nicht immer die richtige Wahl ist.

Zielbild

Nach diesem Artikel kannst du:

  • Zwischen OAuth2/OIDC, API Keys und mTLS begründet wählen
  • JWT vs. opaque Tokens für deinen Use Case bewerten
  • Token Lifetimes und Rotation sinnvoll konfigurieren
  • CORS und CSRF für Browser-Clients korrekt einrichten
  • Ein AuthN-Modell dokumentieren, das Security und DX balanciert

Kernfragen

Bevor du weiterliest, versuche diese Fragen für dein Projekt zu beantworten:

  1. Wer sind die Clients? Browser, Mobile Apps, Server, Partner?
  2. OAuth2 oder API Keys? Oder beides für verschiedene Use Cases?
  3. JWT oder opaque Tokens? Was sind die Trade-offs?
  4. Wie lange leben Tokens? Access Token? Refresh Token?
  5. Wie rotiert ihr Keys? JWKS? Manuelle Rotation?

Auth-Modelle im Überblick

Drei Hauptansätze haben sich etabliert, jeder mit eigenen Stärken.

OAuth2 / OpenID Connect

OAuth2 ist ein Autorisierungs-Framework, OIDC erweitert es um Authentifizierung.

APIAuth ServerClientAPIAuth ServerClient1. Login2. Token3. API Request + Token4. Response

Flows:

Flow Use Case
Authorization Code + PKCE Browser, Mobile Apps (User-Login)
Client Credentials Server-to-Server (Machine-to-Machine)
Device Code Smart TVs, CLIs ohne Browser
Refresh Token Grant (RFC) Lange Sessions ohne Re-Login

Vorteile:

  • Standardisiert, gut dokumentiert
  • Delegierte Authentifizierung (User loggt sich beim IdP ein)
  • Scopes für granulare Berechtigungen
  • Token Revocation möglich

Nachteile:

  • Komplexer zu implementieren
  • Abhängigkeit vom Auth Server
  • Mehr Roundtrips bei Token-Refresh

Wann verwenden:

  • User-facing APIs (Browser, Mobile)
  • Multi-Tenant-Systeme
  • Wenn delegierte Auth nötig ist (Social Login, SSO)

API Keys

Einfache, langlebige Credentials für Server-to-Server-Kommunikation.

GET /v1/orders HTTP/1.1
Authorization: Bearer sk_live_abc123xyz

Oder als Header:

X-API-Key: sk_live_abc123xyz

Vorteile:

  • Einfach zu implementieren und zu nutzen
  • Kein Token-Refresh nötig
  • Gut für Server-to-Server

Nachteile:

  • Kein eingebauter Ablauf (manuelles Rotieren)
  • Schwer zu revoken ohne Client-Änderung
  • Keine User-Identität (nur Client/Tenant)

Wann verwenden:

  • Server-to-Server-Integration
  • Partner-APIs
  • Einfache Webhooks
  • Wenn OAuth2 Overkill ist

mTLS (Mutual TLS)

Beide Seiten authentifizieren sich mit Zertifikaten.

APIClientAPIClientbeide prüfen ZertifikateTLS Handshake

Vorteile:

  • Sehr sicher (Zertifikate statt Secrets)
  • Keine Credentials im Request
  • Gut für Zero-Trust-Architekturen

Nachteile:

  • Komplex zu betreiben (PKI, Zertifikatsmanagement)
  • Schwieriger für externe Partner
  • Nicht für Browser geeignet

Wann verwenden:

  • Interne Service-to-Service-Kommunikation
  • Hochsichere Umgebungen
  • Wenn PKI bereits existiert

Entscheidungsmatrix

Kriterium OAuth2/OIDC API Keys mTLS
Browser-Clients Ja Nein Nein
Mobile Apps Ja Eingeschränkt Nein
Server-to-Server Ja Ja Ja
User-Identität Ja Nein Nein
Komplexität Hoch Niedrig Hoch
Betriebsaufwand Mittel Niedrig Hoch

Empfehlung: OAuth2 für User-facing, API Keys für einfache Server-Integration, mTLS für interne Services.

JWT vs. Opaque Tokens

Die Wahl zwischen JWT und opaque Tokens ist eine der häufigsten Fragen.

JWT (JSON Web Token)

Self-contained Token mit Payload:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiJ1c2VyXzEyMyIsInNjb3BlIjoicmVhZCB3cml0ZSIsImV4cCI6MTcwNjUyMjQwMH0.
signature

Decoded:

{
  "sub": "user_123",
  "iss": "https://auth.example.com",
  "aud": "https://api.example.com",
  "scope": "read write",
  "exp": 1706522400,
  "iat": 1706518800
}

Vorteile:

  • Stateless: API muss Auth Server nicht fragen
  • Payload enthält Claims (User-ID, Scopes)
  • Skaliert gut (kein zentraler Token-Store)

Nachteile:

  • Nicht sofort revozierbar (läuft erst ab)
  • Größer als opaque Tokens
  • Payload ist lesbar (Base64, nicht verschlüsselt)

Opaque Tokens

Zufälliger String ohne Bedeutung:

at_7f3b8c9d2e1a4f5b6c7d8e9f0a1b2c3d

API muss den Token serverseitig prüfen (Introspection oder Token-Store):

POST /oauth/introspect
Content-Type: application/x-www-form-urlencoded

token=at_7f3b8c9d2e1a4f5b6c7d8e9f0a1b2c3d

Vorteile:

  • Sofort revozierbar
  • Kleiner
  • Keine sensitiven Daten im Token

Nachteile:

  • Stateful: jeder Request braucht serverseitige Prüfung
  • Auth Server wird Bottleneck
  • Mehr Latenz

Wann was verwenden?

Szenario Empfehlung
Hohe Last, Skalierung kritisch JWT
Sofortige Revocation nötig Opaque
Sensitive Claims (PII) Opaque
Microservices, viele APIs JWT
Single API, einfacher Betrieb Opaque
Kurze Token-Lifetime (< 15 min) JWT
Lange Token-Lifetime Opaque

Hybrid-Ansatz: Kurze JWTs (5-15 min) + Refresh Tokens (opaque). So bekommst du Stateless-Vorteile mit kontrollierbarer Revocation.

Token Lifetimes

Token-Lifetimes balancieren Security (kurz) und UX (lang).

Access Tokens

Kontext Empfohlene Lifetime
Browser (SPA) 5-15 Minuten
Mobile App 15-60 Minuten
Server-to-Server 1-24 Stunden
Hochsicher 5 Minuten

Warum kurz?

  • Kompromittierte Tokens sind nur kurz gültig
  • Erzwingt regelmäßige Re-Validierung
  • Begrenzt Schaden bei Token-Leak

Refresh Tokens

Kontext Empfohlene Lifetime
Browser 1-7 Tage
Mobile App 30-90 Tage
Server Nicht nötig (Client Credentials)

Refresh Token Rotation:

Bei jedem Refresh ein neues Refresh Token ausgeben:

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=rt_old123

Response:

{
  "access_token": "at_new456",
  "refresh_token": "rt_new789",
  "expires_in": 900
}

Warum Rotation?

  • Kompromittiertes Refresh Token wird bei nächster Nutzung ungültig
  • Erkennung von Token-Diebstahl (zwei Clients mit gleichem Token)

Absolute vs. Sliding Expiration

Absolute: Token läuft nach fester Zeit ab, egal wie aktiv der User ist.

Sliding: Lifetime verlängert sich bei Aktivität.

Empfehlung: Absolute Expiration für Access Tokens, Sliding für Sessions.

Key Management und Rotation

Signing Keys für JWTs müssen sicher verwaltet und regelmäßig rotiert werden.

JWKS (JSON Web Key Set)

Öffentliche Keys werden über einen Endpoint bereitgestellt:

GET /.well-known/jwks.json

Response:

{
  "keys": [
    {
      "kty": "RSA",
      "kid": "key_2024_01",
      "use": "sig",
      "alg": "RS256",
      "n": "0vx7agoebG...",
      "e": "AQAB"
    },
    {
      "kty": "RSA",
      "kid": "key_2023_12",
      "use": "sig",
      "alg": "RS256",
      "n": "abc123...",
      "e": "AQAB"
    }
  ]
}

Key Rotation

  1. Neuen Key generieren und zu JWKS hinzufügen
  2. Neue Tokens mit neuem Key signieren (kid im Header)
  3. Grace Period (z.B. 24h): beide Keys aktiv
  4. Alten Key entfernen nach Grace Period
{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "key_2024_01"
}

Regel: API validiert gegen alle Keys im JWKS, nicht nur den neuesten.

Rotation Schedule

Key-Typ Rotation
Signing Keys (JWT) 90 Tage
API Keys Bei Kompromittierung
TLS-Zertifikate 90 Tage (Let's Encrypt)

CORS für Browser-Clients

Cross-Origin Resource Sharing ist kritisch für SPAs.

Minimale CORS-Policy

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400

CORS-Regeln

Regel Erklärung
Keine Wildcards mit Credentials * + credentials: include funktioniert nicht
Origin-Whitelist Explizite Liste erlaubter Origins
Preflight cachen Access-Control-Max-Age reduziert OPTIONS-Requests
Credentials explizit Access-Control-Allow-Credentials: true wenn nötig

Preflight-Requests

Browser senden OPTIONS vor komplexen Requests:

OPTIONS /v1/orders HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization, Content-Type

Response:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400

CSRF-Schutz

Cross-Site Request Forgery ist relevant, wenn Cookies verwendet werden.

Wann ist CSRF ein Risiko?

Auth-Methode CSRF-Risiko
Bearer Token (Header) Nein
Cookie-basiert Ja
API Key (Header) Nein

Warum? Browser senden Cookies automatisch. Header nicht.

CSRF-Schutz-Strategien

1. SameSite Cookies:

Set-Cookie: session=abc123; SameSite=Strict; Secure; HttpOnly
SameSite Verhalten
Strict Nie cross-site gesendet
Lax Nur bei Top-Level-Navigation
None Immer (erfordert Secure)

2. CSRF-Tokens:

<input type="hidden" name="_csrf" value="token123">

Server prüft Token bei POST/PUT/DELETE.

3. Double-Submit Cookie:

Cookie + Header müssen übereinstimmen:

Cookie: csrf=abc123
X-CSRF-Token: abc123

Empfehlung: Bearer Tokens im Authorization-Header statt Cookies. Dann ist CSRF in der Praxis mitigiert, solange Tokens nicht als Cookies gespeichert werden (XSS bleibt ein separates Risiko).

Anonymous Access

Manchmal brauchen APIs öffentliche Endpoints ohne Auth.

Regeln für Anonymous Access

  1. Explizit markieren: Dokumentiere, welche Endpoints anonym sind
  2. Minimieren: So wenig wie möglich
  3. Rate Limiting: Aggressiver als für authentifizierte Requests
  4. Keine sensitiven Daten: Nur öffentliche Informationen

Beispiele

Endpoint Anonymous Begründung
GET /health Ja Monitoring
GET /v1/products Ja Öffentlicher Katalog
POST /v1/auth/login Ja Login-Flow
GET /v1/users/me Nein User-Daten
POST /v1/orders Nein Geschäftsaktion

Regeln & Anti-Patterns

Do

  • OAuth2 für User-facing APIs, API Keys für Server-to-Server
  • Kurze Access Token Lifetimes (5-15 min für Browser)
  • Refresh Token Rotation aktivieren
  • JWKS für Key Distribution, regelmäßige Rotation
  • CORS-Whitelist statt Wildcards
  • Bearer Tokens statt Cookies (vermeidet CSRF)
  • Anonymous Access minimieren und explizit dokumentieren

Don't

  • Langlebige JWTs ohne Refresh-Mechanismus
  • API Keys in Browser-Clients
  • Access-Control-Allow-Origin: * mit Credentials
  • CSRF-Schutz vergessen bei Cookie-Auth
  • Signing Keys nie rotieren
  • Sensitive Claims in JWT-Payload ohne Verschlüsselung
  • Passwords im Token (auch nicht gehasht)

Artefakt: AuthN-ADR

# ADR: Authentifizierungsmodell

## Status

Accepted

## Kontext

Die API wird von verschiedenen Clients genutzt:

- Browser (SPA)
- Mobile Apps
- Partner-Server

Wir brauchen ein Auth-Modell, das Security, DX und Betriebsaufwand balanciert.

## Entscheidung

### Auth-Methoden

| Client-Typ       | Auth-Methode                     |
|------------------|----------------------------------|
| Browser (SPA)    | OAuth2 Authorization Code + PKCE |
| Mobile App       | OAuth2 Authorization Code + PKCE |
| Partner-Server   | API Keys                         |
| Interne Services | mTLS (optional)                  |

### Token-Typ

- **Access Tokens:** JWT (RS256)
- **Refresh Tokens:** Opaque

### Token Lifetimes

| Token                  | Lifetime                |
|------------------------|-------------------------|
| Access Token (Browser) | 15 Minuten              |
| Access Token (Mobile)  | 30 Minuten              |
| Access Token (Server)  | 1 Stunde                |
| Refresh Token          | 7 Tage (Rotation aktiv) |

### JWT Claims

```json
{
  "iss": "https://auth.example.com",
  "aud": "https://api.example.com",
  "sub": "user_abc123",
  "tenant_id": "tenant_xyz",
  "scope": "read:orders write:orders",
  "exp": 1706522400,
  "iat": 1706521500
}
```

### Key Rotation

- Algorithmus: RS256
- JWKS Endpoint: `/.well-known/jwks.json`
- Rotation: alle 90 Tage
- Grace Period: 24 Stunden

### API Keys

- Format: `sk_live_` + 32 Zeichen
- Scopes: pro Key konfigurierbar
- Rotation: manuell, bei Kompromittierung

### CORS

Erlaubte Origins:

- `https://app.example.com`
- `https://admin.example.com`

### Anonymous Endpoints

- `GET /health`
- `GET /v1/products`
- `POST /v1/auth/login`
- `POST /v1/auth/token`

## Konsequenzen

### Positiv

- Standardisiertes Auth-Modell
- Kurze Token-Lifetimes begrenzen Schaden
- Refresh Rotation erkennt Token-Diebstahl

### Negativ

- OAuth2-Komplexität für simple Use Cases
- JWKS-Caching nötig für Performance

## Flow-Diagramm

### Browser/Mobile (Authorization Code + PKCE)

```mermaid
sequenceDiagram
  participant User
  participant App
  participant AuthServer as Auth Server
  participant API

  User->>App: Login starten
  App->>AuthServer: Authorization Request (PKCE)
  AuthServer-->>User: Login
  User-->>AuthServer: Credentials
  AuthServer-->>App: Authorization Code
  App->>AuthServer: Token Exchange (Code → Access + Refresh)
  AuthServer-->>App: Access + Refresh Token
  App->>API: API Request + Access Token

  alt Token abgelaufen
    App->>AuthServer: Refresh Token
    AuthServer-->>App: Neuer Access Token
    App->>API: API Request + Access Token
  end
```

### Partner-Server (API Key)

```mermaid
sequenceDiagram
  participant Server
  participant API

  Server->>API: API Request + API Key
  API->>API: Validate Key
  API-->>Server: Response
```

## Checkliste

Bevor du zum nächsten Artikel gehst, prüfe:

- [ ] Auth-Modell ist gewählt (OAuth2, API Keys, mTLS)
- [ ] OAuth2-Flows sind dokumentiert
- [ ] Token-Typ ist entschieden (JWT vs. opaque)
- [ ] Token Lifetimes sind definiert
- [ ] Refresh Token Rotation ist aktiviert
- [ ] JWKS-Endpoint existiert und Keys rotieren
- [ ] CORS ist konfiguriert (keine Wildcards mit Credentials)
- [ ] CSRF-Schutz ist implementiert (falls Cookies)
- [ ] Anonymous Endpoints sind explizit dokumentiert
- [ ] AuthN-ADR ist geschrieben

## Wie es weitergeht

Im nächsten Teil geht es um Autorisierung: RBAC vs. ABAC, wie du BOLA
verhinderst, und warum Least Privilege schwieriger ist als gedacht.

Alle Teile der Serie: [Serie: API-Design](/serien/api-design/)

Mehr Beiträge aus dem Blog.