Files
nx9-auth/docs/AUTHENTICATION.md

2.5 KiB

Authentication Security

nx9-auth implements an OWASP-aligned login flow.

Login contract

POST /api/v1/auth/login
Content-Type: application/json
Accept: application/json

{
  "username": "sunil",
  "password": "Password123!"
}

Response (200)

{
  "access_token": "<opaque session token>",
  "refresh_token": "<opaque refresh token>",
  "expires_in": 86400,
  "token_type": "Bearer",
  "user": {
    "id": "...",
    "username": "...",
    "status": "active",
    "roles": ["admin"],
    "permissions": ["users:create", "..."]
  }
}

Also sets an HttpOnly nx9_session cookie (same value as access_token).

Failures

Status Meaning
401 Invalid username or password (non-enumerating)
400 Malformed request body
429 Rate limited

There is no GET login. Query-string credentials are never accepted.

Password handling

Layer Behavior
Transport HTTPS in production (cookie_secure + reverse-proxy TLS)
Client Sends plaintext password only in POST JSON body — never hashes client-side
Server Argon2id PHC ($argon2id$v=19$…) with unique salt
Storage Only password hashes — never plaintext
Logs Never log password, tokens, cookies, or Authorization

Session security

  • New session token on every successful login (rotation)
  • Prior sessions and refresh tokens revoked on login (fixation mitigation)
  • Idle TTL + absolute TTL
  • Session token hashed (BLAKE3) at rest
  • Refresh tokens hashed (BLAKE3) in refresh_tokens table

SPA client

  1. POST /api/v1/auth/login with JSON
  2. Store access_token in sessionStorage
  3. Send Authorization: Bearer <access_token> on subsequent requests
  4. Browser may also store HttpOnly cookie automatically

The HTML login form uses method="post" so a native fallback cannot leak credentials into the URL.

Production configuration

[server]
cookie_secure = true
production = true
  • production = true refuses cookie_secure = false
  • Enables Strict-Transport-Security when secure mode is on
  • Terminate TLS (TLS 1.3 recommended) at a reverse proxy or load balancer

Security headers

Every response includes:

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • Referrer-Policy: no-referrer
  • Content-Security-Policy: …
  • Permissions-Policy: …
  • Strict-Transport-Security (when production/secure)

Rate limiting

Login is rate-limited per IP with progressive lockout (see security::rate_limit).