Inventory System delegates all authentication to Neon Auth — not NextAuth or a custom credential system. Neon Auth manages the full identity lifecycle: sign-up, sign-in, password resets, magic links, and OTPs. The application only stores a lightweightDocumentation 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.
Usuario record that mirrors the Neon Auth identity and enriches it with tenant membership, role, and permission data.
How Neon Auth works
Neon Auth is initialised once insrc/lib/auth/server.ts using @neondatabase/auth/next/server and reused across every server component and API route.
@neondatabase/auth/next exposes a thin React client used to drive the sign-in UI:
| Concern | Implementation |
|---|---|
| Session storage | Signed, lax HTTP-only cookie encrypted with NEON_AUTH_COOKIE_SECRET |
| Session retrieval (server) | auth.getSession() — called inside obtenerSesion() on every authenticated request |
| Sign-in UI | Provided by @neondatabase/auth-ui at /auth/sign-in |
| Middleware redirect | Unauthenticated requests are redirected to /auth/sign-in by auth.middleware() |
In production, both
NEON_AUTH_BASE_URL and NEON_AUTH_COOKIE_SECRET are required. The server throws an error at startup if either is missing. During the Next.js build phase (NEXT_PHASE === 'phase-production-build') placeholder values are used so the build does not fail.Webhook integration
Neon Auth publishes lifecycle events to your application via HTTP webhooks. Inventory System registers the endpointPOST /api/webhooks/neon to receive those events.
NEON_WEBHOOK_SECRET. Requests with an invalid signature return 401 before any business logic runs.
User sync
When Neon Auth firesuser.created, the webhook handler creates a matching Usuario row in Neon Postgres and, if the user signed up without an invitation, provisions a new Empresa for them.
If the webhook does not arrive in time (e.g. a network delay), obtenerSesion() has a built-in fallback: on the very first authenticated page request it checks whether a Usuario exists for the neonAuthId returned by auth.getSession(). If none exists, it creates the Empresa and Usuario automatically and logs a warning:
If
ALLOWLIST_REGISTRO is set in the environment, the fallback auto-creation is restricted to the comma-separated list of emails in that variable. Emails not on the list (and not in ADMIN_EMAILS) have their auto-creation blocked and the session resolves to null.Middleware protection
src/middleware.ts runs on every request matched by the Next.js matcher. Its two responsibilities are rate limiting and authentication gating.
| Pattern | Reason |
|---|---|
/auth/* | Public sign-in, sign-up, password-reset pages |
/invitacion* | Invitation acceptance landing page (token in URL) |
/_next/static, /_next/image | Static asset pipeline |
/favicon.ico, /sitemap.xml, /robots.txt | SEO and browser files |
/api/* | API routes handle their own auth via obtenerSesion() |
auth.middleware({ loginUrl: '/auth/sign-in' }), which redirects unauthenticated users to the sign-in page.
Rate limiting
SensitivePOST endpoints are rate-limited to 5 requests per minute per IP address to prevent brute-force and abuse. When Upstash Redis credentials (UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN) are configured, the limiter uses a sliding window counter in Redis. Without Redis, an in-memory Map serves as a fallback (suitable for local development).
Rate-limited endpoints include:
429 and a Retry-After-equivalent wait time in the message.
Admin email auto-promotion
Any email listed in theADMIN_EMAILS environment variable is automatically promoted to the SUPER_ADMIN role on login. This happens inside obtenerSesion() after the Usuario record is resolved:
esEmailAdministrador() reads the ADMIN_EMAILS environment variable, splits it on commas, and performs a case-insensitive comparison:
ADMIN_EMAILS in .env is the recommended way to bootstrap the first platform administrator without a database migration.
Invitation flow
Users can be added to an existing company through a token-based invitation flow instead of self-registration.Admin sends invitation
An
ADMIN calls POST /api/invitaciones with the invitee’s email and an optional role (ADMIN or USUARIO). The request is authenticated and scoped to the admin’s own company. A unique Invitacion record is written to the database with a 7-day expiry, and an email containing the token link is dispatched via Resend.Invitee receives email
The invitee receives a transactional email with a unique link containing the invitation token. The link points to the public
/invitacion?token=<token> page.Token validation
When the invitee opens the link, the frontend calls
GET /api/invitaciones/validar?token=<token>. The endpoint uses InvitacionesService.obtenerPorToken() to verify that the invitation exists, is in PENDIENTE state, and has not expired. If the token has passed its expiraEn date the state is updated to EXPIRADA and the endpoint returns an error.User completes registration
After authenticating with Neon Auth, the frontend calls
POST /api/invitaciones/aceptar with the token and the Neon Auth identity:User record created and linked
InvitacionesService.aceptar() validates the token one final time, then atomically creates the Usuario record (with estado: ACTIVO and the role specified in the invitation) and marks the Invitacion as ACEPTADA with an aceptadaEn timestamp. The new user is immediately active inside the inviting company.Invitation states
PENDIENTE
Default state. The invitation has been sent and is awaiting acceptance. Valid until
expiraEn.ACEPTADA
The invitee successfully registered and the
Usuario record has been created.EXPIRADA
The invitation was not accepted before its 7-day expiry. Set automatically when the token is validated after the deadline.
CANCELADA
An admin cancelled the invitation before it was accepted via
DELETE /api/invitaciones/[id].User states after registration
| State | Meaning |
|---|---|
PENDIENTE | User self-registered (not via invitation). An admin must approve the account before the user can operate. |
ACTIVO | Account is approved. The user can sign in and use the application normally. |
SUSPENDIDO | Account has been manually blocked by an admin. Sign-in succeeds but all protected routes return 403. |
Environment variables
The base URL of your Neon Auth project, e.g.
https://your-project.neonauth.tech/dbname/auth.A long, random secret used to sign the session cookie. Rotate this value to invalidate all active sessions.
The signing secret provided by Neon Auth for webhook signature verification. Set this in the Neon Auth dashboard and copy the value here.
Comma-separated list of email addresses that are automatically promoted to
SUPER_ADMIN on login. Example: [email protected],[email protected].REST URL of an Upstash Redis database. Required for persistent, cross-instance rate limiting in production.
Authentication token for the Upstash Redis database.
Optional comma-separated list of emails allowed to self-register. When set, all other emails are blocked from the fallback auto-creation path unless they appear in
ADMIN_EMAILS.