Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Renzo717/Aula-Virtual-Universidad-Radiolocucion/llms.txt

Use this file to discover all available pages before exploring further.

The NuestraVoz API authenticates requests using an httpOnly session cookie (nv_session) that holds a Supabase JWT access token. When a user logs in successfully the server issues this cookie; the browser then attaches it automatically to every subsequent same-origin request. For cross-origin clients (such as a front-end running on a different port) you must explicitly opt in to sending credentials with each request.

Endpoints

POST /api/auth/register

Creates a new user account. Registration is open to estudiante and docente roles only — admin and preceptor accounts must be created by an administrator through POST /api/users.
Docente accounts are set to status: "pendiente" automatically upon registration. An administrator must activate the account before the teacher can log in. Student accounts are activated immediately (status: "activo").
Request body
nombre
string
required
Given name of the user. Minimum 2 characters.
apellido
string
required
Family name of the user. Minimum 2 characters.
email
string
required
A valid email address. Must be unique across the platform.
password
string
required
Account password. Minimum 6 characters.
rol
string
required
Role to assign. Accepted values: "estudiante" or "docente".
Success response — 201 Created
{
  "data": {
    "message": "Cuenta creada. Inicia sesión para continuar."
  }
}
Error responses
Statuserror valueCause
400"Datos inválidos"Zod validation failed — check details
400Supabase error messageEmail already in use or Supabase auth error
TypeScript example
const res = await fetch("http://localhost:3001/api/auth/register", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    nombre: "María",
    apellido: "González",
    email: "maria@universidad.edu",
    password: "segura123",
    rol: "estudiante",
  }),
});

const body = await res.json();

if (!res.ok) {
  // body.error contains the error message
  // body.details contains the Zod field-level errors (on 400)
  throw new Error(body.error);
}

// body.data.message → "Cuenta creada. Inicia sesión para continuar."
console.log(body.data.message);

POST /api/auth/login

Authenticates a user with email and password. On success, the response sets the nv_session cookie and returns the user’s profile data. This cookie must be included in all subsequent requests to protected endpoints. Request body
email
string
required
The user’s registered email address.
password
string
required
The user’s password. Minimum 6 characters.
remember
boolean
When true, the session cookie has a maxAge of 30 days. When false or omitted, the cookie expires after 1 day.
Success response — 200 OK The Set-Cookie response header sets nv_session with the Supabase JWT access token.
data
object
The authenticated user’s profile.
Success response body example
{
  "data": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "email": "maria@universidad.edu",
    "nombre": "María",
    "apellido": "González",
    "rol": "estudiante",
    "status": "activo",
    "avatar_url": null
  }
}
Cookie properties
PropertyValue
Namenv_session (configurable via SESSION_COOKIE_NAME env var)
httpOnlytrue — not accessible from JavaScript
securetrue in production, false in development
sameSitelax
path/
maxAge30 days if remember: true; 1 day otherwise
Error responses
Statuserror valueCause
400"Datos inválidos"Zod validation failed
401"Credenciales inválidas"Email or password is incorrect
403"Cuenta inactiva o pendiente de aprobación"Account status is "inactivo"
403"Tu cuenta de docente está pendiente de aprobación"Docente account awaiting admin activation
TypeScript example
interface AuthUser {
  id: string;
  email: string;
  nombre: string;
  apellido: string;
  rol: "admin" | "docente" | "estudiante" | "preceptor";
  status: "activo" | "pendiente" | "inactivo";
  avatar_url: string | null;
}

async function login(
  email: string,
  password: string,
  remember = false
): Promise<AuthUser> {
  const res = await fetch("http://localhost:3001/api/auth/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    // credentials: "include" sends and stores the Set-Cookie header
    credentials: "include",
    body: JSON.stringify({ email, password, remember }),
  });

  const body = await res.json();

  if (!res.ok) {
    throw new Error(body.error);
  }

  return body.data as AuthUser;
}

GET /api/auth/me

Returns the full profile of the currently authenticated user. Requires a valid nv_session cookie. Success response — 200 OK
data
object
Extended profile, including optional contact information and the last login timestamp.
TypeScript example
const res = await fetch("http://localhost:3001/api/auth/me", {
  credentials: "include",
});

if (res.status === 401) {
  // No active session — redirect to login
}

const { data } = await res.json();
console.log(`Logged in as ${data.nombre} ${data.apellido} (${data.rol})`);

POST /api/auth/logout

Clears the nv_session cookie, ending the session. No request body is required. Success response — 200 OK
{
  "data": {
    "message": "Sesión cerrada"
  }
}
TypeScript example
await fetch("http://localhost:3001/api/auth/logout", {
  method: "POST",
  credentials: "include",
});
// Cookie is now cleared; subsequent requests will receive 401

Making Authenticated Requests

Every request to a protected route must include the session cookie. How you do this depends on your HTTP client:
The cookie is sent automatically once credentials: "include" (or withCredentials: true) is set — you never need to read or pass the token manually.
Fetch API
const res = await fetch("http://localhost:3001/api/perfil", {
  credentials: "include", // <-- required
});
Axios
import axios from "axios";

const api = axios.create({
  baseURL: "http://localhost:3001",
  withCredentials: true, // <-- required
});

const { data } = await api.get("/api/perfil");
cURL (for testing)
# After logging in and saving the cookie jar:
curl -c cookies.txt -X POST http://localhost:3001/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"maria@universidad.edu","password":"segura123"}'

# Use the saved cookie on subsequent requests:
curl -b cookies.txt http://localhost:3001/api/auth/me

Middleware Reference

All protected routes in the NuestraVoz API are guarded by one or both of the following middleware functions, defined in apps/api/src/middleware/auth.ts.

requireAuth

Verifies that the incoming request carries a valid nv_session cookie, validates the JWT against Supabase, and fetches the user’s profile from the profiles table. If the check passes, it attaches a user object to req for use by downstream handlers.
// Shape of req.user after requireAuth succeeds
interface ReqUser {
  id: string;
  email: string;
  rol: "admin" | "docente" | "estudiante" | "preceptor";
  status: string;
}
Failure conditionHTTP statuserror value
Cookie absent401"No autenticado"
JWT invalid or expired401"Sesión inválida"
Profile not found or status === "inactivo"403"Cuenta inactiva"

requireRole(...roles)

A factory that returns middleware enforcing that req.user.rol is one of the accepted roles. Must be used after requireAuth in the middleware chain.
import { requireAuth, requireRole } from "./middleware/auth.js";

// Only admins may access this route
router.get(
  "/admin-only",
  requireAuth,
  requireRole("admin"),
  (req, res) => { ... }
);

// Both docentes and admins may access this route
router.post(
  "/grades",
  requireAuth,
  requireRole("docente", "admin"),
  (req, res) => { ... }
);
Failure conditionHTTP statuserror value
req.user missing (called without requireAuth)401"No autenticado"
User’s role not in the allowed list403"Sin permisos"
Always place requireAuth before requireRole in the middleware chain. Calling requireRole on a route that skips requireAuth will always return 401 because req.user will be undefined.
During local development you can inspect the decoded JWT payload by pasting the value of your nv_session cookie into jwt.io. The sub field will match the user’s UUID in both the Supabase auth.users table and the profiles table.

Build docs developers (and LLMs) love