Files
HIPCTF2/docs/api/auth.md
T
2026-07-21 21:19:36 +00:00

6.1 KiB
Raw Blame History

type, title, description, tags, timestamp
type title description tags timestamp
api Auth Endpoints Login, refresh, logout, public self-registration, CSRF, first-admin registration, and registration throttling.
api
auth
login
register
refresh
csrf
2026-07-21T18:28:00Z

Endpoints

Method Path Auth CSRF Rate limited Source
POST /api/v1/auth/register @Public() Enforced — caller must first call GET /api/v1/auth/csrf (the SPA's AuthService.ensureCsrf() does this automatically). Yes — RegistrationRateLimitService per IP backend/src/modules/auth/auth.controller.ts
POST /api/v1/auth/login @Public() Enforced — same pattern as register. CSRF skip was removed in this change. Yes — LoginBackoffService per IP + username backend/src/modules/auth/auth.controller.ts
POST /api/v1/auth/refresh Public route, requires a valid refresh cookie. Skipped (the request is a POST carrying an HttpOnly cookie, not a state-changing user action). No backend/src/modules/auth/auth.controller.ts
POST /api/v1/auth/logout Authenticated (JWT). Enforced. No backend/src/modules/auth/auth.controller.ts
GET /api/v1/auth/csrf @Public() n/a (GET). No backend/src/modules/auth/auth.controller.ts
POST /api/v1/auth/register-first-admin @Public() Skipped (path is in CsrfMiddleware.SKIP_PATH_PREFIXES). Yes backend/src/modules/users/users.controller.ts

Public player self-registration — POST /api/v1/auth/register

New endpoint, gated by the registrationsEnabled setting (backend/src/config/env.schema.tsSETTINGS_KEYS.REGISTRATIONS_ENABLED, default "false").

Field Type Constraints
username string 332 chars, regex ^[a-zA-Z0-9_.-]+$
password string 1256 chars; also passes validatePassword (Argon2 policy).
passwordConfirm string Must equal password (zod refine → VALIDATION_FAILED on passwordConfirm).

Validated by backend/src/modules/auth/dto/auth.dto.tsRegisterDtoSchema.

Successful response (201)

{
  "accessToken": "<jwt>",
  "expiresIn": 900,
  "user": { "id": "<uuid>", "username": "player1", "role": "player" }
}

The refresh token is set as an HttpOnly cookie via setRefreshCookie(res, session.refreshToken, config). The new user row has role='player' and status='enabled'.

Error envelope

HTTP code When
400 VALIDATION_FAILED zod body validation failed.
400 WEAK_PASSWORD Argon2 policy rejects the password.
403 REGISTRATIONS_DISABLED setting.registrationsEnabled !== 'true'.
409 USERNAME_TAKEN A user with that username already exists.
429 RATE_LIMITED The per-IP registration limiter has reached 10 attempts in the rolling 60-second window.

Wiring

  • AuthService.registerPlayer(dto, ip) runs inside a single DataSource.transaction:
    1. RegistrationRateLimitService.tryConsume(ip) permits at most 10 attempts per IP in a rolling 60-second window.
    2. SettingsService.get(SETTINGS_KEYS.REGISTRATIONS_ENABLED, 'false') check.
    3. Username uniqueness lookup → USERNAME_TAKEN on collision.
    4. Argon2id hash + insert UserEntity({ role: 'player', status: 'enabled' }).
    5. mintSession(manager, user) → access JWT + refresh row.
  • AuthService.registerFirstAdmin(username, password, ip) uses the same limiter before checking whether an administrator already exists.
  • The limiter is an in-memory Nest provider in backend/src/common/services/registration-rate-limit.service.ts; counters are process-local and reset when the API process restarts.

Key Files

File Responsibility
backend/src/modules/auth/auth.service.ts Validates registration policy, applies the limiter, creates users, and mints sessions.
backend/src/common/services/registration-rate-limit.service.ts Tracks timestamps and enforces the 10-per-minute per-IP limit.
backend/src/modules/auth/auth.controller.ts Exposes public registration and authentication routes.
backend/src/modules/auth/dto/auth.dto.ts Validates registration request fields.

Frontend consumer

frontend/src/app/core/services/auth.service.ts exposes login() and register(). Both:

  1. Call ensureCsrf()GET /api/v1/auth/csrf to materialise the csrf cookie if it isn't set yet.
  2. POST with { withCredentials: true, observe: 'response' }.
  3. Return success or the standard error envelope.

The LandingComponent consumes both methods and maps failures to user-facing copy (see Landing Page).

Examples

Tester flow: registration rate limit

  1. Enable registrations in the admin settings.
  2. Open the public registration form from the landing page.
  3. Submit attempts from the same client IP.
  4. The first 10 attempts are processed normally; the 11th within 60 seconds returns HTTP 429 with code RATE_LIMITED.
  5. After the rolling window expires, a new attempt is accepted if other requirements pass.

See also