resolveActorEmail — resolución de identidad CF Access con fallback JWT
resolveActorEmail — Resolución de identidad CF Access con fallback JWT
Función exportada en functions/_lib/staff.ts que devuelve el email del actor autenticado en un request de Cloudflare Pages Functions. Es el único punto de resolución de identidad para las integraciones que necesitan saber “quién es este request”.
Problema que resuelve
CF Access puede inyectar la identidad del usuario de dos formas distintas:
-
Header
Cf-Access-Authenticated-User-Email: solo está disponible si la aplicación CF Access tiene activada la opción “Include identity”. En CreaRack Pro esta opción no estaba activada, por lo que el panel/settings/integrations/zohomostraba “sesión actual: desconocida” y bloqueaba la re-autorización. -
JWT en
Cf-Access-Jwt-Assertion: CF Access siempre inyecta este header en requests autenticados. El payload del JWT incluye el claimemailcuando la política tiene identidad de usuario.
Comportamiento
resolveActorEmail(request)
├── Lee header cf-access-authenticated-user-email
│ Si existe → devuelve email.toLowerCase()
└── Fallback: lee cf-access-jwt-assertion
Si existe → decodeJwtPayload() → payload.email.toLowerCase()
Si no → null
Seguridad del decode sin validar firma
La función auxiliar decodeJwtPayload decodifica el payload Base64url sin verificar la firma. Esto es seguro en este contexto porque:
- El middleware de la aplicación (
_middleware.ts) ya ha validado la presencia y autenticidad del JWT antes de que la request llegue al handler. - CF Access es el único emisor del JWT y solo lo entrega a sesiones válidas.
decodeJwtPayloadse usa solo para extracción de identidad, no para control de acceso.
Firma
export function resolveActorEmail(request: Request): string | null
| Retorno | Condición |
|---|---|
string (email en minúsculas) | Header presente O JWT con claim email válido |
null | Sin header y sin JWT, o JWT malformado |
Función auxiliar
function decodeJwtPayload(jwt: string): Record<string, unknown> | null
Privada (no exportada). Realiza:
- Split del JWT en 3 partes.
- Conversión Base64url → Base64 estándar (sustitución
-→+,_→/). - Padding con
===según longitud. atob()+JSON.parse().- Devuelve
nullen cualquier error.
Consumidores
| Función/Módulo | Uso |
|---|---|
resolveTeamMember(request) | Usa resolveActorEmail internamente para mapear email → TeamMember |
oauth/zoho/start.ts | Llama directamente para obtener cfAccessEmail (auditoría + modo admin) |
Ubicación
functions/_lib/staff.ts
El módulo _lib/staff.ts es el único mapping canónico CF Access email → TeamMember del proyecto. Todas las integraciones que necesiten identificar al actor deben pasar por este módulo, no leer el header directamente.
Historial
Introducida en commit e65dde4 (fix #46, 2026-05-17) como parte del fix del panel Zoho que mostraba “sesión actual: desconocida”.
Véase también
- [[feature—zoho—admin-oauth-proxy]]
- [[feature—zoho—oauth-per-user]]
- [[decision—20260517—integracion-zoho-calendar-mail-pivot]]