Saltar al contenido principal

Sistema de Usuarios por Tenant

Descripción

Sistema de autenticación y autorización para usuarios dentro de cada tenant. Cada tenant tiene su propia tabla de usuarios aislada, separada de los admin_users globales.

Arquitectura

Schema de Usuarios

Cada tenant tiene dos tablas en su schema:

  1. users: Usuarios del tenant
  2. user_sessions: Sesiones activas de usuarios
-- tenant_{slug}.users
CREATE TABLE users (
id UUID PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
password TEXT NOT NULL, -- bcrypt hash
name TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'member',
is_active BOOLEAN DEFAULT true,
permissions JSONB DEFAULT '{}',
metadata JSONB DEFAULT '{}',
last_login_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);

-- tenant_{slug}.user_sessions
CREATE TABLE user_sessions (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
token TEXT UNIQUE NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);

Roles

RoleDescripciónPermisos por defecto
ownerDueño del tenantFull control
adminAdministradorGestionar usuarios y settings
memberMiembroCrear y editar records
viewerObservadorSolo lectura

Permisos

Los permisos se almacenan en el campo permissions (JSONB):

{
"entities": {
"products": ["create", "read", "update", "delete"],
"invoices": ["read", "update"],
"customers": ["read"]
},
"canManageUsers": false,
"canManageSettings": false
}

API REST

Crear Usuario

POST /auth/tenant/users
Authorization: Bearer {admin_token}

{
"email": "user@example.com",
"password": "secure123",
"name": "John Doe",
"role": "member",
"permissions": {
"entities": {
"products": ["create", "read", "update"]
}
},
"metadata": {
"department": "Sales",
"position": "Sales Rep"
}
}

Response:

{
"success": true,
"data": {
"id": "uuid",
"email": "user@example.com",
"name": "John Doe",
"role": "member",
"permissions": {},
"metadata": {},
"createdAt": "2026-02-03T..."
}
}

Login de Usuario

POST /auth/tenant/login
Authorization: Bearer {admin_token}

{
"email": "user@example.com",
"password": "secure123"
}

Response:

{
"success": true,
"data": {
"token": "uuid-session-token",
"user": {
"id": "uuid",
"email": "user@example.com",
"name": "John Doe",
"role": "member"
}
}
}

Usar el Token

Una vez que el usuario tiene un token, puede usarlo de dos formas:

Opción 1: Authorization Header

GET /api/entities/products/records
Authorization: Bearer {user_token}
X-Tenant-ID: fyso

Opción 2: X-API-Key Header (Recomendado para APIs externas)

GET /api/entities/products/records
X-API-Key: {user_token}
X-Tenant-ID: fyso

Otros Endpoints

# Logout
POST /auth/tenant/logout
X-API-Key: {user_token}

# Obtener info del usuario actual
GET /auth/tenant/me
X-API-Key: {user_token}

# Listar usuarios
GET /auth/tenant/users
Authorization: Bearer {admin_token}

# Obtener usuario por ID
GET /auth/tenant/users/{id}
Authorization: Bearer {admin_token}

# Actualizar usuario
PUT /auth/tenant/users/{id}
Authorization: Bearer {admin_token}
{
"name": "New Name",
"role": "admin",
"permissions": {}
}

# Cambiar contraseña
PUT /auth/tenant/users/{id}/password
Authorization: Bearer {admin_token}
{
"password": "newpass123"
}

# Eliminar usuario
DELETE /auth/tenant/users/{id}
Authorization: Bearer {admin_token}

MCP Tool

create_user

Crea un usuario en el tenant desde Claude Desktop.

// Ejemplo
create_user({
tenantSlug: "fyso",
email: "sales@company.com",
password: "secure123",
name: "Sales Manager",
role: "admin",
permissions: {
entities: {
products: ["create", "read", "update", "delete"],
customers: ["create", "read", "update"],
invoices: ["read"]
},
canManageUsers: true,
canManageSettings: false
},
metadata: {
department: "Sales",
position: "Manager",
phone: "+1234567890"
}
})

Response:

{
"success": true,
"user": {
"id": "uuid",
"email": "sales@company.com",
"name": "Sales Manager",
"role": "admin"
},
"loginInfo": {
"endpoint": "POST /auth/tenant/login",
"body": {
"email": "sales@company.com",
"password": "(provided)"
},
"headers": {
"X-Tenant-ID": "fyso"
}
}
}

Middleware

El middleware requireTenantContext ahora soporta ambos:

  1. Admin tokens (flujo original)

    • Token en cookie o Authorization header
    • Tenant ID en el JWT
  2. Tenant user tokens (nuevo)

    • Token en X-API-Key o Authorization header
    • Tenant slug en X-Tenant-ID header
    • Valida sesión en la tabla user_sessions del tenant

El middleware automáticamente:

  • Detecta el tipo de token
  • Valida la sesión
  • Establece el contexto (userId, userRole, userPermissions, db)
  • Configura el search_path al schema del tenant

Flujo Completo

1. Admin crea el tenant

POST /auth/tenants
Authorization: Bearer {admin_token}

{
"name": "ACME Corp",
"description": "..."
}

2. Admin crea usuarios en el tenant

POST /auth/tenant/users
Authorization: Bearer {admin_token} (con tenant seleccionado)

{
"email": "owner@acme.com",
"password": "secure123",
"name": "Owner User",
"role": "owner"
}

3. Usuario se loguea

POST /auth/tenant/login
Authorization: Bearer {admin_token}

{
"email": "owner@acme.com",
"password": "secure123"
}

# Retorna user_token

4. Usuario usa la API

GET /api/entities/products/records
X-API-Key: {user_token}
X-Tenant-ID: acme-corp

Seguridad

Passwords

  • Hasheados con bcrypt (10 rounds)
  • Nunca se retornan en las respuestas
  • Mínimo 8 caracteres

Tokens

  • UUIDs generados con crypto.randomUUID()
  • Válidos por 7 días
  • Almacenados en tabla user_sessions
  • Se eliminan al logout

Aislamiento

  • Usuarios aislados por schema del tenant
  • No hay acceso cross-tenant
  • Middleware valida tenant antes de ejecutar queries

Permisos

  • Roles con permisos predefinidos
  • Permisos custom por entity
  • Validación en cada request (futuro)

Testing

Ejecutar tests:

cd packages/api
bun test tenant-users.test.ts

Tests incluidos:

  • TU1: Crear usuario
  • TU2: Login
  • TU3: Get current user
  • TU4: List users
  • TU5: Update user
  • TU6: Access records with user token
  • TU7: Update password
  • TU8: Logout
  • TU9: Delete user

Migración de Tenants Existentes

Para tenants creados antes de este feature, correr:

cd packages/db
bun src/scripts/add-users-to-tenants.ts

Este script agregará las tablas users y user_sessions a todos los tenants existentes.

Creado con Fyso