Inventory System API routes are protected by Neon Auth session cookies. When a user signs in, Neon Auth sets an encrypted session cookie in the browser. Every subsequent request to anDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/juadariasmar/inventory_project/llms.txt
Use this file to discover all available pages before exploring further.
/api/* route carries that cookie automatically, and the route handler validates it server-side before touching any data. There are no long-lived API keys or JWT Bearer tokens — the session cookie is the single credential for all API access.
How authentication works
User signs in
The user authenticates through the Neon Auth sign-in flow at
/auth/sign-in. On success, Neon Auth sets an encrypted session cookie scoped to your domain. The cookie secret is configured via NEON_AUTH_COOKIE_SECRET in your environment.Request arrives at an API route
The browser (or server-side caller) includes the session cookie with every request. The API route handler calls
obtenerSesion() to decode and validate the session.Session is enriched with database user
obtenerSesion() looks up the Usuario record in Postgres by neonAuthId (or falls back to email). The returned session object includes the user’s empresaId, rol, permisos, and estado — everything needed for authorization decisions.obtenerSesion()
obtenerSesion() is the primary session accessor, defined in src/lib/permisos.ts. It is memoized with React cache() so it runs at most once per request regardless of how many route segments call it.
obtenerSesion() performs the following steps:
Get the Neon Auth session
Calls
auth.getSession() from the Neon Auth server SDK. Returns null immediately if no valid session cookie is present.Look up the Usuario record
Queries
prisma.usuario.findUnique({ where: { neonAuthId } }). If not found by neonAuthId, it falls back to a lookup by email and backfills the neonAuthId column.Auto-provision on first sign-in
If no
Usuario record exists at all, the function creates a new Empresa and an ADMIN Usuario in a single sequence. This is a safety net for cases where the Neon Auth webhook did not fire before the first API call.Auto-provisioning can be restricted to an allowlist of email addresses by setting the
ALLOWLIST_REGISTRO environment variable to a comma-separated list of permitted emails. Addresses not on the list (and not in ADMIN_EMAILS) will receive a null session and a 401 response.validarAccesoEmpresa()
validarAccesoEmpresa() is a convenience guard called at the top of protected route handlers. It wraps obtenerSesion() and throws an AppError with the correct HTTP status if any check fails.
| Condition | Failure response |
|---|---|
Session exists and user is present | 401 Unauthorized — “No autorizado” |
User estado is ACTIVO | 403 Forbidden — “Usuario inactivo o suspendido” |
User has a non-null empresaId | 403 Forbidden — “Usuario sin empresa asignada” |
{ usuarioId, empresaId, rol, usuario }, which is all a route handler needs to scope its queries correctly.
Role and permission checks
Beyond session validity, individual routes enforce role and permission requirements.esAdmin()
Returns
true if the active user has rol === 'ADMIN' or rol === 'SUPER_ADMIN'. Used to gate write operations — for example, POST /api/productos requires esAdmin().tienePermiso(permiso)
Checks whether an
EMPLEADO user holds a specific granular Permiso flag (e.g. REALIZAR_VENTAS). ADMIN and SUPER_ADMIN users always return true from this check.POST /api/ventas:
Rate limiting
Themiddleware.ts file applies a sliding-window rate limiter to all POST requests on mutation endpoints before they reach the route handler. The limiter uses Upstash Redis when UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN are configured, and falls back to an in-process Map otherwise.
Settings: 5 POST requests per minute per IP (sliding window).
The client IP is resolved in priority order:
x-forwarded-forheader (first value)x-real-ipheader- Falls back to the string
"unknown"
Rate-limited endpoints
The followingPOST paths are covered by the middleware rate limiter:
reset timestamp (or from the in-memory window expiry as a fallback).
Calling protected endpoints
From a browser
Browsers include the session cookie automatically whencredentials: 'include' is set on the fetch call:
From server-side Next.js
When making requests from a Server Component or Route Handler, forward the incoming request cookies so the downstream API route can reconstruct the session:Middleware route coverage
The Next.js middleware runs on all paths matched by theconfig.matcher pattern, which covers every route except:
_next/static— static assets_next/image— image optimization responsesfavicon.icoauth/— public authentication pages (sign-in, password reset)invitacion— public invitation acceptance pagesitemap.xmlandrobots.txt— SEO files
/api/*) pass through the middleware for rate limiting, but are not redirected to sign-in on missing session — that is the responsibility of each route handler via obtenerSesion() or validarAccesoEmpresa(). Page routes (everything else) are passed through auth.middleware({ loginUrl: '/auth/sign-in' }), which redirects unauthenticated visitors to the sign-in page automatically.