Service Authentication

Autenticazione dei Servizi Distribuiti

DownToZero (DTZ) fornisce un sistema di autenticazione integrato per i tuoi servizi distribuiti, rendendo semplice costruire applicazioni sicure senza dover gestire un’infrastruttura di autenticazione propria.

Panoramica

Quando distribuisci un servizio su DTZ, la piattaforma fornisce automaticamente credenziali di autenticazione e informazioni contestuali tramite variabili d’ambiente. Il tuo servizio può utilizzare queste per:

  • Autenticare le richieste verso altri servizi DTZ (chiamate API, object storage, ecc.)
  • Verificare le richieste in ingresso degli utenti
  • Accedere a risorse specifiche del contesto
  • Implementare una comunicazione sicura tra servizi

Variabili d’Ambiente

DTZ inietta automaticamente le seguenti variabili d’ambiente nei container dei tuoi servizi:

Variabile Descrizione Esempio
DTZ_ACCESS_TOKEN Un token JWT per accedere ai servizi DTZ all’interno del tuo contesto. eyJhbGciOiJSUzI1NiI...
DTZ_CONTEXT_ID Il tuo identificatore di contesto DTZ. context-3cd84429-64a4-4226-b868-c83feeff0f46
PORT La porta su cui il tuo servizio dovrebbe ascoltare. 80

Autenticazione delle Richieste in Ingresso

Il tuo servizio distribuito può autenticare le richieste in ingresso con diversi metodi:

Autenticazione con API Key

Gli utenti possono autenticarsi con il tuo servizio usando le chiavi API DTZ, fornendo la chiave nell’header X-API-KEY.

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

Autenticazione con Bearer Token

Fornisci un token JWT nell’header Authorization per autenticarti.

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

Autenticazione Basic

Puoi anche passare le chiavi API usando l’autenticazione basic.

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

Per applicazioni web, il servizio Identity di DTZ può gestire l’autenticazione tramite cookie del browser.

Flusso OAuth

DTZ fornisce autenticazione OAuth automatica per le applicazioni web. Quando utenti non autenticati accedono al tuo servizio, vengono automaticamente reindirizzati alla pagina di login DTZ e poi riportati indietro dopo un login riuscito.

Utilizzo dell’Autenticazione DTZ nel Tuo Servizio

Effettuare Richieste Autenticate ai Servizi DTZ

Usa la variabile d’ambiente DTZ_ACCESS_TOKEN per effettuare chiamate autenticate ad altri servizi DTZ.

import os
import requests

# Ottieni il token di accesso DTZ dall'ambiente
token = os.environ.get('DTZ_ACCESS_TOKEN')
context_id = os.environ.get('DTZ_CONTEXT_ID')

# Effettua una richiesta autenticata all'API DTZ
headers = {
    'Authorization': f'Bearer {token}',
    'Content-Type': 'application/json'
}

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

Verifica dell’Autenticazione in Ingresso

DTZ gestisce automaticamente l’autenticazione per le richieste in ingresso. Quando un utente fa una richiesta autenticata al tuo servizio, DTZ:

  1. Valida le credenziali di autenticazione.
  2. Converte le chiavi API in token JWT.
  3. Inoltra la richiesta con un header Authorization: Bearer <token>.

Il tuo servizio riceve il token JWT e può estrarre le informazioni utente da esso.

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': 'Nessuna autenticazione fornita'}, 401
    
    token = auth_header.split(' ')[1]
    
    try:
        # Il token è già verificato da DTZ, quindi puoi
        # estrarre le claims senza verificare la firma.
        payload = jwt.decode(token, options={"verify_signature": False})
        
        user_id = payload.get('sub')  # ID identità
        context_id = payload.get('scope')  # ID contesto
        roles = payload.get('roles', [])  # Ruoli utente
        
        return {
            'user_id': user_id,
            'context_id': context_id,
            'roles': roles,
            'message': 'Accesso consentito'
        }
    except jwt.InvalidTokenError:
        return {'error': 'Token non valido'}, 401
// Esempio 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: 'Nessuna autenticazione fornita' });
    }
    
    const token = authHeader.split(' ')[1];
    
    try {
        // Il token è già verificato da DTZ.
        const payload = jwt.decode(token);
        
        const userId = payload.sub;      // ID identità
        const contextId = payload.scope; // ID contesto
        const roles = payload.roles || [];    // Ruoli utente
        
        res.json({
            user_id: userId,
            context_id: contextId,
            roles: roles,
            message: 'Accesso consentito'
        });
    } catch (error) {
        res.status(401).json({ error: 'Token non valido' });
    }
});

Struttura del Token JWT

I token JWT DTZ contengono le seguenti claims:

Claim Descrizione Esempio
iss Emittente (sempre “dtz.rocks”) "dtz.rocks"
sub Soggetto (ID identità utente) "identity-abc123..."
aud Destinatario (sempre “dtz.rocks”) "dtz.rocks"
scope ID contesto "context-3cd84429..."
roles Ruoli/permessi utente ["https://dtz.rocks/context/admin/{context_id}"]
contexts Contesti disponibili ["context-3cd84429..."]
exp Tempo di scadenza 1640995200
iat Tempo di emissione 1640908800

Controllo di Accesso Basato sui Ruoli

DTZ utilizza il controllo di accesso basato sui ruoli con identificatori di ruolo basati su URI. I pattern di ruolo comuni includono:

  • https://dtz.rocks/context/admin/{context_id} - Amministratore del contesto
  • https://dtz.rocks/containers/admin/{context_id} - Amministratore dei servizi container
  • https://dtz.rocks/objectstore/admin/{context_id} - Amministratore dell’object store

Puoi controllare i ruoli nel tuo servizio:

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

# Esempio:
if check_role(token, 'https://dtz.rocks/containers/admin/{context_id}'):
    # L'utente ha permessi di amministratore dei container
    pass

Best Practices

Sicurezza

  • Valida sempre che i token JWT contengano le claims previste.
  • Controlla i ruoli utente prima di concedere accesso a operazioni sensibili.
  • Usa HTTPS per tutte le comunicazioni.
  • Non registrare nei log token di autenticazione sensibili.

Gestione degli Errori

  • Ritorna codici di stato HTTP appropriati (es. 401 per non autorizzato, 403 per proibito).
  • Fornisci messaggi di errore significativi senza esporre informazioni sensibili.

Performance

  • Cache i risultati della convalida dei token JWT quando possibile.
  • Usa connection pooling per le chiamate API DTZ.
  • Considera di implementare il rate limiting delle richieste.

Esempio: Servizio Completo Autenticato

Ecco un esempio completo di un servizio Python Flask con autenticazione 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):
    """Estrae le informazioni utente da un token 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 decoratore per richiedere l'autenticazione."""
    def decorated(*args, **kwargs):
        auth_header = request.headers.get('Authorization')
        if not auth_header or not auth_header.startswith('Bearer '):
            return jsonify({'error': 'Autenticazione richiesta'}), 401
        
        token = auth_header.split(' ')[1]
        user = get_user_from_token(token)
        
        if not user:
            return jsonify({'error': 'Token non valido'}), 401
        
        request.user = user
        return f(*args, **kwargs)
    
    decorated.__name__ = f.__name__
    return decorated

@app.route('/health')
def health():
    """Un endpoint pubblico per il controllo dello stato."""
    return jsonify({'status': 'healthy'})

@app.route('/profile')
@require_auth
def profile():
    """Un endpoint protetto che restituisce il profilo utente."""
    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 riservato agli amministratori."""
    required_role = f'https://dtz.rocks/context/admin/{DTZ_CONTEXT_ID}'
    
    if required_role not in request.user['roles']:
        return jsonify({'error': 'Accesso admin richiesto'}), 403
    
    # Effettua una richiesta autenticata all'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)

Risoluzione dei Problemi

Problemi Comuni

  • Token di autenticazione non trovato: Assicurati che il tuo servizio sia distribuito tramite il servizio container DTZ e che le variabili d’ambiente vengano lette correttamente.
  • Errori token non valido: Controlla che estrai correttamente il token dall’header Authorization e che il parsing JWT funzioni.
  • Errori 403 Forbidden: Verifica che l’utente abbia i ruoli richiesti nel token JWT.
  • Autenticazione servizio a servizio fallita: Assicurati di usare la variabile d’ambiente DTZ_ACCESS_TOKEN per le richieste in uscita.

Test dell’Autenticazione

Puoi testare l’autenticazione del tuo servizio usando curl:

# Test con una API key
curl -H "X-API-KEY: your-api-key" https://yourservice.dtz.rocks/profile

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

# Test senza autenticazione (dovrebbe restituire 401)
curl https://yourservice.dtz.rocks/profile

Per maggiori dettagli sull’autenticazione DTZ, consulta la documentazione sull’autenticazione.