Authentification des services

Authentifier les services déployés

DownToZero (DTZ) fournit une authentification intégrée pour vos services déployés, ce qui facilite la création d’applications sécurisées sans gérer votre propre infrastructure d’authentification.

Vue d’ensemble

Lorsque vous déployez un service sur DTZ, la plateforme fournit automatiquement des informations d’authentification et de contexte via des variables d’environnement. Votre service peut les utiliser pour :

  • Authentifier des requêtes vers d’autres services DTZ (appels API, stockage d’objets, etc.)
  • Vérifier les requêtes entrantes des utilisateurs
  • Accéder à des ressources spécifiques au contexte
  • Mettre en œuvre une communication sécurisée service-à-service

Variables d’environnement

DTZ injecte automatiquement les variables d’environnement suivantes dans les conteneurs de votre service :

Variable Description Exemple
DTZ_ACCESS_TOKEN Un jeton JWT pour accéder aux services DTZ dans votre contexte. eyJhbGciOiJSUzI1NiI...
DTZ_CONTEXT_ID L’identifiant de contexte DTZ. context-3cd84429-64a4-4226-b868-c83feeff0f46
PORT Le port sur lequel votre service doit écouter. 80

Authentification des requêtes entrantes

Votre service déployé peut authentifier les requêtes entrantes en utilisant plusieurs méthodes :

Authentification par clé API

Les utilisateurs peuvent s’authentifier auprès de votre service en fournissant la clé DTZ dans l’en-tête X-API-KEY.

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

Authentification par jeton Bearer

Fournissez un jeton JWT dans l’en-tête Authorization pour vous authentifier.

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

Authentification basique

Vous pouvez également transmettre des clés API en utilisant l’authentification basique.

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

Pour les applications web, le service Identity de DTZ peut gérer l’authentification via des cookies du navigateur.

Flux OAuth

DTZ fournit une authentification OAuth automatique pour les applications web. Lorsque des utilisateurs non authentifiés accèdent à votre service, ils sont automatiquement redirigés vers la page de connexion DTZ puis redirigés de retour après une connexion réussie.

Utiliser l’authentification DTZ dans votre service

Faire des requêtes authentifiées vers les services DTZ

Utilisez la variable d’environnement DTZ_ACCESS_TOKEN pour effectuer des appels authentifiés vers d’autres services DTZ.

import os
import requests

# Récupère le jeton d'accès DTZ depuis l'environnement
token = os.environ.get('DTZ_ACCESS_TOKEN')
context_id = os.environ.get('DTZ_CONTEXT_ID')

# Effectue une requête authentifiée vers l'API DTZ
headers = {
    'Authorization': f'Bearer {token}',
    'Content-Type': 'application/json'
}

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

Vérifier l’authentification entrante

DTZ gère automatiquement l’authentification des requêtes entrantes. Lorsqu’un utilisateur effectue une requête authentifiée vers votre service, DTZ :

  1. Valide les informations d’authentification.
  2. Convertit les clés API en jetons JWT.
  3. Transmet la requête avec un en-tête Authorization: Bearer <token>.

Votre service reçoit le jeton JWT et peut en extraire les informations utilisateur.

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': "Aucune authentification fournie"}, 401
    
    token = auth_header.split(' ')[1]
    
    try:
        # Le jeton est déjà vérifié par DTZ, vous pouvez donc
        # extraire les claims sans vérification de signature.
        payload = jwt.decode(token, options={"verify_signature": False})
        
        user_id = payload.get('sub')  # ID d'identité
        context_id = payload.get('scope')  # ID de contexte
        roles = payload.get('roles', [])  # Rôles utilisateur
        
        return {
            'user_id': user_id,
            'context_id': context_id,
            'roles': roles,
            'message': "Accès autorisé"
        }
    except jwt.InvalidTokenError:
        return {'error': 'Jeton invalide'}, 401
// Exemple Express.js
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: "Aucune authentification fournie" });
    }
    
    const token = authHeader.split(' ')[1];
    
    try {
        // Le jeton est déjà vérifié par DTZ.
        const payload = jwt.decode(token);
        
        const userId = payload.sub;      // ID d'identité
        const contextId = payload.scope; // ID de contexte
        const roles = payload.roles || [];    // Rôles utilisateur
        
        res.json({
            user_id: userId,
            context_id: contextId,
            roles: roles,
            message: "Accès autorisé"
        });
    } catch (error) {
        res.status(401).json({ error: 'Jeton invalide' });
    }
});

Structure du jeton JWT

Les jetons JWT DTZ contiennent les claims suivants :

Claim Description Example
iss Émetteur (toujours “dtz.rocks”) "dtz.rocks"
sub Sujet (ID de l’identité utilisateur) "identity-abc123..."
aud Audience (toujours “dtz.rocks”) "dtz.rocks"
scope ID de contexte "context-3cd84429..."
roles Rôles/permissions de l’utilisateur ["https://dtz.rocks/context/admin/{context_id}"]
contexts Contextes disponibles ["context-3cd84429..."]
exp Temps d’expiration 1640995200
iat Temps d’émission 1640908800

Contrôle d’accès basé sur les rôles

DTZ utilise un contrôle d’accès basé sur les rôles avec des identifiants de rôle au format URI. Les modèles de rôle courants incluent :

  • https://dtz.rocks/context/admin/{context_id} - Administrateur de contexte
  • https://dtz.rocks/containers/admin/{context_id} - Administrateur du service de conteneurs
  • https://dtz.rocks/objectstore/admin/{context_id} - Administrateur du stockage d’objets

Vous pouvez vérifier les rôles dans votre service :

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

# Exemple :
if check_role(token, 'https://dtz.rocks/containers/admin/{context_id}'):
    # L'utilisateur a les permissions d'administrateur des conteneurs
    pass

Bonnes pratiques

Sécurité

  • Validez toujours que les jetons JWT contiennent les claims attendus.
  • Vérifiez les rôles utilisateur avant d’accorder l’accès à des opérations sensibles.
  • Utilisez HTTPS pour toutes les communications.
  • Ne consignez pas les jetons d’authentification sensibles.

Gestion des erreurs

  • Retournez des codes d’état HTTP appropriés (par ex., 401 pour non autorisé, 403 pour interdit).
  • Fournissez des messages d’erreur significatifs sans divulguer d’informations sensibles.

Performance

  • Mettez en cache les résultats de validation des jetons JWT lorsque c’est possible.
  • Utilisez le pooling de connexions pour les appels à l’API DTZ.
  • Envisagez de mettre en place une limitation du débit des requêtes.

Exemple : service authentifié complet

Voici un exemple complet d’un service Python Flask avec authentification DTZ :

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):
    """Extrait les informations utilisateur d'un jeton JWT DTZ."""
    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):
    """Un décorateur pour exiger l'authentification."""
    def decorated(*args, **kwargs):
        auth_header = request.headers.get('Authorization')
        if not auth_header or not auth_header.startswith('Bearer '):
            return jsonify({'error': 'Authentification requise'}), 401
        
        token = auth_header.split(' ')[1]
        user = get_user_from_token(token)
        
        if not user:
            return jsonify({'error': 'Jeton invalide'}), 401
        
        request.user = user
        return f(*args, **kwargs)
    
    decorated.__name__ = f.__name__
    return decorated

@app.route('/health')
def health():
    """Un endpoint public de vérification de l'état."""
    return jsonify({'status': 'healthy'})

@app.route('/profile')
@require_auth
def profile():
    """Un endpoint protégé qui renvoie le profil de l'utilisateur."""
    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():
    """Un endpoint réservé aux administrateurs."""
    required_role = f'https://dtz.rocks/context/admin/{DTZ_CONTEXT_ID}'
    
    if required_role not in request.user['roles']:
        return jsonify({'error': "Accès administrateur requis"}), 403
    
    # Effectue une requête authentifiée vers l'API DTZ
    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)

Dépannage

Problèmes courants

  • Jeton d’authentification introuvable : Assurez-vous que votre service est déployé via le service de conteneurs DTZ et que les variables d’environnement sont lues correctement.
  • Erreurs de jeton invalide : Vérifiez que vous extrayez correctement le jeton depuis l’en-tête Authorization et que l’analyse du JWT fonctionne.
  • Erreurs 403 Forbidden : Vérifiez que l’utilisateur possède les rôles requis dans son jeton JWT.
  • Échec de l’authentification service-à-service : Assurez-vous d’utiliser la variable d’environnement DTZ_ACCESS_TOKEN pour les requêtes sortantes.

Tester l’authentification

Vous pouvez tester l’authentification de votre service en utilisant curl :

# Test avec une clé API
curl -H "X-API-KEY: your-api-key" https://yourservice.dtz.rocks/profile

# Test avec un jeton bearer  
curl -H "Authorization: Bearer your-jwt-token" https://yourservice.dtz.rocks/profile

# Test non authentifié (devrait retourner 401)
curl https://yourservice.dtz.rocks/profile

Pour plus d’informations détaillées sur l’authentification DTZ, consultez la Documentation d’authentification.