Autenticazione dei servizi

Autenticare i servizi distribuiti

DownToZero (DTZ) fornisce un’autenticazione integrata per i tuoi servizi distribuiti, rendendo semplice costruire applicazioni sicure senza gestire la tua infrastruttura di autenticazione.

Panoramica

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

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

Variabili d’ambiente

DTZ inietta automaticamente le seguenti variabili d’ambiente nei container del tuo servizio:

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 usando diversi metodi:

Autenticazione con API Key

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

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

Autenticazione con token Bearer

Fornisci un token JWT nell’header Authorization per autenticare.

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

Autenticazione Basic

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

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

Per le 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 reindirizzati indietro dopo un login riuscito.

Utilizzare l’autenticazione DTZ nel tuo servizio

Effettuare richieste autenticate ai servizi DTZ

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

import os
import requests

# Get the DTZ access token from the environment
token = os.environ.get('DTZ_ACCESS_TOKEN')
context_id = os.environ.get('DTZ_CONTEXT_ID')

# Make an authenticated request to the 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 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

Verificare l’autenticazione in ingresso

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

  1. Valida le credenziali di autenticazione.
  2. Converte le API key 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 sull’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': 'No authentication provided'}, 401
    
    token = auth_header.split(' ')[1]
    
    try:
        # The token is already verified by DTZ, so you can 
        # extract claims without signature verification.
        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', [])  # User roles
        
        return {
            'user_id': user_id,
            'context_id': context_id,
            'roles': roles,
            'message': 'Access granted'
        }
    except jwt.InvalidTokenError:
        return {'error': 'Invalid 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: 'No authentication provided' });
    }
    
    const token = authHeader.split(' ')[1];
    
    try {
        // The token is already verified by DTZ.
        const payload = jwt.decode(token);
        
        const userId = payload.sub;      // Identity ID
        const contextId = payload.scope; // Context ID
        const roles = payload.roles || [];    // User roles
        
        res.json({
            user_id: userId,
            context_id: contextId,
            roles: roles,
            message: 'Access granted'
        });
    } catch (error) {
        res.status(401).json({ error: 'Invalid token' });
    }
});

Struttura del token JWT

I token JWT di DTZ contengono le seguenti claim:

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

Controllo degli accessi basato sui ruoli

DTZ utilizza il controllo degli accessi basato sui ruoli con identificatori di ruolo basati su URI. Alcuni 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 dello storage oggetti

Puoi verificare 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

# Example:
if check_role(token, 'https://dtz.rocks/containers/admin/{context_id}'):
    # User has container admin permissions
    pass

Buone pratiche

Sicurezza

  • Verifica sempre che i token JWT contengano le claim attese.
  • Controlla i ruoli degli utenti prima di concedere accesso a operazioni sensibili.
  • Usa HTTPS per tutte le comunicazioni.
  • Non registrare (log) token di autenticazione sensibili.

Gestione degli errori

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

Prestazioni

  • Cache i risultati della validazione dei token JWT quando possibile.
  • Usa il connection pooling per le chiamate alle API DTZ.
  • Valuta l’implementazione di limitazione della frequenza delle richieste (rate limiting).

Esempio: servizio autenticato completo

Here is a complete example of a Python Flask service with DTZ authentication:

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):
    """Extracts user information from a 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):
    """A decorator to require authentication."""
    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():
    """A public health check endpoint."""
    return jsonify({'status': 'healthy'})

@app.route('/profile')
@require_auth
def profile():
    """A protected endpoint that returns the user's profile."""
    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():
    """An admin-only endpoint."""
    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
    
    # Make an authenticated request to the 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)

Risoluzione dei problemi

Problemi comuni

  • Authentication token not found: Assicurati che il tuo servizio sia distribuito tramite il servizio containers di DTZ e che le variabili d’ambiente siano lette correttamente.
  • Invalid token errors: Verifica che stai estraendo correttamente il token dall’header Authorization e che il parsing del JWT funzioni.
  • 403 Forbidden errors: Verifica che l’utente abbia i ruoli richiesti nel proprio token JWT.
  • Service-to-service authentication failing: 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 with an API key
curl -H "X-API-KEY: your-api-key" https://yourservice.dtz.rocks/profile

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

# Test unauthenticated (should return 401)
curl https://yourservice.dtz.rocks/profile

Per informazioni più dettagliate sull’autenticazione DTZ, consulta la documentazione sull’autenticazione.