Service-Authentifizierung

Authentifizierung bereitgestellter Dienste

DownToZero (DTZ) bietet integrierte Authentifizierung für Ihre bereitgestellten Dienste und macht es einfach, sichere Anwendungen zu bauen, ohne eine eigene Authentifizierungsinfrastruktur zu verwalten.

Übersicht

Wenn Sie einen Dienst bei DTZ bereitstellen, stellt die Plattform automatisch Authentifizierungsdaten und Kontextinformationen über Umgebungsvariablen zur Verfügung. Ihr Dienst kann diese verwenden, um:

  • Anfragen an andere DTZ-Dienste zu authentifizieren (API-Aufrufe, Objektspeicher usw.)
  • Eingehende Benutzeranfragen zu verifizieren
  • Kontext-spezifische 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-Dienste innerhalb Ihres Kontexts. eyJhbGciOiJSUzI1NiI...
DTZ_CONTEXT_ID Ihre DTZ-Kontextkennung. context-3cd84429-64a4-4226-b868-c83feeff0f46
PORT Der Port, auf dem Ihr Dienst lauschen sollte. 80

Authentifizierung eingehender Anfragen

Ihr bereitgestellter Dienst kann eingehende Anfragen mit mehreren Methoden authentifizieren:

API-Schlüssel-Authentifizierung

Benutzer können sich mit DTZ-API-Schlüsseln bei Ihrem Dienst 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

Geben Sie ein JWT-Token im Authorization-Header an, um zu authentifizieren.

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

Basis-Authentifizierung

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

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

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

OAuth-Flow

DTZ bietet automatische OAuth-Authentifizierung für Webanwendungen. Wenn nicht authentifizierte Benutzer auf Ihren Dienst zugreifen, werden sie automatisch zur DTZ-Anmeldeseite weitergeleitet und nach erfolgreicher Anmeldung zurückgeleitet.

Verwendung der DTZ-Authentifizierung in Ihrem Dienst

Authentifizierte Anfragen an DTZ-Dienste stellen

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

import os
import requests

# Holen Sie das DTZ-Zugriffstoken aus der Umgebung
token = os.environ.get('DTZ_ACCESS_TOKEN')
context_id = os.environ.get('DTZ_CONTEXT_ID')

# Führen Sie eine authentifizierte Anfrage an die DTZ-API aus
headers = {
    'Authorization': f'Bearer {token}',
    'Content-Type': 'application/json'
}

response = requests.get(
    'https://api.dtz.rocks/v1/containers/services',
    headers=headers
)
// Node.js example
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 example
curl -H "Authorization: Bearer $DTZ_ACCESS_TOKEN" \
     -H "Content-Type: application/json" \
     https://api.dtz.rocks/v1/containers/services

Eingehende Authentifizierung verifizieren

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

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

Ihr Dienst erhält das JWT-Token und kann Benutzerinformationen daraus 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': 'Keine Authentifizierung bereitgestellt'}, 401
    
    token = auth_header.split(' ')[1]
    
    try:
        # Das Token ist bereits von DTZ verifiziert, daher können Sie 
        # Ansprüche ohne Signaturprüfung extrahieren.
        payload = jwt.decode(token, options={"verify_signature": False})
        
        user_id = payload.get('sub')  # Identity ID
        context_id = payload.get('scope')  # Context ID
        roles = payload.get('roles', [])  # Benutzerrollen
        
        return {
            'user_id': user_id,
            'context_id': context_id,
            'roles': roles,
            'message': 'Zugriff gewährt'
        }
    except jwt.InvalidTokenError:
        return {'error': 'Ungültiges Token'}, 401
// Express.js example
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: 'Keine Authentifizierung bereitgestellt' });
    }
    
    const token = authHeader.split(' ')[1];
    
    try {
        // Das Token ist bereits von DTZ verifiziert.
        const payload = jwt.decode(token);
        
        const userId = payload.sub;      // Identity ID
        const contextId = payload.scope; // Context ID
        const roles = payload.roles || [];    // Benutzerrollen
        
        res.json({
            user_id: userId,
            context_id: contextId,
            roles: roles,
            message: 'Zugriff gewährt'
        });
    } catch (error) {
        res.status(401).json({ error: 'Ungültiges Token' });
    }
});

JWT-Token-Struktur

DTZ-JWT-Tokens enthalten die folgenden 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 Ausstellungszeitpunkt 1640908800

Rollenbasierte Zugriffskontrolle

DTZ verwendet rollenbasierte Zugriffskontrolle mit URI-basierten Rollenkennungen. Gängige Rollenmuster sind:

  • https://dtz.rocks/context/admin/{context_id} - Kontextadministrator
  • https://dtz.rocks/containers/admin/{context_id} - Container-Service-Administrator
  • https://dtz.rocks/objectstore/admin/{context_id} - Object-Store-Administrator

Sie können in Ihrem Dienst auf 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

Beste Praktiken

Sicherheit

  • Überprüfen 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 Kommunikationen.
  • Protokollieren Sie keine sensiblen Authentifizierungstokens.

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 sensible Informationen offenzulegen.

Leistung

  • Cachen Sie ggf. die Ergebnisse der JWT-Token-Validierung.
  • Verwenden Sie Connection-Pooling für DTZ-API-Aufrufe.
  • Erwägen Sie die Implementierung von Ratenbegrenzung für Anfragen.

Beispiel: Vollständig authentifizierter Dienst

Hier ein vollständiges Beispiel eines Python-Flask-Dienstes 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 erfordert."""
    def decorated(*args, **kwargs):
        auth_header = request.headers.get('Authorization')
        if not auth_header or not auth_header.startswith('Bearer '):
            return jsonify({'error': 'Authentifizierung erforderlich'}), 401
        
        token = auth_header.split(' ')[1]
        user = get_user_from_token(token)
        
        if not user:
            return jsonify({'error': 'Ungültiges Token'}), 401
        
        request.user = user
        return f(*args, **kwargs)
    
    decorated.__name__ = f.__name__
    return decorated

@app.route('/health')
def health():
    """Ein öffentlicher Health-Check-Endpunkt."""
    return jsonify({'status': 'healthy'})

@app.route('/profile')
@require_auth
def profile():
    """Ein geschützter Endpunkt, der das Profil des Benutzers 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 nur für Admins zugänglicher Endpunkt."""
    required_role = f'https://dtz.rocks/context/admin/{DTZ_CONTEXT_ID}'
    
    if required_role not in request.user['roles']:
        return jsonify({'error': 'Admin-Zugriff erforderlich'}), 403
    
    # Führen Sie eine authentifizierte Anfrage an die DTZ-API aus
    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 Dienst über den DTZ-Container-Service bereitgestellt wird und die Umgebungsvariablen korrekt gelesen werden.
  • Ungültige Token-Fehler: Prüfen Sie, ob Sie das Token korrekt aus dem Authorization-Header extrahieren und ob das JWT-Parsen funktioniert.
  • 403 Forbidden-Fehler: Vergewissern Sie sich, dass der Benutzer die erforderlichen Rollen in seinem JWT-Token hat.
  • Service-zu-Service-Authentifizierung schlägt fehl: Stellen Sie sicher, dass Sie die Umgebungsvariable DTZ_ACCESS_TOKEN für ausgehende Anfragen verwenden.

Authentifizierung testen

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

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

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

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

For more detailed information about DTZ authentication, see the Authentication documentation.