Skip to main content

accounts app

User accounts, password management, email verification, passkey (WebAuthn) sign-in, and GDPR-compliant data export / account deletion.

Models (6)

  • User — extended Django user with UUID PK, email-as-username, encrypted phone
  • UserEmail — secondary verified emails for one user; SSO uses these for matching
  • UserConsent — captures consent template acceptances (privacy / terms / marketing / analytics)
  • DataExport — async data-export job state (status, S3 URL, expires_at)
  • WebAuthnCredential — stored passkey credential (credential_id, public_key, sign_count, friendly name, last_used_at)
  • PasskeyEnrollmentDismissal — tracks "remind me later" for the post-login passkey enrollment prompt

Key endpoints

URLPurpose
POST /api/auth/register/Self-signup with email + password
POST /api/auth/login/Email + password → sets JWT cookies
POST /api/auth/logout/Clears cookies, blacklists refresh token
GET /api/auth/me/Current user profile
PATCH /api/auth/me/Update first/last name, phone
POST /api/auth/me/password/Change password
POST /api/auth/me/emails/add/Add a secondary email
POST /api/auth/me/emails/verify/Verify a secondary email via token
POST /api/auth/password-reset/request/Request reset email
POST /api/auth/password-reset/confirm/Submit new password with token
POST /api/auth/me/export/Trigger GDPR data export (Celery → S3)
POST /api/auth/me/delete/Request account deletion (30-day grace period)
POST /api/auth/admin/password-reset/Admin-initiated password reset (org admin only)
POST /api/auth/passkey/register/begin/Build WebAuthn registration options (signed-in user)
POST /api/auth/passkey/register/complete/Verify attestation, persist WebAuthnCredential
POST /api/auth/passkey/authenticate/begin/Build discoverable WebAuthn assertion options (no auth required)
POST /api/auth/passkey/authenticate/complete/Verify assertion, look up credential, issue JWT
GET /api/auth/passkey/credentials/List the current user's passkeys
PATCH /api/auth/passkey/credentials/<id>/Rename a passkey
DELETE /api/auth/passkey/credentials/<id>/Revoke a passkey
GET /api/auth/passkey/prompt-status/Should the post-login enrollment prompt show?
POST /api/auth/passkey/dismiss-prompt/"Remind me later" — sets 30-day cooldown

Permissions

  • IsAuthenticated — most endpoints
  • AllowAny — register, login, password reset request (rate-throttled)
  • IsNationalAdmin — admin password reset

Background tasks

  • send_email_verification(user_email_id) — sends verification email
  • send_temporary_password_notification(user_id, temp_password) — admin reset flow
  • export_user_data(data_export_id) — bundles all user data, gzips, uploads to S3, generates presigned URL valid for 7 days

External integrations

  • AWS S3 (data export storage via boto3)
  • Email backend (configured at apps.platform.PlatformEmailConfig)

Signals

  • post_save on User — auto-creates UserEmail row from primary email

Notable patterns

Account deletion grace period

POST /api/auth/me/delete/ doesn't immediately delete. It:

  1. Sets User.deletion_requested_at = now()
  2. Sends confirmation email with a "cancel" link
  3. After 30 days, prune_pending_deletions task scrubs the account

Cancelling: signing in within 30 days clears deletion_requested_at.

Password hashing

Production uses Django's default PBKDF2 with 870K iterations. Tests override to MD5 via PASSWORD_HASHERS to keep tests fast.

Throttling

EndpointThrottle
register/account_request (3/hour prod)
login/, logout/auth (10/min prod)
password-reset/request/password_reset (5/hour prod)
admin/password-reset/admin_password_reset_target (10/hour prod)
passkey/*/begin/passkey_begin (10/min prod)
passkey/*/complete/passkey_complete (5/min prod)

Code paths

  • Models: backend/apps/accounts/models.py
  • Views: backend/apps/accounts/views.py, backend/apps/accounts/webauthn_views.py
  • Serializers: backend/apps/accounts/serializers.py
  • URLs: backend/apps/accounts/urls.py
  • Tasks: backend/apps/accounts/tasks.py
  • Tests: backend/apps/accounts/tests/ + backend/apps/accounts/tests_webauthn.py