Roles & Access Control
Nexa uses JWT bearer auth with role-based guards. Roles are scoped — an operator's authority is bounded by airport or tenant.
Roles
| Role | Scope | Capabilities |
|---|---|---|
ADMIN | Tenant-wide | Everything, including policy publishing, PSP token management, agent config |
OPS_SUPERVISOR | Tenant | Policy activation, demand approval, allocation override, manual booking, case reopen |
AIRPORT_OPERATOR | Airport | Claim + resolve manual review, close cases, manual notification resend |
FINANCE | Tenant | Read-only on reservations, contracts, savings reports |
PASSENGER | Self | Read their own voucher (passenger portal) |
A user can hold multiple roles; the union of permissions applies.
JWT claims
Bearer tokens are required on every endpoint except /version and the passenger portal's public lookup. Expected claims:
{
"sub": "urn:nexa:user:abc",
"roles": ["OPS_SUPERVISOR"],
"tenant": "airline-xx",
"airports": ["MAD", "BCN"],
"iat": 1713800000,
"exp": 1713803600
}
tenant— the airline / organization.airports— the airport IATA codes this user can act on (forAIRPORT_OPERATOR).
A request to /cases/urn:nexa:case:gru-xxx from a user with airports: ["MAD"] returns 403.
Identity provider
Nexa doesn't ship an identity provider. Typical setups:
- Airline SSO (Okta, Azure AD, Ping) with a Nexa-specific app that maps groups to roles.
- Auth0 (the operations UI uses it by default in dev).
- Machine accounts for airline upstream integrations, with long-lived tokens scoped to
POST /casesonly.
The JWT is validated against the IdP's JWKS endpoint. Configure:
AUTH_JWKS_URI=https://airline.okta.com/oauth2/default/v1/keys
AUTH_ISSUER=https://airline.okta.com/oauth2/default
AUTH_AUDIENCE=nexa-api
Adding a new role
Roles are enum-typed (src/nexa/auth/role.ts). To add a role:
- Add the enum value.
- Add the role to the guards that should accept it (decorators are per-controller).
- Update this page 😊.
Role additions go through review; avoid inventing a role when an existing one plus airport scoping does the job.
Rate limiting
Per-user rate limits protect expensive endpoints:
/policies/synthesize— 20 req / user / hourPOST /cases— 60 req / user / minPOST /manual-review/:urn/rebook— 30 req / user / min
Machine accounts (airline upstream) get separate, higher limits negotiated per contract.
Audit
Every endpoint that changes state requires:
- A valid bearer token.
- A reason, for manual actions — enforced by the controller.
The audit entry includes actor, tokenId, ip, reason, and a before/after snapshot. See Audit.