Skip to content
IRC-Coding IRC-Coding
OAuth 2.0 Authorization Code Flow Access Token Refresh Token API Security

OAuth 2.0: Authorization Code, Flows & Access Token

OAuth 2.0 ist das Standard-Autorisierungsframework. Authorization Code Flow, Client Credentials, Access/Refresh Token mit Implementierungsbeispielen.

S

schutzgeist

2 min read

OAuth 2.0: Authorization Code, Flows & Access Token

Dieser Beitrag ist eine Begriffserklärung zum OAuth 2.0 Autorisierungsframework – inklusive Flows, Token und praktischer Implementierung.

In a Nutshell

OAuth 2.0 ist ein standardisiertes Autorisierungsframework, das es Drittanbietern erlaubt, auf Benutzerressourcen zuzugreifen, ohne deren Zugangsdaten zu kennen.

Kompakte Fachbeschreibung

OAuth 2.0 ist ein weitverbreitetes Autorisierungsprotokoll für Web-, Desktop- und Mobile-Anwendungen. Es ermöglicht einer Anwendung (Client), im Namen eines Benutzers auf Ressourcen eines anderen Systems (Ressourcenserver) zuzugreifen.

Kerneigenschaften:

  • Delegation: Benutzer delegieren Zugriffsrechte an Anwendungen
  • Token-basiert: Access Tokens statt Passwörter
  • Rollenbasiert: Klare Trennung der beteiligten Akteure
  • Standardisiert: RFC 6749 definiert das Framework

Beteiligte Rollen:

  • Resource Owner: Benutzer, der die Ressourcen besitzt
  • Client: Anwendung, die auf Ressourcen zugreifen möchte
  • Authorization Server: Stellt Tokens aus und validiert sie
  • Resource Server: Stellt die geschützten Ressourcen bereit

OAuth 2.0 überträgt keine Benutzeridentität, sondern autorisiert lediglich Zugriffe auf Ressourcen. Für Identitätsüberprüfung gibt es OpenID Connect.

Prüfungsrelevante Stichpunkte

  • Autorisierungsframework, nicht Authentifizierung
  • Rollen: Resource Owner, Client, Authorization Server, Resource Server
  • Access Token und optional Refresh Token
  • Authorization Code Flow für Web-Anwendungen
  • Client Credentials Flow für Server-zu-Server-Kommunikation
  • HTTP-basiert und unterstützt REST APIs (IHK-relevant)
  • Token-basierter Zugriff erhöht Sicherheit
  • Schutz vor Passwortweitergabe durch Delegationsmechanismus

Kernkomponenten

  1. Resource Owner: Benutzer oder System, das die Ressourcen besitzt
  2. Client: Anwendung, die Zugriff auf Ressourcen benötigt
  3. Authorization Server: Stellt Tokens aus und verifiziert sie
  4. Resource Server: Hostet die geschützten Ressourcen
  5. Access Token: Kurzlebiges Token für Ressourcenzugriff
  6. Refresh Token: Langlebiges Token für Token-Erneuerung
  7. Authorization Code: Temporärer Code für Token-Austausch
  8. Redirect URI: Ziel-URL nach Autorisierung
  9. Scope: Definiert den Zugriffsbereich
  10. Grant Type: Bestimmt den OAuth 2.0 Flow

OAuth 2.0 Flows

1. Authorization Code Flow (für Web-Apps)

1. Client → User: Weiterleitung zu Authorization Server
2. User → Authorization Server: Login & Zustimmung
3. Authorization Server → Client: Authorization Code
4. Client → Authorization Server: Code gegen Access Token einlösen
5. Authorization Server → Client: Access Token + Refresh Token
6. Client → Resource Server: Ressourcen mit Access Token anfordern

2. Client Credentials Flow (für Server-zu-Server)

1. Client → Authorization Server: Client Credentials senden
2. Authorization Server → Client: Access Token
3. Client → Resource Server: Ressourcen mit Access Token anfordern

3. Implicit Flow (veraltet, für Single-Page-Apps)

1. Client → User: Weiterleitung zu Authorization Server
2. User → Authorization Server: Login & Zustimmung
3. Authorization Server → Client: Access Token direkt im Fragment

Praxisbeispiele

Authorization Code Flow Implementation

// Frontend: Autorisierung anfordern
const authUrl = 'https://auth.example.com/authorize';
const params = new URLSearchParams({
  response_type: 'code',
  client_id: 'your_client_id',
  redirect_uri: 'https://yourapp.com/callback',
  scope: 'read:profile write:data',
  state: generateRandomString() // CSRF-Schutz
});

window.location.href = `${authUrl}?${params}`;

// Callback-Handler
async function handleCallback(code, state) {
  // State validieren
  if (state !== getStoredState()) {
    throw new Error('Invalid state - CSRF attack detected');
  }
  
  // Code gegen Token einlösen
  const tokenResponse = await fetch('https://auth.example.com/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      code: code,
      client_id: 'your_client_id',
      client_secret: 'your_client_secret',
      redirect_uri: 'https://yourapp.com/callback'
    })
  });
  
  const tokens = await tokenResponse.json();
  localStorage.setItem('access_token', tokens.access_token);
  localStorage.setItem('refresh_token', tokens.refresh_token);
}

Backend: Token-Validierung

// Spring Boot Example
@RestController
@RequestMapping("/api")
public class ApiController {
    
    @GetMapping("/profile")
    public ResponseEntity<UserProfile> getProfile(
            @RequestHeader("Authorization") String authHeader) {
        
        // Token extrahieren und validieren
        String token = authHeader.replace("Bearer ", "");
        
        if (!tokenValidator.isValid(token)) {
            return ResponseEntity.status(401).build();
        }
        
        // Token-Claims extrahieren
        Claims claims = tokenValidator.getClaims(token);
        String userId = claims.getSubject();
        List<String> scopes = claims.get("scope", List.class);
        
        // Scope-Prüfung
        if (!scopes.contains("read:profile")) {
            return ResponseEntity.status(403).build();
        }
        
        UserProfile profile = userService.getProfile(userId);
        return ResponseEntity.ok(profile);
    }
    
    @PostMapping("/data")
    public ResponseEntity<?> createData(
            @RequestHeader("Authorization") String authHeader,
            @RequestBody DataRequest request) {
        
        String token = authHeader.replace("Bearer ", "");
        
        if (!tokenValidator.isValid(token)) {
            return ResponseEntity.status(401).build();
        }
        
        Claims claims = tokenValidator.getClaims(token);
        List<String> scopes = claims.get("scope", List.class);
        
        // Schreib-Berechtigung prüfen
        if (!scopes.contains("write:data")) {
            return ResponseEntity.status(403).build();
        }
        
        Data data = dataService.create(request, claims.getSubject());
        return ResponseEntity.ok(data);
    }
}

Client Credentials Flow

# Python Beispiel für Server-zu-Server-Kommunikation
import requests

def get_access_token():
    token_url = "https://auth.example.com/token"
    
    data = {
        'grant_type': 'client_credentials',
        'client_id': 'your_client_id',
        'client_secret': 'your_client_secret',
        'scope': 'api:read api:write'
    }
    
    response = requests.post(token_url, data=data)
    response.raise_for_status()
    
    token_data = response.json()
    return token_data['access_token']

def api_call():
    access_token = get_access_token()
    
    headers = {
        'Authorization': f'Bearer {access_token}',
        'Content-Type': 'application/json'
    }
    
    response = requests.get(
        'https://api.example.com/data',
        headers=headers
    )
    
    return response.json()

Refresh Token Flow

// Token-Erneuerung mit Refresh Token
async function refreshAccessToken() {
  const refreshToken = localStorage.getItem('refresh_token');
  
  if (!refreshToken) {
    throw new Error('No refresh token available');
  }
  
  try {
    const response = await fetch('https://auth.example.com/token', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: new URLSearchParams({
        grant_type: 'refresh_token',
        refresh_token: refreshToken,
        client_id: 'your_client_id',
        client_secret: 'your_client_secret'
      })
    });
    
    if (!response.ok) {
      throw new Error('Token refresh failed');
    }
    
    const tokens = await response.json();
    localStorage.setItem('access_token', tokens.access_token);
    
    if (tokens.refresh_token) {
      localStorage.setItem('refresh_token', tokens.refresh_token);
    }
    
    return tokens.access_token;
  } catch (error) {
    // Refresh Token ungültig - erneute Anmeldung erforderlich
    localStorage.removeItem('access_token');
    localStorage.removeItem('refresh_token');
    redirectToLogin();
  }
}

Sicherheitshinweise

State Parameter (CSRF-Schutz)

function generateState() {
  return Math.random().toString(36).substring(2, 15) +
         Math.random().toString(36).substring(2, 15);
}

function storeState(state) {
  sessionStorage.setItem('oauth_state', state);
}

function validateState(receivedState) {
  const storedState = sessionStorage.getItem('oauth_state');
  sessionStorage.removeItem('oauth_state');
  return storedState === receivedState;
}

PKCE (Proof Key for Code Exchange)

// PKCE für mobile/single-page apps
function generatePKCE() {
  const codeVerifier = generateRandomString(128);
  const codeChallenge = base64urlEncode(sha256(codeVerifier));
  
  return { codeVerifier, codeChallenge };
}

// Authorization Request mit PKCE
const params = new URLSearchParams({
  response_type: 'code',
  client_id: 'your_client_id',
  code_challenge: pkce.codeChallenge,
  code_challenge_method: 'S256',
  redirect_uri: 'https://yourapp.com/callback'
});

Vorteile und Nachteile

Vorteile

  • Sicherheit: Keine Passweitergabe an Drittanbieter
  • Standardisierung: Weit verbreitet und gut dokumentiert
  • Flexibilität: Verschiedene Flows für unterschiedliche Anwendungsfälle
  • Skalierbarkeit: Zentrale Autorisierung für viele Services
  • Revokation: Tokens können jederzeit widerrufen werden

Nachteile

  • Komplexität: Mehr Schritte als einfache Authentifizierung
  • Performance: Zusätzliche Netzwerkaufrufe für Token-Erneuerung
  • State Management: Client muss Token-Status verwalten
  • Security Risks: Falsche Implementierung kann Sicherheitslücken schaffen

Häufige Prüfungsfragen

  1. Was ist der Unterschied zwischen OAuth 2.0 und OpenID Connect? OAuth 2.0 ist für Autorisierung, OpenID Connect fügt Identität (Authentication) hinzu.

  2. Erklären Sie den Authorization Code Flow! Client leitet User zu Authorization Server, erhält Code, tauscht Code gegen Token ein.

  3. Wann verwendet man Client Credentials Flow? Für Server-zu-Server-Kommunikation ohne Benutzerinteraktion.

  4. Was ist der Zweck des State Parameters? CSRF-Schutz durch Verifizierung, dass die Anfrage vom Client initiiert wurde.

Wichtigste Quellen

  1. https://tools.ietf.org/html/rfc6749
  2. https://oauth.net/2/
  3. https://auth0.com/docs/authorization-framework/overview
Zurück zum Blog
Share:

Ähnliche Beiträge