category: Containers title: Autenticación de Servicios order: 250 description: “Aprende a usar la autenticación integrada de DownToZero para tus servicios desplegados, incluyendo variables de entorno, métodos de autenticación y estructura JWT.”

Autenticación de servicios desplegados

DownToZero (DTZ) proporciona autenticación integrada para tus servicios desplegados, lo que facilita construir aplicaciones seguras sin gestionar tu propia infraestructura de autenticación.

Descripción general

Cuando despliegas un servicio en DTZ, la plataforma proporciona automáticamente credenciales de autenticación e información de contexto a través de variables de entorno. Tu servicio puede usarlas para:

  • Autenticar solicitudes a otros servicios de DTZ (llamadas a la API, almacenamiento de objetos, etc.)
  • Verificar solicitudes entrantes de usuarios
  • Acceder a recursos específicos del contexto
  • Implementar comunicación segura entre servicios

Variables de entorno

DTZ inyecta automáticamente las siguientes variables de entorno en los contenedores de tu servicio:

Variable Description Example
DTZ_ACCESS_TOKEN A JWT token for accessing DTZ services within your context. eyJhbGciOiJSUzI1NiI...
DTZ_CONTEXT_ID Your DTZ context identifier. context-3cd84429-64a4-4226-b868-c83feeff0f46
PORT The port your service should listen on. 80

Autenticando solicitudes entrantes

Tu servicio desplegado puede autenticar solicitudes entrantes usando varios métodos:

Autenticación con API Key

Los usuarios pueden autenticarse con tu servicio usando claves de API de DTZ proporcionando la clave en el encabezado X-API-KEY.

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

Autenticación con token Bearer

Proporciona un token JWT en el encabezado Authorization para autenticar.

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

Autenticación básica

También puedes enviar claves de API usando autenticación básica.

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

Autenticación basada en cookies

Para aplicaciones web, el servicio de identidad de DTZ puede manejar la autenticación mediante cookies del navegador.

Flujo OAuth

DTZ proporciona autenticación OAuth automática para aplicaciones web. Cuando usuarios no autenticados acceden a tu servicio, son redirigidos automáticamente a la página de inicio de sesión de DTZ y luego redirigidos de vuelta después de un inicio de sesión exitoso.

Usando la autenticación de DTZ en tu servicio

Realizar solicitudes autenticadas a servicios DTZ

Usa la variable de entorno DTZ_ACCESS_TOKEN para realizar llamadas autenticadas a otros servicios de DTZ.

import os
import requests

# Obtener el token de acceso de DTZ desde el entorno
token = os.environ.get('DTZ_ACCESS_TOKEN')
context_id = os.environ.get('DTZ_CONTEXT_ID')

# Hacer una solicitud autenticada a la API de DTZ
headers = {
    'Authorization': f'Bearer {token}',
    'Content-Type': 'application/json'
}

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

Verificar la autenticación entrante

DTZ maneja automáticamente la autenticación para solicitudes entrantes. Cuando un usuario realiza una solicitud autenticada a tu servicio, DTZ:

  1. Valida las credenciales de autenticación.
  2. Convierte las claves de API a tokens JWT.
  3. Reenvía la solicitud con un encabezado Authorization: Bearer <token>.

Tu servicio recibe el token JWT y puede extraer información del usuario a partir de él.

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:
        # El token ya está verificado por DTZ, por lo que puedes
        # extraer las claims sin verificación de firma.
        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
// Ejemplo en 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: 'No authentication provided' });
    }
    
    const token = authHeader.split(' ')[1];
    
    try {
        // El token ya está verificado por 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' });
    }
});

Estructura del token JWT

Los tokens JWT de DTZ contienen las siguientes claims:

Claim Description Example
iss Issuer (always “dtz.rocks”) "dtz.rocks"
sub Subject (user identity ID) "identity-abc123..."
aud Audience (always “dtz.rocks”) "dtz.rocks"
scope Context ID "context-3cd84429..."
roles User roles/permissions ["https://dtz.rocks/context/admin/{context_id}"]
contexts Available contexts ["context-3cd84429..."]
exp Expiration time 1640995200
iat Issued at time 1640908800

Control de acceso basado en roles

DTZ utiliza control de acceso basado en roles con identificadores de rol basados en URI. Los patrones de rol comunes incluyen:

  • https://dtz.rocks/context/admin/{context_id} - Administrador del contexto
  • https://dtz.rocks/containers/admin/{context_id} - Administrador del servicio de contenedores
  • https://dtz.rocks/objectstore/admin/{context_id} - Administrador del almacenamiento de objetos

Puedes comprobar los roles en tu servicio:

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

# Ejemplo:
if check_role(token, 'https://dtz.rocks/containers/admin/{context_id}'):
    # El usuario tiene permisos de administrador de contenedores
    pass

Buenas prácticas

Seguridad

  • Valida siempre que los tokens JWT contengan las claims esperadas.
  • Comprueba los roles de usuario antes de conceder acceso a operaciones sensibles.
  • Usa HTTPS para todas las comunicaciones.
  • No registres tokens de autenticación sensibles.

Manejo de errores

  • Devuelve códigos de estado HTTP apropiados (por ejemplo, 401 para no autorizado, 403 para prohibido).
  • Proporciona mensajes de error significativos sin exponer información sensible.

Rendimiento

  • Cachea los resultados de validación de tokens JWT cuando sea posible.
  • Usa agrupación de conexiones (connection pooling) para llamadas a la API de DTZ.
  • Considera implementar limitación de tasa de solicitudes.

Ejemplo: Servicio completamente autenticado

Aquí tienes un ejemplo completo de un servicio Python Flask con autenticación 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):
    """Extrae la información del usuario desde un token JWT de 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 decorador para requerir autenticación."""
    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():
    """Un endpoint público para comprobar el estado."""
    return jsonify({'status': 'healthy'})

@app.route('/profile')
@require_auth
def profile():
    """Un endpoint protegido que devuelve el perfil del usuario."""
    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 solo para administradores."""
    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
    
    # Hacer una solicitud autenticada a la API de 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)

Resolución de problemas

Problemas comunes

  • Authentication token not found: Asegúrate de que tu servicio esté desplegado a través del servicio de contenedores de DTZ y de que las variables de entorno se estén leyendo correctamente.
  • Invalid token errors: Comprueba que estés extrayendo correctamente el token del encabezado Authorization y que el parseo del JWT funcione.
  • 403 Forbidden errors: Verifica que el usuario tenga los roles requeridos en su token JWT.
  • Service-to-service authentication failing: Asegúrate de que estás usando la variable de entorno DTZ_ACCESS_TOKEN para las solicitudes salientes.

Pruebas de autenticación

Puedes probar la autenticación de tu servicio usando curl:

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

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

# Probar sin autenticar (debería devolver 401)
curl https://yourservice.dtz.rocks/profile

Para más información detallada sobre la autenticación de DTZ, consulta la Documentación de autenticación.