Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Glemynart/SaaS/llms.txt

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

The login endpoint verifies a user’s identity within a specific tenant and issues a new JWT token pair on success. Because La Oficina Nítida is multi-tenant, a user is identified by the combination of their email address and their organization’s NIT — not by email alone. This allows the same email to exist in different organizations without conflict. Every successful login updates the user’s lastLoginAt timestamp and writes a LOGIN audit event containing the caller’s IP address and user-agent string.

Endpoint

POST /auth/login
No authentication required. Returns 200 OK on success.

Request body

tenantNit
string
required
The Colombian NIT of the organization the user belongs to, without the check digit. Used to look up the correct tenant before verifying the user’s credentials.
email
string
required
The user’s email address. Compared against the tenant-scoped user record (case-insensitive).
passwordPlain
string
required
The user’s plain-text password. Compared against the stored bcrypt hash. Never logged or persisted.

Response

A 200 OK response with the following shape:
accessToken
string
Signed JWT valid for 15 minutes. Include as Authorization: Bearer {accessToken} on every protected request. Store in memory only — do not write to localStorage or sessionStorage.
refreshToken
string
Opaque base64 string (base64(tokenId:secret)) valid for 7 days. Use with POST /auth/refresh to obtain a new token pair. Store in an httpOnly cookie when possible.
user
object

Example request

curl -X POST https://api.oficina-nitida.com/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "tenantNit": "900123456",
    "email": "admin@sanjose.edu.co",
    "passwordPlain": "MiClave2025!"
  }'

Example response

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "YjdlMWYzYTItNWM2ZC00ZTdmLTlhMGItMWMyZDNlNGY1YTZiOnNlY3JldA==",
  "user": {
    "id": "b7e1f3a2-5c6d-4e7f-9a0b-1c2d3e4f5a6b",
    "email": "admin@sanjose.edu.co",
    "nombre": "Laura",
    "apellido": "Gómez",
    "rol": "ADMIN",
    "tenantId": "a3f8c2d1-4b5e-4f6a-8c9d-0e1f2a3b4c5d",
    "tenantNombre": "Colegio San José de La Salle"
  }
}

Login audit

Every successful login is recorded in the AuditLog table with:
  • action: "LOGIN"
  • entityType: "Auth"
  • tenantId and userId of the authenticated user
  • metadata: { "ip": "...", "userAgent": "..." } capturing the caller’s IP address and browser/client string
This audit trail is available to tenant admins and is used for security monitoring and compliance reporting.

Error cases

StatusCause
401 UnauthorizedThe tenantNit does not match any tenant; the email does not exist within that tenant; the password is incorrect; or the user account is inactive. All credential errors return the same generic message to prevent user enumeration.
400 Bad RequestThe tenant exists but its activo flag is false — the organization has been deactivated.
429 Too Many RequestsMore than 5 login requests per minute from the same IP address.
Store tokens securely. The access token should live in JavaScript memory only (a React state variable or a module-scoped variable), never in localStorage. The refresh token should be stored in an httpOnly, Secure, SameSite=Strict cookie so it is inaccessible to client-side scripts. Storing tokens in localStorage exposes them to XSS attacks.

Build docs developers (and LLMs) love