Skip to content

Latest commit

 

History

History
38 lines (22 loc) · 2.47 KB

File metadata and controls

38 lines (22 loc) · 2.47 KB

0008 — Self-serve registration (session on success)

Status: Accepted — May 2026
Scope: django-backend/accounts/ HTTP surface, frontend/ /register route, and how registration differs from login for duplicate-email signaling.

Context

M2 shipped session login via POST /api/auth/login/ and createsuperuser for the first admin. New sandbox users still had to be created out-of-band (admin or shell), which blocks demos and early testers.

We keep Django sessions (no JWT), the email-based User model, and the same cookie + X-CSRFToken contract as login. Registration must not grant is_staff / is_superuser from the client.

Decision

  1. Single endpoint: POST /api/auth/register/ with body email, password, password_confirm (snake_case JSON matches DRF field names).

  2. Validation: reuse Django AUTH_PASSWORD_VALIDATORS via validate_password on a transient User(email=…) instance; require password == password_confirm in the serializer validate() method.

  3. Persistence: User.objects.create_user(email, password) only — never raw User.objects.create. Normalize email with User.objects.normalize_email inside the serializer so storage matches create_user.

  4. Session: on success, call django_login(request, user) immediately (same fixation benefit as login). Response 201 Created with the same JSON shape as POST /api/auth/login/ (UserSerializer).

  5. Duplicate email: return 409 Conflict with {"code": "EmailAlreadyRegistered", "detail": "…"}. This intentionally differs from login’s generic 401 for wrong credentials — registration cannot hide that an email is already taken.

  6. Concurrency: catch IntegrityError around create_user as a second line of defense after an exists() check (race-safe).

  7. Out of scope for this ADR: email verification, password reset, rate limiting, captcha, Profile model.

Consequences

  • Frontend adds /register, RegisterForm, and useAuth().register(...).
  • docs/api-contracts.md documents the new endpoint; compliance notes mention open registration in sandbox.
  • CI gains accounts tests using Django Client (CSRF header parity is documented where DRF’s csrf_exempt wrapper applies).

Related

  • docs/decisions/0005-cross-origin-session-spa.md — CSRF + CORS for unsafe POSTs.
  • docs/api-contracts.md — request/response matrix.
  • AGENTS.md — session auth remains the transport; no client-trusted balances.