Autenticacion¶
El Gateway soporta dos metodos de autenticacion segun el tipo de cliente:
| Metodo | Clientes | Mecanismo | Uso tipico |
|---|---|---|---|
| REST API Key | Scripts, bots, sistemas | X-API-Key + X-User-ID (headers) |
Integraciones programaticas |
| MCP OAuth 2.0 | Claude, ChatGPT, Gemini | JWT via Auth0 | Agentes de inteligencia artificial |
REST API - Autenticacion por API Key¶
Headers requeridos¶
Cada request a la REST API (/api/v1/*) requiere dos headers obligatorios:
| Header | Formato | Descripcion |
|---|---|---|
X-API-Key |
sk-gdi-xxx |
API Key del sistema externo, almacenada como hash en la tabla public.api_keys |
X-User-ID |
UUID v4 | Identificador del usuario que ejecuta la operacion. Debe estar autorizado para esa API Key en public.api_key_users |
Ambos headers son obligatorios
Si falta cualquiera de los dos headers, el Gateway responde 401 Unauthorized inmediatamente. No existe autenticacion solo por API Key sin identificar al usuario.
Ejemplo de request¶
curl -H "X-API-Key: sk-gdi-abc123" \
-H "X-User-ID: 550e8400-e29b-41d4-a716-446655440000" \
"https://gateway.tu-municipio.gdilatam.com/api/v1/cases/search?page=1&status=active"
Flujo de validacion¶
El Gateway ejecuta los siguientes pasos en orden al recibir un request REST:
flowchart TD
A["Request entrante<br/>GET /api/v1/cases/search"] --> B{"Header<br/>X-API-Key?"}
B -->|No| C["401: X-API-Key header requerido"]
B -->|Si| D{"Header<br/>X-User-ID?"}
D -->|No| E["401: X-User-ID header requerido<br/>para REST API"]
D -->|Si| F["Buscar API Key en<br/>public.api_keys"]
F --> G{"Key activa<br/>y no expirada?"}
G -->|No| H["401: API Key invalida<br/>o expirada"]
G -->|Si| I["Buscar User ID en<br/>public.api_key_users"]
I --> J{"Usuario autorizado<br/>para esta key?"}
J -->|No| K["401: Usuario no autorizado<br/>para esta API Key"]
J -->|Si| L["Obtener datos del usuario<br/>(municipalidad, sector, schema)"]
L --> M["Construir MCPContext<br/>y ejecutar operacion"]
Errores de autenticacion REST¶
| HTTP | Mensaje | Causa | Solucion |
|---|---|---|---|
| 401 | X-API-Key header requerido |
No se envio el header X-API-Key |
Agregar el header con la API Key proporcionada por el administrador |
| 401 | X-User-ID header requerido para REST API |
No se envio el header X-User-ID |
Agregar el header con el UUID del usuario |
| 401 | API Key invalida o expirada |
La API Key no existe, fue desactivada o expiro | Verificar la key con el administrador. Solicitar una nueva si expiro |
| 401 | Usuario no autorizado para esta API Key |
El User ID no esta asociado a esa API Key | Solicitar al administrador que vincule el usuario a la API Key |
Formato de respuesta de error
Todos los errores de autenticacion devuelven un JSON con la siguiente estructura:
Como obtener API Key y User ID¶
Las credenciales se gestionan exclusivamente desde el BackOffice (panel de administracion):
- El administrador del municipio accede al BackOffice
- En la seccion de API Keys, crea una nueva key con un nombre descriptivo (ej: "Sistema de Tramites Municipal")
- El sistema genera una key con formato
sk-gdi-xxxy la muestra una sola vez - El administrador asocia uno o mas usuarios a esa key (tabla
api_key_users) - El administrador entrega la API Key y los User IDs al equipo tecnico que realizara la integracion
Seguridad de la API Key
La API Key se muestra una unica vez al momento de crearla. Si se pierde, el administrador debe generar una nueva. Nunca incluir API Keys en codigo fuente o repositorios publicos.
MCP Server - Autenticacion OAuth 2.0¶
Descripcion general¶
El MCP Server utiliza OAuth 2.0 con Auth0 como proveedor de identidad. El flujo de autenticacion es completamente automatico para clientes compatibles: el usuario solo necesita iniciar sesion con su cuenta de GDI cuando el navegador se lo solicite.
Los clientes soportados actualmente son:
- Claude Code (Anthropic)
- ChatGPT (OpenAI)
- Gemini (Google)
Estandares implementados
El flujo se basa en los RFCs 9728 (OAuth Protected Resource Metadata) y 8414 (OAuth Authorization Server Metadata), lo que permite a los clientes MCP descubrir automaticamente como autenticarse sin configuracion manual.
Flujo de autenticacion paso a paso¶
sequenceDiagram
participant C as Cliente MCP<br/>(Claude, ChatGPT, Gemini)
participant G as Gateway<br/>(:8005)
participant A as Auth0<br/>(Authorization Server)
participant B as Navegador<br/>(del usuario)
C->>G: 1. POST /mcp (sin auth)
G-->>C: 2. 401 + WWW-Authenticate:<br/>Bearer resource_metadata="/.well-known/oauth-protected-resource"
C->>G: 3. GET /.well-known/oauth-protected-resource
G-->>C: 4. { authorization_servers: ["https://gdilatam.us.auth0.com"] }
C->>A: 5. Descubrir endpoints OAuth<br/>(authorize, token, register)
C->>B: 6. Abrir pantalla de login
B->>A: 7. Usuario ingresa credenciales
A-->>C: 8. Authorization code + tokens
C->>G: 9. POST /mcp<br/>Authorization: Bearer eyJhbG...
G->>G: 10. Validar JWT con JWKS de Auth0
G->>G: 11. Extraer email del token
G->>G: 12. Buscar usuario en BD por email
G->>G: 13. Construir MCPContext
G-->>C: 14. Respuesta con datos solicitados
Detalle de cada paso¶
Paso 1-2: Desafio de autenticacion
El cliente MCP intenta conectarse al endpoint /mcp sin credenciales. El Gateway responde con un 401 Unauthorized y un header WWW-Authenticate que indica donde descubrir la configuracion OAuth:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://gateway.tu-municipio.gdilatam.com/.well-known/oauth-protected-resource"
Content-Type: application/json
{
"error": "Authorization required",
"hint": "Use OAuth 2.0 to authenticate"
}
Paso 3-4: Discovery del recurso protegido (RFC 9728)
El cliente consulta el endpoint de metadata para saber a que servidor de autorizacion debe dirigirse:
{
"resource": "https://gateway.tu-municipio.gdilatam.com",
"authorization_servers": [
"https://gdilatam.us.auth0.com"
],
"scopes_supported": [
"openid",
"profile",
"email",
"offline_access"
],
"bearer_methods_supported": ["header"]
}
Paso 5-8: Login via Auth0
El cliente MCP redirige al usuario a Auth0 para autenticarse. El usuario ingresa sus credenciales de GDI (email + contrasena) en el navegador. Auth0 emite un JWT al completar el login.
Paso 9-14: Operacion autenticada
El cliente envia requests al endpoint /mcp incluyendo el JWT como Bearer token. El Gateway valida el token, identifica al usuario y ejecuta la operacion con los permisos correspondientes.
Endpoints de discovery¶
| Endpoint | RFC | Proposito |
|---|---|---|
/.well-known/oauth-protected-resource |
RFC 9728 | Indica el authorization server (Auth0) y scopes soportados |
/.well-known/oauth-authorization-server |
RFC 8414 | Metadata OAuth completa (authorize, token, register endpoints). Proxy del openid-configuration de Auth0 |
/.well-known/mcp.json |
MCP Spec | Manifest del servidor MCP (nombre, version, tools disponibles) |
/.well-known/openapi.json |
OpenAPI 3.0 | Especificacion REST API. Usado por ChatGPT Actions para auto-configuracion |
ChatGPT y discovery
ChatGPT requiere especificamente el endpoint /.well-known/oauth-authorization-server (no el path estandar de OpenID Connect). Tambien acepta el alias /.well-known/oauth-authorization-server/mcp.
Validacion del JWT¶
El Gateway valida el JWT recibido contra las JWKS (JSON Web Key Set) de Auth0, con cache de 30 minutos. Se aceptan multiples audiences para compatibilidad con distintos clientes:
Audiences validos:
- AUTH0_AUDIENCE (variable de entorno, audience del Backend)
- MCP_RESOURCE_URI (variable de entorno, configurable por deploy)
- https://gateway.tu-municipio.gdilatam.com (URL del recurso)
Del JWT se extrae el email del usuario (del claim email o, como fallback, consultando el endpoint /userinfo de Auth0). Con el email se busca al usuario en la base de datos y se construye el contexto de operacion.
Multi-tenant: usuarios con multiples organizaciones¶
Si un usuario tiene acceso a mas de una organizacion (municipalidad), el Gateway responde con un error que indica que debe seleccionar un tenant:
{
"error": "multi_tenant_selection_required",
"message": "El usuario tiene acceso a multiples organizaciones",
"tenants": [
{
"tenant_id": "uuid-municipio-a",
"name": "Municipalidad de San Martin"
},
{
"tenant_id": "uuid-municipio-b",
"name": "Municipalidad de Tres de Febrero"
}
]
}
Para resolver esto:
- El agente IA ejecuta el tool
list_my_tenantspara obtener las organizaciones disponibles - El usuario indica a cual quiere conectarse
- Las llamadas subsiguientes incluyen el
tenant_idseleccionado
Tablas de base de datos¶
Las credenciales REST se almacenan en el schema public (compartido entre todos los tenants):
public.api_keys¶
Almacena las API Keys generadas desde el BackOffice:
CREATE TABLE public.api_keys (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
key_hash VARCHAR NOT NULL, -- Hash SHA-256 de la API Key
name VARCHAR NOT NULL, -- Nombre descriptivo (ej: "Sistema Tramites")
is_active BOOLEAN DEFAULT true, -- Si la key esta activa
expires_at TIMESTAMP, -- Fecha de expiracion (NULL = sin expiracion)
created_at TIMESTAMP DEFAULT now()
);
Hash, no texto plano
La API Key se almacena como hash SHA-256. El valor original (sk-gdi-xxx) solo se muestra al momento de crear la key. El Gateway hashea la key recibida en el header y la compara contra key_hash.
public.api_key_users¶
Vincula API Keys con usuarios autorizados y sus municipalidades:
CREATE TABLE public.api_key_users (
api_key_id UUID REFERENCES public.api_keys(id),
user_id UUID NOT NULL, -- UUID del usuario en GDI
municipality_id UUID NOT NULL -- Municipalidad del usuario
);
Relacion entre tablas¶
erDiagram
api_keys ||--o{ api_key_users : "tiene"
api_key_users }o--|| users : "referencia"
api_keys {
uuid id PK
varchar key_hash
varchar name
boolean is_active
timestamp expires_at
timestamp created_at
}
api_key_users {
uuid api_key_id FK
uuid user_id
uuid municipality_id
}
Una API Key puede tener multiples usuarios asociados. Esto permite que un sistema externo opere con distintos usuarios segun el contexto (por ejemplo, un bot que crea documentos en nombre de diferentes funcionarios).
Comparacion de metodos¶
| Aspecto | REST API Key | MCP OAuth 2.0 |
|---|---|---|
| Quien lo usa | Sistemas, scripts, bots | Agentes IA (Claude, ChatGPT, Gemini) |
| Credenciales | API Key + User ID (headers) | JWT automatico via Auth0 |
| Configuracion | Manual (el admin crea la key) | Automatica (discovery RFC 9728) |
| Identidad del usuario | Explicita (X-User-ID header) |
Implicita (email del JWT) |
| Expiracion | Configurable o permanente | JWT con TTL de Auth0 + refresh token |
| Multi-tenant | Resuelto por municipality_id en api_key_users |
Resuelto por email, con selector si hay multiples orgs |
| Permisos | Mismos que el usuario en la UI | Mismos que el usuario en la UI |
| Revocacion | Desactivar key en BackOffice | Desactivar usuario en Auth0 |
Cual usar
- Si tu integracion es un script o sistema automatizado, usa REST API Key. Es mas simple de configurar y no requiere flujo OAuth.
- Si tu integracion es un agente IA que opera en nombre de un usuario humano, usa MCP OAuth 2.0. El usuario se autentica con su propia cuenta y el agente hereda sus permisos.