Skip to content

Autenticación

Endpoints de autenticación para registro, inicio de sesión y gestión de perfiles de usuario.

Los siguientes modelos de Prisma pertenecen al schema auth:

model User {
id String @id @default(cuid())
email String @unique
emailVerified DateTime? @map("email_verified")
accounts Account[] @relation("UserAccounts")
Authenticator Authenticator[]
profile UserProfile?
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("users")
@@schema("auth")
}
model Account {
id String @id @default(cuid())
userId String @map("user_id")
type String
provider String
providerAccountId String @map("provider_account_id")
refresh_token String? @map("refresh_token") @db.Text
access_token String? @map("access_token") @db.Text
id_token String? @map("id_token") @db.Text
session_state String? @map("session_state")
scope String?
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade, name: "UserAccounts")
@@unique([provider, providerAccountId])
@@map("accounts")
@@schema("auth")
}
model Authenticator {
credentialID String @unique
userId String
providerAccountId String
credentialPublicKey String
counter Int
credentialDeviceType String
credentialBackedUp Boolean
transports String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@id([userId, credentialID])
@@schema("auth")
}
enum UserRole {
ADMIN
STAFF
TRAINER
MEMBER
@@schema("auth")
}

Registrar un nuevo usuario en el sistema.

  • URL: /api/v1/auth/register
  • Método: POST
  • Autenticación Requerida: No

Este endpoint realiza las siguientes operaciones:

  1. Hashea la contraseña usando Bun.password.hash para almacenamiento seguro
  2. Crea el usuario y perfil en una transacción atómica de Prisma:
    • Crea el registro User con el email proporcionado
    • Crea automáticamente el UserProfile asociado con nombre, tipo de documento y número de documento
    • El rol por defecto es MEMBER
  3. Crea la cuenta de credenciales en la tabla Account con:
    • type: “credentials”
    • provider: “credentials”
    • access_token: contraseña hasheada
  4. Retorna el usuario y perfil creados (sin la contraseña)

Nota: La verificación por email está comentada actualmente pero preparada para enviar un enlace de verificación.

{
"email": "user@example.com",
"password": "password123",
"documentType": "CC",
"document": "123456789",
"name": "John Doe"
}
{
"message": "Usuario registrado exitosamente",
"data": {
"user": {
"id": "cm...",
"email": "user@example.com"
},
"profile": {
"id": "cm...",
"role": "USER"
}
}
}
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.auth.register.$post({
json: {
email: 'user@example.com',
password: 'password123',
documentType: 'CC',
document: '123456789',
name: 'John Doe'
}
})
if (res.ok) {
const data = await res.json()
console.log(data.message)
}

Autenticar un usuario y obtener tokens de acceso.

  • URL: /api/v1/auth/login
  • Método: POST
  • Autenticación Requerida: No

Este endpoint realiza las siguientes operaciones:

  1. Busca la cuenta de credenciales del usuario por email en la tabla Account
  2. Verifica la contraseña usando Bun.password.verify comparando con el hash almacenado
  3. Genera tokens JWT:
    • Access Token: contiene sub (userId), email, role y pid (profileId). Expira según TOKEN_EXPIRATION
    • Refresh Token: contiene solo sub (userId). Expira según REFRESH_TOKEN_EXPIRATION
  4. Retorna los tokens junto con la información básica del usuario y perfil
  • 401 Unauthorized: Credenciales inválidas
{
"email": "user@example.com",
"password": "password123"
}
{
"message": "Login exitoso",
"data": {
"accessToken": "eyJ...",
"refreshToken": "eyJ...",
"user": { ... }
}
}
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.auth.login.$post({
json: {
email: 'user@example.com',
password: 'password123'
}
})

Obtener el perfil del usuario actualmente autenticado.

  • URL: /api/v1/auth/profile
  • Método: GET
  • Autenticación Requerida:

Este endpoint realiza las siguientes operaciones:

  1. Obtiene el userId del token JWT en el contexto de la petición
  2. Consulta el perfil completo del usuario incluyendo todas las relaciones:
    • Membresía activa (membership)
    • Perfil de entrenador si aplica (trainer)
    • Rachas activas (streaks)
    • Logros obtenidos con detalles (userAchievements.achievement)
    • Objetivos personales (goals)
    • Meta nutricional (nutritionGoal)
    • Seguidores y seguidos (followers, follows)
    • Consumo de agua (waterIntake)
    • Likes dados (likes)
    • Actividades diarias (dailyActivities)
  3. Retorna toda la información del perfil con las relaciones anidadas
{
"data": {
"id": "...",
"userId": "...",
"name": "John Doe",
...
}
}
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.auth.profile.$get()

Actualizar el perfil del usuario actualmente autenticado.

  • URL: /api/v1/auth/profile
  • Método: PATCH
  • Autenticación Requerida:

Este endpoint realiza las siguientes operaciones:

  1. Obtiene el userId del token JWT en el contexto
  2. Actualiza el UserProfile con los campos proporcionados:
    • Convierte dateOfBirth a objeto Date si se proporciona
    • Campos actualizables: name, phone, avatar_url, dateOfBirth, gender, etc.
  3. Verifica completitud del perfil:
    • Si profileComplete es false, evalúa si todos los campos requeridos están llenos
    • Si el perfil está completo, actualiza profileComplete a true
  4. Retorna el perfil actualizado
{
"name": "Jane Doe",
"phone": "1234567890"
}
{
"message": "Perfil actualizado exitosamente",
"data": { ... }
}
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.auth.profile.$patch({
json: {
name: 'Jane Doe'
}
})

Refrescar el token de acceso usando un token de refresco válido (pasado via cookie).

  • URL: /api/v1/auth/refresh
  • Método: GET
  • Autenticación Requerida: No (usa cookie de Refresh Token)

Este endpoint realiza las siguientes operaciones:

  1. Extrae el refresh token de la cookie de la petición
  2. Verifica el token usando el AUTH_SECRET y extrae el sub (userId)
  3. Busca el usuario en la base de datos con su email, rol y profileId
  4. Genera nuevos tokens:
    • Nuevo Access Token con la información actualizada del usuario
    • Nuevo Refresh Token
  5. Retorna ambos tokens nuevos
  • 401 Unauthorized: Token de refresco inválido o expirado
{
"message": "Tokens refrescados exitosamente",
"data": {
"accessToken": "...",
"refreshToken": "..."
}
}
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.auth.refresh.$get()

Cerrar sesión del usuario y limpiar cookies de autenticación.

  • URL: /api/v1/auth/logout
  • Método: POST
  • Autenticación Requerida:

Este endpoint realiza las siguientes operaciones:

  1. Invalida la sesión del usuario actual
  2. Limpia las cookies de autenticación (access token y refresh token)
  3. Retorna confirmación del cierre de sesión
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.auth.logout.$post()

Solicitar un enlace para restablecer la contraseña.

  • URL: /api/v1/auth/forgot-password
  • Método: POST
  • Autenticación Requerida: No

Este endpoint realiza las siguientes operaciones:

  1. Busca el usuario por email en la base de datos
  2. Verifica el email - Si el email no ha sido verificado, lanza error (requiere contactar soporte)
  3. Genera token de reset válido por 5 minutos usando JWT
  4. Construye el enlace de restablecimiento: {FRONTEND_URL}/reset-password?token={resetToken}
  5. Envía email al usuario con el enlace de restablecimiento
  • 400 Bad Request: El email no ha sido verificado
  • 404 Not Found: Usuario no encontrado
{
"email": "user@example.com"
}
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.auth['forgot-password'].$post({
json: {
email: 'user@example.com'
}
})

Restablecer contraseña usando el token proporcionado.

  • URL: /api/v1/auth/reset-password
  • Método: POST
  • Autenticación Requerida: No

Este endpoint realiza las siguientes operaciones:

  1. Verifica el token JWT y extrae el sub (userId)
  2. Hashea la nueva contraseña usando Bun.password.hash
  3. Busca la cuenta de credenciales del usuario
  4. Actualiza el access_token (contraseña hasheada) en la tabla Account
  5. Retorna confirmación del cambio
  • 400 Bad Request: Token inválido o expirado
{
"token": "reset_token_here",
"newPassword": "newpassword123"
}
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.auth['reset-password'].$post({
json: {
token: 'reset_token',
newPassword: 'newpassword123'
}
})