Service Authentication

Authentifizierung bereitgestellter Services

DownToZero (DTZ) bietet eine integrierte Authentifizierung für Ihre bereitgestellten Services, wodurch es einfach wird, sichere Anwendungen zu erstellen, ohne eine eigene Authentifizierungsinfrastruktur zu verwalten.

Überblick

Wenn Sie einen Service bei DTZ bereitstellen, stellt die Plattform automatisch Authentifizierungsanmeldeinformationen und Kontextinformationen über Umgebungsvariablen bereit. Ihr Service kann diese verwenden, um:

  • Anfragen an andere DTZ-Services zu authentifizieren (API-Aufrufe, Objektspeicher usw.)
  • Eingehende Benutzeranfragen zu verifizieren
  • Kontextbezogene Ressourcen zuzugreifen
  • Sichere Service-zu-Service-Kommunikation zu implementieren

Umgebungsvariablen

DTZ injiziert automatisch die folgenden Umgebungsvariablen in Ihre Service-Container:

Variable Beschreibung Beispiel
DTZ_ACCESS_TOKEN Ein JWT-Token zum Zugriff auf DTZ-Services innerhalb Ihres Kontexts. eyJhbGciOiJSUzI1NiI...
DTZ_CONTEXT_ID Ihre DTZ-Kontextkennung. context-3cd84429-64a4-4226-b868-c83feeff0f46
PORT Der Port, auf dem Ihr Service lauschen soll. 80

Authentifizierung eingehender Anfragen

Ihr bereitgestellter Service kann eingehende Anfragen mit verschiedenen Methoden authentifizieren:

API Key Authentifizierung

Benutzer können sich mit DTZ-API-Schlüsseln gegenüber Ihrem Service authentifizieren, indem sie den Schlüssel im Header X-API-KEY übergeben.

curl -H "X-API-KEY: your-api-key" https://yourservice.dtz.rocks/api/endpoint

Bearer Token Authentifizierung

Übergeben Sie ein JWT-Token im Authorization-Header zur Authentifizierung.

curl -H "Authorization: Bearer your-jwt-token" https://yourservice.dtz.rocks/api/endpoint

Basic Authentication

Sie können API-Schlüssel auch via Basic Authentication übergeben.

curl -u apikey:your-api-key https://yourservice.dtz.rocks/api/endpoint

Für Webanwendungen kann der DTZ-Identity-Service die Authentifizierung über Browser-Cookies übernehmen.

OAuth Flow

DTZ bietet automatische OAuth-Authentifizierung für Webanwendungen. Wenn nicht authentifizierte Nutzer auf Ihren Service zugreifen, werden sie automatisch zur DTZ-Login-Seite weitergeleitet und nach erfolgreichem Login zurückgeleitet.

Verwendung der DTZ-Authentifizierung in Ihrem Service

Authentifizierte Anfragen an DTZ-Services stellen

Verwenden Sie die Umgebungsvariable DTZ_ACCESS_TOKEN, um authentifizierte Aufrufe an andere DTZ-Services zu tätigen.

import os
import requests

# Token aus der Umgebung holen
token = os.environ.get('DTZ_ACCESS_TOKEN')
context_id = os.environ.get('DTZ_CONTEXT_ID')

# Authentifizierte Anfrage an die DTZ-API
headers = {
    'Authorization': f'Bearer {token}',
    'Content-Type': 'application/json'
}

response = requests.get(
    'https://api.dtz.rocks/v1/containers/services',
    headers=headers
)
// Node.js Beispiel
const token = process.env.DTZ_ACCESS_TOKEN;
const contextId = process.env.DTZ_CONTEXT_ID;

const response = await fetch('https://api.dtz.rocks/v1/containers/services', {
    headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
    }
});
# Bash Beispiel
curl -H "Authorization: Bearer $DTZ_ACCESS_TOKEN" \
     -H "Content-Type: application/json" \
     https://api.dtz.rocks/v1/containers/services

Verifizierung der eingehenden Authentifizierung

DTZ übernimmt automatisch die Authentifizierung für eingehende Anfragen. Wenn ein Benutzer eine authentifizierte Anfrage an Ihren Service stellt, führt DTZ folgende Schritte aus:

  1. Validiert die Authentifizierungsdaten.
  2. Konvertiert API-Schlüssel in JWT-Tokens.
  3. Leitet die Anfrage mit einem Authorization: Bearer <token>-Header weiter.

Ihr Service erhält das JWT-Token und kann daraus Benutzerinformationen extrahieren.

import jwt
import os
from flask import Flask, request

app = Flask(__name__)

@app.route('/protected')
def protected_endpoint():
    auth_header = request.headers.get('Authorization')
    if not auth_header or not auth_header.startswith('Bearer '):
        return {'error': 'No authentication provided'}, 401
    
    token = auth_header.split(' ')[1]
    
    try:
        # Das Token wurde bereits von DTZ überprüft,
        # daher kann die Payload ohne Signaturprüfung ausgelesen werden.
        payload = jwt.decode(token, options={"verify_signature": False})
        
        user_id = payload.get('sub')  # Identitäts-ID
        context_id = payload.get('scope')  # Kontext-ID
        roles = payload.get('roles', [])  # Benutzerrollen
        
        return {
            'user_id': user_id,
            'context_id': context_id,
            'roles': roles,
            'message': 'Access granted'
        }
    except jwt.InvalidTokenError:
        return {'error': 'Invalid token'}, 401
// Express.js Beispiel
const express = require('express');
const jwt = require('jsonwebtoken');

const app = express();

app.get('/protected', (req, res) => {
    const authHeader = req.headers.authorization;
    
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
        return res.status(401).json({ error: 'No authentication provided' });
    }
    
    const token = authHeader.split(' ')[1];
    
    try {
        // Das Token wurde bereits von DTZ überprüft.
        const payload = jwt.decode(token);
        
        const userId = payload.sub;      // Identitäts-ID
        const contextId = payload.scope; // Kontext-ID
        const roles = payload.roles || [];    // Benutzerrollen
        
        res.json({
            user_id: userId,
            context_id: contextId,
            roles: roles,
            message: 'Access granted'
        });
    } catch (error) {
        res.status(401).json({ error: 'Invalid token' });
    }
});

JWT-Token-Struktur

DTZ JWT-Tokens enthalten folgende Claims:

Claim Beschreibung Beispiel
iss Aussteller (immer “dtz.rocks”) "dtz.rocks"
sub Subjekt (Benutzer-Identitäts-ID) "identity-abc123..."
aud Audience (immer “dtz.rocks”) "dtz.rocks"
scope Kontext-ID "context-3cd84429..."
roles Benutzerrollen/Berechtigungen ["https://dtz.rocks/context/admin/{context_id}"]
contexts Verfügbare Kontexte ["context-3cd84429..."]
exp Ablaufzeit 1640995200
iat Ausgabezeitpunkt 1640908800

Rollenbasierte Zugriffskontrolle

DTZ verwendet rollenbasierte Zugriffskontrolle mit URI-basierten Rollenbezeichnern. Häufige Rollenmuster sind:

  • https://dtz.rocks/context/admin/{context_id} - Kontext-Administrator
  • https://dtz.rocks/containers/admin/{context_id} - Container-Service-Administrator
  • https://dtz.rocks/objectstore/admin/{context_id} - Objektstore-Administrator

Sie können in Ihrem Service Rollen prüfen:

def check_role(token, required_role_pattern):
    payload = jwt.decode(token, options={"verify_signature": False})
    roles = payload.get('roles', [])
    context_id = payload.get('scope')
    
    required_role = required_role_pattern.replace('{context_id}', context_id)
    return required_role in roles

# Beispiel:
if check_role(token, 'https://dtz.rocks/containers/admin/{context_id}'):
    # Benutzer hat Container-Admin-Berechtigungen
    pass

Best Practices

Sicherheit

  • Validieren Sie stets, dass JWT-Tokens die erwarteten Claims enthalten.
  • Prüfen Sie Benutzerrollen, bevor Sie Zugriff auf sensible Operationen gewähren.
  • Verwenden Sie HTTPS für alle Kommunikation.
  • Protokollieren Sie keine sensiblen Authentifizierungs-Tokens.

Fehlerbehandlung

  • Geben Sie passende HTTP-Statuscodes zurück (z. B. 401 für nicht autorisiert, 403 für verboten).
  • Bieten Sie aussagekräftige Fehlermeldungen ohne Offenlegung sensibler Informationen.

Performance

  • Cachen Sie Ergebnisse der JWT-Token-Verifizierung, wenn möglich.
  • Verwenden Sie Connection-Pooling für DTZ API-Anfragen.
  • Erwägen Sie eine Ratenbegrenzung für Anfragen.

Beispiel: Vollständiger authentifizierter Service

Hier ein vollständiges Beispiel eines Python-Flask-Services mit DTZ-Authentifizierung:

import os
import jwt
import requests
from flask import Flask, request, jsonify

app = Flask(__name__)

DTZ_TOKEN = os.environ.get('DTZ_ACCESS_TOKEN')
DTZ_CONTEXT_ID = os.environ.get('DTZ_CONTEXT_ID')

def get_user_from_token(token):
    """Extrahiert Benutzerinformationen aus einem DTZ JWT-Token."""
    try:
        payload = jwt.decode(token, options={"verify_signature": False})
        return {
            'user_id': payload.get('sub'),
            'context_id': payload.get('scope'),
            'roles': payload.get('roles', [])
        }
    except jwt.InvalidTokenError:
        return None

def require_auth(f):
    """Ein Decorator, der Authentifizierung erzwingt."""
    def decorated(*args, **kwargs):
        auth_header = request.headers.get('Authorization')
        if not auth_header or not auth_header.startswith('Bearer '):
            return jsonify({'error': 'Authentication required'}), 401
        
        token = auth_header.split(' ')[1]
        user = get_user_from_token(token)
        
        if not user:
            return jsonify({'error': 'Invalid token'}), 401
        
        request.user = user
        return f(*args, **kwargs)
    
    decorated.__name__ = f.__name__
    return decorated

@app.route('/health')
def health():
    """Eine öffentliche Health-Check-Route."""
    return jsonify({'status': 'healthy'})

@app.route('/profile')
@require_auth
def profile():
    """Ein geschützter Endpunkt, der das Nutzerprofil zurückgibt."""
    return jsonify({
        'user_id': request.user['user_id'],
        'context_id': request.user['context_id'],
        'roles': request.user['roles']
    })

@app.route('/admin/users')
@require_auth
def admin_users():
    """Ein Endpunkt nur für Administratoren."""
    required_role = f'https://dtz.rocks/context/admin/{DTZ_CONTEXT_ID}'
    
    if required_role not in request.user['roles']:
        return jsonify({'error': 'Admin access required'}), 403
    
    # Authentifizierte Anfrage an die DTZ-API
    headers = {'Authorization': f'Bearer {DTZ_TOKEN}'}
    response = requests.get(
        'https://identity.dtz.rocks/api/2021-02-21/users',
        headers=headers
    )
    
    return jsonify(response.json())

if __name__ == '__main__':
    port = int(os.environ.get('PORT', 80))
    app.run(host='0.0.0.0', port=port)

Fehlerbehebung

Häufige Probleme

  • Authentifizierungs-Token nicht gefunden: Stellen Sie sicher, dass Ihr Service über den DTZ-Container-Service bereitgestellt wurde und die Umgebungsvariablen korrekt ausgelesen werden.
  • Ungültige Token-Fehler: Prüfen Sie, ob das Token richtig aus dem Authorization-Header extrahiert wird und die JWT-Parsing-Logik funktioniert.
  • 403 Forbidden Fehler: Vergewissern Sie sich, dass der Benutzer die erforderlichen Rollen im JWT-Token hat.
  • Service-zu-Service-Authentifizierung schlägt fehl: Verwenden Sie die Umgebungsvariable DTZ_ACCESS_TOKEN für ausgehende Anfragen.

Authentifizierung testen

Sie können die Authentifizierung Ihres Services mit curl testen:

# Test mit API-Schlüssel
curl -H "X-API-KEY: your-api-key" https://yourservice.dtz.rocks/profile

# Test mit Bearer-Token  
curl -H "Authorization: Bearer your-jwt-token" https://yourservice.dtz.rocks/profile

# Test ohne Authentifizierung (sollte 401 zurückgeben)
curl https://yourservice.dtz.rocks/profile

Für detailliertere Informationen zur DTZ-Authentifizierung lesen Sie die Authentifizierungsdokumentation.