Core Docs

Registration & Login

Registration & Login



Complete flows for user registration, login, third-party auth, and 2FA verification. All flows are single-page, AJAX-driven with progressive screen reveal.

Architecture



Single-Page, Progressive Reveal



All registration and login flows are served as a single HTML page from core.auth.medkronos.com. The page loads all screen components at once, but only the current screen is visible. Transitions between screens are handled by JavaScript — no page reloads, no framework. Just DOM manipulation.

Page load (all screens rendered, only screen 1 visible)
│
▼
Screen 1 visible ──user action──→ AJAX call to core.auth API
│ │
▼ ▼
Screen 2 visible ←── response ── API validates/creates
│
▼
...progressive reveal until flow completes


Connection Tokens



When a user is redirected from an app to core.auth for registration/login, the app issues a connection token and passes it as a URL parameter (connection_ref). This token carries the app context through the entire flow:

  • Which app initiated the flow

  • Where to redirect after completion

  • What state/CSRF token the app expects back

-- connection_tokens table
-- See: /02-L2-architecture/02-auth/08-database-schemas


If there's no connection_ref, the user came directly to core.auth.medkronos.com and there's no app to redirect back to.

Frontend Technology



  • HTML/CSS/JS served from core.auth domain

  • Stylesheets from core.assets (core.backend HTML init)

  • No framework — vanilla JS, DOM manipulation

  • AJAX calls to core.auth API endpoints

  • Mobile responsive

Registration Flow



Screen 1 — Info & Entry Point



┌─────────────────────────────────────────┐
│ │
│ Welcome to UNVRSL │
│ │
│ [ Let's Register ] │
│ │
│ ── or continue with ── │
│ │
│ [G] [A] [F] │
│ Google Apple Facebook │
│ │
│ Already have an account? Log in │
│ │
│ [Cancel] │
│ │
└─────────────────────────────────────────┘


  • "Let's Register" → Screen 2 (direct registration)

  • Google/Apple/Facebook → redirect to provider OAuth flow

  • "Log in instead" → switch to Login Flow (Screen 1)

  • "Cancel" → redirect back to app (if connection_ref exists) or close

Third-Party Auth Redirect



User clicks "Continue with Google"
│
▼
JS redirects to: core.auth.medkronos.com/auth/google?connection_ref=CONN_TOKEN
│
▼
core.auth redirects to Google OAuth consent screen
│
▼
User authorizes with Google
│
▼
Google redirects back to: core.auth.medkronos.com/auth/google/callback
│
▼
core.auth:
1. Receives OAuth code from Google
2. Exchanges code for Google access token
3. Fetches user profile (name, email, avatar)
4. Checks: does a user_third_party_auths record exist for this provider+provider_user_id?
│
├── Yes → existing account found → skip to Login completion
│
└── No → new user:
├── Check: does a user exist with this email?
│ ├── Yes → prompt: "An account with this email exists. Log in to link Google."
│ └── No → create new user account:
│ ├── Generate random password (16 chars, alphanumeric)
│ ├── Hash with Argon2id
│ ├── Set email_verified = true (Google verified it)
│ ├── Store display_name, avatar_url from Google profile
│ ├── Create user_third_party_auths record
│ ├── Send welcome email with generated password
│ └── Continue to Screen 5 (success or app authorization)


Welcome email content:
Subject: Your UNVRSL Account

Hi [Name],

Your account was created via Google Sign-In.

We generated a password for you in case you want to log in
without Google:

Password: [random-16-chars]

⚠️ If you plan to use password login, please:
1. Log in and change this password in account settings
2. Delete this email

If you only use Google Sign-In, you can ignore this.


Screen 2 — Basic Info (Direct Registration)



┌─────────────────────────────────────────┐
│ │
│ Create Your Account │
│ │
│ First name: [ ] │
│ Last name: [ ] │
│ Email: [ ] │
│ Phone: [ ] │
│ Password: [ ] │
│ Confirm: [ ] │
│ │
│ [Continue] │
│ [Back] │
│ │
└─────────────────────────────────────────┘


Password policy:
  • Minimum 10 characters

  • Hashed with Argon2id (PASSWORD_ARGON2ID)

  • No breach database checking

Validation (AJAX):
POST /api/register/validate
{
"email": "user@example.com",
"phone": "+41...",
"password": "..."
}

Response:
{
"valid": true,
"errors": [] // or ["Email already registered", "Password too short"]
}


If the email is already registered, the response includes a suggestion: "An account with this email exists. [Log in instead?]"

Screen 3 — Email + Code Verification



┌─────────────────────────────────────────┐
│ │
│ Verify Your Email │
│ │
│ We sent a code to user@example.com │
│ │
│ Code: [ - - - - ] │
│ │
│ [Verify] │
│ Resend code (55s) │
│ [Back] │
│ │
└─────────────────────────────────────────┘


Flow:
POST /api/register/send-code
{ "email": "user@example.com" }

→ core.auth generates 6-digit code, stores hashed, sends via email
→ Code expires in 10 minutes

POST /api/register/verify-code
{ "email": "user@example.com", "code": "123456" }

→ core.auth verifies code hash
→ On success: mark email_verified = true, proceed to Screen 4
→ On failure: 401, up to 3 attempts before lockout (15 min)


Phone verification (optional, if phone provided):
  • Same flow: send SMS code, verify, mark phone_verified = true

  • Can be done in parallel or sequentially after email

Screen 4 — Biometric Setup (Optional)



┌─────────────────────────────────────────┐
│ │
│ Quick Login Setup │
│ │
│ Use fingerprint or face recognition │
│ for faster login next time. │
│ │
│ [ Set Up Biometric ] │
│ │
│ Skip for now │
│ │
└─────────────────────────────────────────┘


Only shown if the platform supports WebAuthn. Detected via:
if (window.PublicKeyCredential &&
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()) {
// Show biometric screen
}


If the user taps "Set Up Biometric" → triggers WebAuthn registration flow (see 2FA & Passkeys).

If "Skip for now" → proceed to Screen 5.

Screen 5 — Completion



If registered directly (no connection_ref):
┌─────────────────────────────────────────┐
│ │
│ ✓ Account Created! │
│ │
│ Welcome to UNVRSL, [Name]. │
│ │
│ [Go to Dashboard] │
│ │
└─────────────────────────────────────────┘


If registered via app (has connection_ref):
→ Switch to App Authorization Flow (see below).

Login Flow



Screen 1 — Login Entry Point



┌─────────────────────────────────────────┐
│ │
│ Log In to UNVRSL │
│ │
│ Email: [ ] │
│ Password: [ ] │
│ │
│ [ Log In ] │
│ │
│ ── or continue with ── │
│ │
│ [G] [A] [F] │
│ Google Apple Facebook │
│ │
│ [📱 Log in with QR code] │
│ │
│ Don't have an account? Register │
│ │
│ [Cancel] │
│ │
└─────────────────────────────────────────┘


  • Email + password → Screen 2

  • Google/Apple/Facebook → third-party auth redirect (same as registration)

  • QR code → QR Login Flow (see QR Login)

  • "Register instead" → Registration Screen 1

  • "Cancel" → redirect back to app or close

Screen 2 — Credential Verification



POST /api/login
{ "email": "user@example.com", "password": "..." }

Response (success):
{
"status": "ok",
"2fa_required": false
}

Response (2FA needed):
{
"status": "2fa_required",
"challenge_id": "abc-123",
"2fa_type": "totp" // or "webauthn"
}

Response (failure):
{
"status": "error",
"error": "Invalid credentials"
}


Screen 3 — 2FA Verification (If Required)



┌─────────────────────────────────────────┐
│ │
│ Two-Factor Authentication │
│ │
│ Enter the code from your app: │
│ │
│ Code: [ - - - - ] │
│ │
│ [Verify] │
│ Use recovery code instead │
│ │
└─────────────────────────────────────────┘


For TOTP:
POST /api/verify-2fa
{ "challenge_id": "abc-123", "code": "123456" }

→ Verify code against stored secret (±1 window tolerance)
→ On success: issue JWT + session_ref cookie
→ On failure: 401, up to 3 attempts before lockout


For WebAuthn:
POST /api/verify-2fa/webauthn
{ "challenge_id": "abc-123" }

→ Returns PublicKeyCredentialRequestOptions
→ Browser shows biometric prompt
→ User authenticates
→ On success: issue JWT + session_ref cookie


Screen 4 — Biometric Login (If Configured)



If the user has a passkey registered, offer biometric login as the primary option:

┌─────────────────────────────────────────┐
│ │
│ Welcome Back │
│ │
│ [ Use Fingerprint ] │
│ │
│ Use password instead │
│ │
└─────────────────────────────────────────┘


This screen appears before the password prompt if the user has passkeys enabled. It's the path of least resistance for returning users.

Screen 5 — Login Completion



If logged in directly (no connection_ref):
→ User is logged in. Redirect to dashboard or the URL they originally requested.

If logged in via app (has connection_ref):
→ Check if user has authorized this app.
→ If yes: issue auth code, redirect back to app.
→ If no: show App Authorization screen.

Account Already Exists (Social Login)



When a user tries to register via social login and an account with the same email already exists:

User clicks "Continue with Google"
│
▼
Google returns email: user@example.com
│
▼
core.auth checks: user exists with this email?
│
└── Yes → check: already linked to this Google account?
│
├── Yes → log in directly (skip to completion)
└── No → prompt:
"An account with user@example.com already exists.
Log in with your password to link your Google account."
│
▼
User enters password → verified
│
▼
core.auth creates user_third_party_auths record (links Google)
│
▼
User is logged in, Google is now linked


This prevents duplicate accounts while allowing users to link multiple social providers.

Password Policy



RuleValue






Minimum length10 characters
Hashing algorithmArgon2id (PASSWORD_ARGON2ID)
Breach database checkingNone (for now)
SaltAutomatic (PHP/Argon2id handles this)
Work factorPHP defaults (configurable)

Rate Limiting




EndpointLimitWindowAction





/api/login5 attempts15 minLock account + notify
/api/register/send-code3 codes10 minTemporary block
/api/register/verify-code3 attempts10 minLock verification + require new code
/api/verify-2fa3 attempts5 minLock 2FA + require recovery code

Rate limits are per-IP and per-user.

API Endpoints Summary




EndpointMethodPurpose

















/api/register/validatePOSTValidate registration fields
/api/register/send-codePOSTSend email/phone verification code
/api/register/verify-codePOSTVerify the code
/api/register/completePOSTCreate user account
/api/loginPOSTAuthenticate credentials
/api/verify-2faPOSTVerify TOTP code
/api/verify-2fa/webauthnPOSTInitiate/verify WebAuthn challenge
/auth/googleGETRedirect to Google OAuth
/auth/google/callbackGETHandle Google OAuth callback
/auth/appleGETRedirect to Apple Sign-In
/auth/apple/callbackGETHandle Apple callback
/auth/facebookGETRedirect to Facebook OAuth
/auth/facebook/callbackGETHandle Facebook callback
/auth/qr/generatePOSTGenerate QR login code
/auth/qr/statusGETPoll QR scan status
/auth/qr/confirmPOSTConfirm QR login from scanning device




Next: 2FA & Passkeys — TOTP, WebAuthn, biometric setup details.