Autenticacion¶
El backend usa Auth0 con tokens JWT RS256 para autenticacion. Definido en auth.py.
Configuracion Auth0¶
| Variable | Valor Default |
|---|---|
AUTH0_DOMAIN |
tu-tenant.us.auth0.com |
AUTH0_AUDIENCE |
Configurado en .env |
AUTH0_ALGORITHMS |
["RS256"] |
Flujo de Autenticacion¶
Modo Produccion¶
1. Frontend obtiene JWT de Auth0 (login)
2. Frontend envia request con header:
Authorization: Bearer <jwt_token>
X-Tenant-Schema: 200_muni
3. TenantMiddleware valida email del JWT contra el schema
4. auth.py valida JWT y obtiene AuthenticatedUser
5. Endpoint recibe current_user con permisos
Modo Testing¶
Si TESTING_MODE=true en .env, el backend acepta autenticacion simplificada:
| Metodo | Header | Descripcion |
|---|---|---|
| API Key fija | X-API-Key: <TESTING_API_KEY> |
Usa primer usuario activo |
| UUID directo | X-User-ID: <uuid> |
UUID del usuario |
| Bearer UUID | Authorization: Bearer <uuid> |
UUID en lugar de JWT |
| JWT normal | Authorization: Bearer <jwt> |
Funciona igual que produccion |
Seguridad
TESTING_MODE se desactiva automaticamente si se detecta entorno de produccion.
Funciones Principales¶
get_current_user¶
Dependency principal de FastAPI. Se usa en todos los endpoints protegidos.
# auth.py
def get_current_user(
request: Request,
credentials: Optional[HTTPAuthorizationCredentials] = Depends(security),
x_user_id: Optional[str] = Header(None, alias="X-User-ID")
) -> AuthenticatedUser:
Retorna un AuthenticatedUser con:
user_id: UUID del usuario en la BDauth_id: ID de Auth0 (subdel JWT)full_name: Nombre completoemail: Emailpermissions: Lista deSectorPermission
Uso en endpoints:
@router.get("/documents")
async def list_documents(
current_user: AuthenticatedUser = Depends(get_current_user),
schema_name: str = Depends(get_tenant_schema)
):
# current_user.user_id, current_user.permissions, etc.
...
verify_token¶
Verifica y decodifica un JWT de Auth0.
Proceso:
- Obtiene la clave RSA publica de Auth0 (JWKS) con cache de 30 minutos
- Busca la clave correspondiente al
kiddel token - Decodifica y verifica el JWT con audience e issuer
Errores:
| HTTP | Detalle |
|---|---|
| 401 | Token expirado |
| 401 | Claims incorrectos (audience/issuer) |
| 401 | Token invalido |
| 503 | No se pudo obtener claves de Auth0 |
verify_auth0_token¶
Valida JWT sin requerir que el usuario exista en BD. Util para onboarding.
def verify_auth0_token(
credentials: HTTPAuthorizationCredentials = Depends(security)
) -> Dict[str, Any]:
load_user_permissions¶
Carga permisos de sectores del usuario desde la BD.
Retorna lista de SectorPermission con informacion de cada sector donde el usuario tiene acceso.
decode_jwt_from_request¶
Extrae y valida JWT del header Authorization. Usado internamente por el TenantMiddleware.
Cache de JWKS¶
Las claves publicas de Auth0 se cachean por 30 minutos para evitar consultas frecuentes:
Permisos de Sectores¶
El sistema de permisos se basa en sectores. Cada usuario puede tener acceso a multiples sectores con diferentes niveles:
| Permiso | Descripcion |
|---|---|
can_view |
Ver documentos y expedientes del sector |
can_edit |
Crear y editar documentos en el sector |
is_primary |
Sector principal del usuario |
Los permisos se cargan desde la tabla user_sector_permissions al autenticar al usuario y se incluyen en el AuthenticatedUser.
Dependencies de FastAPI¶
Definidas en shared/dependencies.py:
def get_tenant_schema(request: Request) -> str:
"""Extrae schema_name del request.state (seteado por TenantMiddleware)."""
return request.state.schema_name
def get_auth_source(request: Request) -> str:
"""Extrae auth_source del request.state."""
return getattr(request.state, 'auth_source', 'unknown')
Auth0 Database Connection¶
El BackOffice soporta dual auth: usuarios pueden autenticarse via proveedores sociales (Google, Microsoft) o via Database Connection (email + password). La gestion de Database Connection se implementa en services/auth0_service.py del BackOffice-Back.
Tipos de Autenticacion¶
| Tipo | Connection | Uso tipico |
|---|---|---|
| Social | google-oauth2, windowslive |
Usuarios con cuenta Google/Microsoft institucional |
| Database | Username-Password-Authentication |
Usuarios sin cuenta social (email + password manual) |
Variables de Entorno¶
| Variable | Descripcion | Default |
|---|---|---|
AUTH0_M2M_CLIENT_ID |
Client ID de la aplicacion M2M (Management API) | requerida |
AUTH0_M2M_CLIENT_SECRET |
Client Secret de la aplicacion M2M | requerida |
AUTH0_M2M_AUDIENCE |
Audience de la Management API | https://{AUTH0_DOMAIN}/api/v2/ |
AUTH0_DB_CONNECTION |
Nombre de la Database Connection | Username-Password-Authentication |
AUTH0_FRONTEND_CLIENT_ID |
Client ID de la app frontend (para redirect en password tickets) | AUTH0_CLIENT_ID (fallback) |
Token M2M (Management API)¶
Para interactuar con la Management API de Auth0, el servicio obtiene un token via Client Credentials grant. El token se cachea por 23 horas (el token dura 24h, se renueva 1 hora antes de expirar).
# Cache del token M2M
_token_cache = {"token": None, "expires_at": 0}
def _get_management_token() -> str:
"""Obtiene token via Client Credentials. Cachea 23h."""
if _token_cache["token"] and time.time() < _token_cache["expires_at"]:
return _token_cache["token"]
# POST https://{AUTH0_DOMAIN}/oauth/token
# grant_type: client_credentials
# ...
Funciones del Servicio¶
check_user_exists_in_auth0(email)¶
Busca si un email ya existe en Auth0 (cualquier connection).
# GET https://{AUTH0_DOMAIN}/api/v2/users-by-email?email={email}
users = check_user_exists_in_auth0("juan@municipio.gob.ar")
# Retorna: lista de usuarios (puede ser vacia)
check_email_has_social_account(email)¶
Verifica si el email ya tiene una cuenta Social (Google/Microsoft). Retorna True/False.
Util para decidir si hay que crear una Database Connection o si el usuario ya puede hacer login social.
create_database_user(email, full_name)¶
Crea un usuario en Auth0 Database Connection con password aleatorio.
result = create_database_user("juan@municipio.gob.ar", "Juan Perez")
# Retorna: {"user_id": "auth0|abc123", ...}
Detalles de implementacion:
- Password aleatorio:
secrets.token_urlsafe(32) + "!A1"(cumple Good policy) email_verified: True(evita mail de verificacion)verify_email: Falseapp_metadata: {"gdi_invitation": True}(marca como invitado)
create_password_change_ticket(auth0_user_id, client_id)¶
Genera un link de activacion (password change ticket) para que el usuario establezca su password.
ticket_url = create_password_change_ticket("auth0|abc123", FRONTEND_CLIENT_ID)
# Retorna: "https://gdilatam.us.auth0.com/lo/reset?..."
Detalles:
- TTL: 5 dias (432000 segundos)
- Usa
client_id(noresult_url) porque New Universal Login ignoraresult_url - Auth0 usa el Application Login URI configurado en la app del
client_idpara el boton de redirect post-cambio
Flujo Completo: Crear Usuario con Database Connection¶
1. Admin crea usuario en BackOffice
2. BackOffice-Back verifica si email tiene cuenta social:
- SI tiene social → no crea Database user, el usuario ya puede loguearse
- NO tiene social → sigue al paso 3
3. create_database_user(email, full_name)
→ Auth0 crea usuario con password aleatorio
4. create_password_change_ticket(auth0_user_id, FRONTEND_CLIENT_ID)
→ Auth0 genera link de activacion (5 dias TTL)
5. Se envia email al usuario con el link de activacion (via Resend)
6. Usuario abre el link → establece su password en Auth0
7. Usuario hace login con email + password → obtiene JWT
8. En primer login, el Backend completa auth_id en la tabla users (onboarding)
Onboarding: auth_id
La columna auth_id en la tabla users se completa en el primer login del usuario. Hasta entonces, el usuario existe en la BD pero sin auth_id. La funcion verify_auth0_token permite validar el JWT sin requerir que el usuario exista previamente en la BD, facilitando este flujo de onboarding.