Sistema de Autenticación
Cannahub utiliza un sistema de autenticación avanzado basado en JWT tokens con soporte para múltiples roles de usuario y arquitectura multi-tenant. Esta guía explica cómo funciona la autenticación y cómo implementar diferentes flujos de acceso.
Arquitectura de Autenticación
Cannahub implementa un sistema de autenticación de múltiples capas que soporta diferentes tipos de usuarios y organizaciones:
Flujo de Autenticación
Tipos de Autenticación por Rol
1. Autenticación de Miembros (Customers)
Los miembros utilizan el flujo estándar de registro y login:
Login de Miembro
curl -X POST https://api.cannahub.tech/auth/customer/emailpass \
-H "Content-Type: application/json" \
-d '{
"email": "miembro@ejemplo.com",
"password": "contraseña_segura"
}'
2. Autenticación de Administradores
Los administradores y staff utilizan un endpoint separado:
Login de Admin
curl -X POST https://api.cannahub.tech/auth/user/emailpass \
-H "Content-Type: application/json" \
-d '{
"email": "admin@club.com",
"password": "admin_password"
}'
Gestión de Tokens JWT
Estructura de Tokens
Cannahub utiliza dos tipos de tokens JWT almacenados como cookies HTTP-only:
Configuración de Tokens
{
"adminToken": {
"name": "cannahubAdminToken",
"expires": 604800, // 7 días en segundos
"httpOnly": true,
"secure": true,
"sameSite": "lax"
},
"memberToken": {
"name": "cannahubToken",
"expires": 604800, // 7 días en segundos
"httpOnly": true,
"secure": true,
"sameSite": "lax"
}
}
Payload del Token
Estructura del JWT
{
"sub": "user_id_123",
"email": "usuario@ejemplo.com",
"role": "member",
"organization": "high-up",
"iat": 1634567890,
"exp": 1635172690
}
El campo role utiliza valores en minúsculas: root, admin, member, staff, doctor, etc.
Roles de Usuario y Permisos
Jerarquía de Roles
{
"root": {
"level": 0,
"description": "Super administrador de plataforma",
"access": "Acceso completo a todas las funcionalidades"
},
"admin": {
"level": 1,
"description": "Administrador de club",
"access": "Gestión de miembros, pedidos, productos y analytics"
},
"doctor": {
"level": 2,
"description": "Profesional médico",
"access": "Gestión de pacientes, prescripciones y consultas"
},
"member": {
"level": 3,
"description": "Miembro del club",
"access": "Acceso al dispensario, crear pedidos, actualizar perfil"
}
}
Middleware de Protección de Rutas
El middleware de Next.js intercepta todas las solicitudes antes de que lleguen a las páginas, validando autenticación, organización y permisos.
Diagrama del Middleware
Rutas Excluidas de Autenticación
Las siguientes rutas no requieren autenticación:
Rutas Públicas
const excludedPaths = [
'/login',
'/register',
'/onboarding',
'/forgot-password',
'/api/health',
'/_next',
'/favicon.ico',
'/images',
]
Validación en el Middleware
Middleware de Autenticación
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl
// 1. Validación de organización
const organization = extractOrganization(request)
if (!organization || !isValidOrganization(organization)) {
return redirectTo404(request)
}
// 2. Extracción de tokens
const memberToken = request.cookies.get('cannahubToken')?.value
const adminToken = request.cookies.get('cannahubAdminToken')?.value
// 3. Rutas excluidas de autenticación
if (isExcludedPath(pathname)) {
return NextResponse.next()
}
// 4. Validación de tokens
const token = adminToken || memberToken
if (!token) {
return redirectToLogin(request, organization)
}
// 5. Verificación del perfil
const profile = await verifyTokenAndGetProfile(token)
if (!profile) {
return redirectToLogin(request, organization)
}
// 6. Verificación de completitud del perfil
if (!profile.isComplete) {
return redirectToOnboarding(request, organization)
}
// 7. Control de acceso basado en roles
if (!hasPermission(profile.role, pathname)) {
return redirectToUnauthorized(request)
}
return NextResponse.next()
}
Para información detallada sobre el proceso de registro de nuevos miembros, estados y endpoints, consulta la documentación de Onboarding.
Integración Multi-Tenant
Validación de Organización
Validación de Organización
const supportedOrganizations = [
'high-up',
'don-marcelino',
'circulo-rojo',
'superfly',
'blow',
'weplant'
]
function extractOrganization(request: NextRequest): string | null {
// Extraer de subdomain, header, o path
const host = request.headers.get('host')
const subdomain = host?.split('.')[0]
if (supportedOrganizations.includes(subdomain)) {
return subdomain
}
// Fallback a path-based org
const pathOrg = request.nextUrl.pathname.split('/')[1]
return supportedOrganizations.includes(pathOrg) ? pathOrg : null
}
Seguridad y Mejores Prácticas
Características de Seguridad
- Cookies HTTP-Only: Previenen ataques XSS al no permitir acceso desde JavaScript
- Protección CSRF: Integrada en Next.js
- Expiración de Tokens: Los tokens expiran después de 7 días
- Aislamiento Multi-Tenant: Separación completa de datos por organización
- Control de Acceso por Roles: Acceso basado en el rol del usuario
Comportamiento de Sesión
Cuando un token expira después de 7 días:
- El usuario es redirigido automáticamente a
/login - Las cookies de sesión son eliminadas
- El usuario debe volver a iniciar sesión para continuar
Actualmente no hay renovación automática de tokens. Los usuarios deben volver a iniciar sesión cuando su token expire.
Variables de Entorno de Seguridad
Configuración de Seguridad
# JWT Configuration
JWT_SECRET=tu_clave_secreta_muy_larga_y_segura
COOKIE_DOMAIN=.tudominio.com
# Session Configuration
SESSION_TIMEOUT=604800 # 7 días en segundos
# Security Headers
FORCE_HTTPS=true
SECURE_COOKIES=true
SAME_SITE_POLICY=lax
# Development Override (NUNCA en producción)
BYPASS_AUTH=false
Manejo de Errores de Autenticación
Códigos de Error Comunes
Respuestas de Error
{
"unauthorized": {
"code": 401,
"message": "Credenciales inválidas o token expirado",
"action": "Redirigir a /login"
},
"forbidden": {
"code": 403,
"message": "No tienes permiso para acceder a este recurso",
"action": "Mostrar página de acceso denegado"
},
"profile_incomplete": {
"code": 302,
"message": "Perfil incompleto",
"action": "Redirigir a /onboarding"
}
}
Los errores siguen el formato estándar de Medusa. El código 401 indica que el usuario debe volver a autenticarse.
Testing y Desarrollo
Modo de Desarrollo
Para facilitar el desarrollo, Cannahub incluye un bypass de autenticación. Ambas variables deben estar activas para que funcione:
Variables de Desarrollo
# Solo para desarrollo local - AMBAS son requeridas
BYPASS_AUTH=true
IS_MOCKED=true
Cuando ambas están activas:
- El middleware omite la validación de tokens
- Se utiliza un usuario mock predefinido
- Las rutas protegidas son accesibles sin login
Importante: Nunca uses estas variables en producción. BYPASS_AUTH=true solo funciona cuando IS_MOCKED=true también está activo.
Recuperación de Contraseña
Actualmente no existe un flujo de recuperación de contraseña self-service. Si un usuario olvida su contraseña:
- Debe contactar al administrador del club
- El administrador puede resetear la contraseña desde el panel de gestión de miembros
- El usuario recibirá instrucciones para establecer una nueva contraseña
La implementación del sistema de autenticación de Cannahub proporciona seguridad robusta mientras mantiene la flexibilidad necesaria para soportar múltiples organizaciones y roles de usuario complejos.