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
- 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.
- 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.
- 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.
- JWT payload — The token carries
{ userId, role, tenantId, email, expiresAt }. tenantId is null for plain user accounts and for the global superadmin.
- Cookie storage —
createSession() writes the signed token into the session cookie with httpOnly: true, sameSite: "lax", path: "/", and secure: true in production, expiring after 7 days.
- 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().
| Action | Accepted roles | Redirects to |
|---|
loginUser | user | / |
loginFacultyAdmin | tenant_admin, event_manager, access_control | /faculty-admin |
loginSuperAdmin | superadmin | /admin |
unifiedLoginAction | delegates based on roleType form field | varies |
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.
| Action | Redirects 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
| Variable | Description |
|---|
JWT_SECRET | HMAC-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:
| Role | Description |
|---|
user | Standard attendee / student account |
tenant_admin | Full administrative access within a faculty tenant |
event_manager | Can create and manage events within their tenant |
access_control | Can scan tickets and verify attendee check-in |
superadmin | Global 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");
}