Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Edfermachado/proyectoSistemas/llms.txt

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

UniEvents uses a stateless JWT-based authentication model. When a user logs in, the server signs a JSON Web Token with an HMAC-SHA256 key and writes it into an HTTP-only session cookie. Every subsequent request to a protected route reads that cookie, verifies the signature, and extracts the session payload — no database round-trip required for session resolution. The approach is compatible with Next.js 16 Server Components and Server Actions because the cookies() API is used exclusively on the server side.

How It Works

  1. Credential submission — The user submits their email and password through one of the server actions (loginUser, loginFacultyAdmin, or loginSuperAdmin). All actions are marked "use server" and run exclusively on the server.
  2. Password verification — The server looks up the user record with Drizzle ORM and compares the submitted password against the stored bcrypt hash using bcrypt.compare.
  3. JWT creation — On a successful match, createSession() calls the encrypt() helper, which uses jose’s SignJWT to produce an HS256-signed token valid for 7 days.
  4. JWT payload — The token carries { userId, role, tenantId, email, expiresAt }. tenantId is null for plain user accounts and for the global superadmin.
  5. Cookie storagecreateSession() writes the signed token into the session cookie with httpOnly: true, sameSite: "lax", path: "/", and secure: true in production, expiring after 7 days.
  6. Session resolution — Every protected route calls getSession(), which reads the raw cookie value and passes it to decrypt(). decrypt() calls jwtVerify and returns the decoded payload, or null if the token is missing, tampered with, or expired.

Login Endpoints (Server Actions)

All login actions live in src/app/actions/auth.ts and follow the same pattern: validate inputs → query the database → verify the bcrypt hash → call createSession()redirect().
ActionAccepted rolesRedirects to
loginUseruser/
loginFacultyAdmintenant_admin, event_manager, access_control/faculty-admin
loginSuperAdminsuperadmin/admin
unifiedLoginActiondelegates based on roleType form fieldvaries

loginUser

Accepts accounts with role = 'user'. Intended for students and general public visitors registering for events.

loginFacultyAdmin

Accepts accounts whose role is one of tenant_admin, event_manager, or access_control. All three roles are scoped to a specific faculty tenant via tenantId.

loginSuperAdmin

Accepts accounts with role = 'superadmin'. The superadmin has no tenant affiliation (tenantId is null).

unifiedLoginAction

A single form entry point that reads the hidden roleType field from the submitted FormData. When roleType === "facultad" it delegates to loginFacultyAdmin; otherwise it delegates to loginUser. This allows a single login page to serve multiple audiences.
// src/app/actions/auth.ts (excerpt)
export async function unifiedLoginAction(prevState: any, formData: FormData) {
  const roleType = formData.get("roleType");
  if (roleType === "facultad") {
    return await loginFacultyAdmin(prevState, formData);
  } else {
    return await loginUser(prevState, formData);
  }
}

Logout Endpoints (Server Actions)

All logout actions live in src/app/actions/auth.ts. Each action calls deleteSession() to remove the session cookie and then redirects the caller to the appropriate login page.
ActionRedirects to
logoutUser/login
logoutFacultyAdmin/login
logoutAdmin/admin/login

logoutUser

Clears the session cookie and redirects standard user accounts to /login.

logoutFacultyAdmin

Clears the session cookie and redirects faculty admin users (tenant_admin, event_manager, access_control) to /login.

logoutAdmin

Clears the session cookie and redirects the superadmin to /admin/login.

Environment Variables

VariableDescription
JWT_SECRETHMAC-SHA256 signing key used by encrypt() and decrypt(). Falls back to "uni-events-super-secret-key-32-chars!!" when not set.
Always set JWT_SECRET in production. The development fallback value is public and must never be used in a live environment. Generate a cryptographically random secret of at least 32 characters — for example with openssl rand -base64 32 — and provide it as an environment variable on your hosting platform.

Role-based Authorization

After calling getSession(), route handlers and Server Components check session.role to determine whether the caller is permitted to proceed. Unauthorized callers are immediately redirected using Next.js’s redirect() function. The roles defined in the users table are:
RoleDescription
userStandard attendee / student account
tenant_adminFull administrative access within a faculty tenant
event_managerCan create and manage events within their tenant
access_controlCan scan tickets and verify attendee check-in
superadminGlobal platform administrator, not scoped to any tenant

auth.ts Helper Functions

The five helper functions exported from src/lib/auth.ts are the building blocks for every authenticated flow in the application.
import { SignJWT, jwtVerify } from "jose";
import { cookies } from "next/headers";

const secretKey = process.env.JWT_SECRET || "uni-events-super-secret-key-32-chars!!";
const encodedKey = new TextEncoder().encode(secretKey);

/** Signs a JWT payload with HS256 and a 7-day expiry. */
export async function encrypt(payload: any) {
  return new SignJWT(payload)
    .setProtectedHeader({ alg: "HS256" })
    .setIssuedAt()
    .setExpirationTime("7d")
    .sign(encodedKey);
}

/** Verifies a JWT string and returns its payload, or null if invalid/expired. */
export async function decrypt(session: string | undefined = "") {
  if (!session) return null;
  try {
    const { payload } = await jwtVerify(session, encodedKey, {
      algorithms: ["HS256"],
    });
    return payload;
  } catch (error) {
    return null;
  }
}

/** Reads the 'session' cookie and returns the decoded JWT payload. */
export async function getSession() {
  const cookieStore = await cookies();
  const sessionCookie = cookieStore.get("session")?.value;
  return await decrypt(sessionCookie);
}

/**
 * Signs a new JWT and writes it into the 'session' HTTP-only cookie.
 * tenantId is null for 'user' and 'superadmin' roles.
 */
export async function createSession(
  userId: string,
  role: string,
  tenantId?: string | null,
  email?: string
) {
  const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
  const session = await encrypt({ userId, role, tenantId, email, expiresAt });
  const cookieStore = await cookies();

  cookieStore.set("session", session, {
    httpOnly: true,
    secure: process.env.NODE_ENV === "production",
    expires: expiresAt,
    sameSite: "lax",
    path: "/",
  });
}

/** Deletes the 'session' cookie, effectively logging the user out. */
export async function deleteSession() {
  const cookieStore = await cookies();
  cookieStore.delete("session");
}

Build docs developers (and LLMs) love