PermisosQR secures its API with JSON Web Tokens (JWT). When a user logs in successfully, the server signs a token with theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/edgar2420/QrPermision/llms.txt
Use this file to discover all available pages before exploring further.
JWT_SECRET environment variable and returns it alongside the user’s profile. By default tokens expire in 8 hours, controlled by the JWT_EXPIRES_IN environment variable (e.g. 8h, 24h, 7d). Every subsequent request to a protected endpoint must present that token in the Authorization header using the Bearer scheme — the server validates the signature and expiry on every call before passing control to the route handler.
How It Works
Obtain a Token
Send your credentials to A successful response looks like this:
POST /api/auth/login. The server verifies the email/password pair against the database, checks the account is active, and — if everything matches — returns a signed JWT alongside the authenticated user’s profile.Two roles exist in PermisosQR:
super_admin and admin_operator. Super admins can access every endpoint; operators are restricted from routes that require elevated privileges.Use the Token
Include the token in the The middleware extracts the token, verifies it against
Authorization header of every request to a protected endpoint. Use the Bearer keyword followed by a single space and the token string.JWT_SECRET, and attaches the decoded payload to req.user before the route handler runs. If the header is absent or malformed the request is rejected immediately with 401.Token Payload
When the server signs a token it encodes the following claims into the payload. You can inspect them by decoding the JWT on the client (e.g. withjwt-decode) — just never trust client-decoded claims for authorization; always let the server verify the signature.
| Claim | Type | Description |
|---|---|---|
id | number | Database primary key of the authenticated user |
name | string | User’s display name |
email | string | User’s email address |
role | string | Either super_admin or admin_operator |
iat | number | Issued-at timestamp (Unix epoch seconds) |
exp | number | Expiry timestamp (Unix epoch seconds) |
Token Expiry
Tokens expire 8 hours after they are issued. This is the default value ofJWT_EXPIRES_IN in .env:
jsonwebtoken library — for example 24h, 7d, or 3600 (seconds). After expiry the token signature remains valid but the server will reject it with a 401 and the message Token inválido o expirado. Re-authenticate via POST /api/auth/login to get a fresh token.
Error Responses
The authentication middleware returns structured errors so clients can handle each case cleanly.| Status | Trigger |
|---|---|
401 | Authorization header is absent or does not start with Bearer |
401 | Token signature is invalid, tampered with, or the token has expired |
403 | Token is valid but the user’s role is not super_admin |
GET /api/auth/me
Once authenticated, use this endpoint to retrieve the full profile of the currently logged-in user — useful for bootstrapping UI state after a page reload.User type defined in the frontend:
POST /api/auth/setup
This one-time endpoint bootstraps the very firstsuper_admin account. It is only available when the users table is completely empty — once any user exists, subsequent calls are rejected with 403.
The public QR endpoints —
GET /api/qr/public/:id, POST /api/qr/public/:id/enable, and POST /api/qr/public/:id/return — do not require a Bearer token in the Authorization header. They are designed to be accessed by external parties scanning a QR code. However, the enable and return actions still verify operator identity through email and password fields supplied in the request body, so unauthenticated actors cannot enable or return permissions on behalf of an operator.Security Recommendations
Additional recommendations:- Rotate
JWT_SECRETimmediately if you suspect it has been compromised — all existing tokens will be invalidated. - Set
NODE_ENV=productionand use a strong, randomJWT_SECRET(at least 32 characters) in production. - Consider shortening
JWT_EXPIRES_INfor high-security deployments (e.g.2h) and implementing a token-refresh strategy on the frontend. - Always serve the API behind HTTPS in production to prevent token interception in transit.