Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

06 — Screen Specifications (Auth Module)

Production-grade specs for each Auth screen: chrome, states, components, micro-interactions, a11y, responsive, dark/light, keyboard, motion. Tokens: Sp = 00-shared/02 §3, Ty = typography §2, El = elevation §5, R = radius §4, Mo = motion §6. All components reference 00-shared/03.


1. Login Screen /login

Chrome: no AppBar (brand only); optional app-logo AppAvatar/logo lockup; screen scrolls with keyboard avoidance (resizeToAvoidBottomInset: true); horizontal padding 16 (phone), 24 (tablet+).

Layout (phone portrait):

Brand lockup  [ primaryContainer block, 64h ]
title  "Welcome back"          headlineMedium
AppTextField email             h 56, r-md
AppTextField password          h 56, r-md, obscure, autofill
[toggle show/hide password]    IconButton 24
Primary CTA "Log in"           FilledButton fullWidth h 48
row: "Forgot password?"        TextButton
divider "or"
TextButton "Register your school"
Legal microcopy               bodySmall onSurfaceVariant

States:

  • idle → email/password empty, CTA disabled.
  • filled → CTA enabled (no server call until tap).
  • submitting → CTA loading spinner replaces label, inputs disabled, no double-submit.
  • error inline (401): error text under field(s) errorContainer chip + "Invalid email or password." — server doesn't reveal which; copy matches server auth.service.ts:125.
  • error locked: banner "Account locked. Try again later." (auth.service.ts:135).
  • rate limited 429: inline AppBanner(error) + countdown labelMedium; inputs stay editable but CTA disabled until window; no auto-retry.
  • offline: AppOfflineBanner top; CTA disabled with tooltip "Connect to retry".
  • password manager: autofill hint both snippets (username, current-password).

Motion: Mo.m-fast ripple; button loading Mo.m-base; error AnimatedSwitcher fade; screen transition to home Mo.m-base.

A11y: each labeled field, Semantics.error on invalid, error announced via live region; focus to first invalid on submit; target ≥ 48.

Dark/light: token-only; brand lockup primaryContainer; both pass contrast.

Tablet/desktop: centered card width ≤ 480; skip-to-content; hover states on buttons; Tab traversal email→password→CTA→forgot.

Safe areas / landscape: all padded within SafeArea; landscape ≤ 640h scrolls.

2. 2FA Challenge Screen (planned)

Layout: same hub; "Enter the 6-digit code from your authenticator"; AppTotpInput 6 boxes (auto-advance, paste support); CTA Verify; "Use a recovery code" (planned); "Back to login". Error: pop AppSnackbar error + red ring; auto-focus box 1 on navigation.

3. Register Screen

Header: headlineMedium "Create your school account". Fields (in order): first name, last name, school/tenant id, email, phone (opt), password (+ strength hint), confirm. Footer: legal microcopy + "I agree" implied by submit. CTA "Create account" loading.. on 409 → inline banner under email + focus email. On success (tokens auto-login, auth.service.ts:95-120): navigate home with "Verify your email" banner (AppBanner info) + "Resend verification" (rate-limited 5/120s).

4. Verify Email Screen

Centered card; auto submit if ?token= present. States: Skeleton while token introspection (cannot introspect — client calls verify once), success check icon success, error banner "Link invalid or expired — resend (X s)" with POST /auth/resend-verification guarded by rate limit countdown. Deep link token → clipboard capture → auto call.

5 & 6. Forgot / Reset Password

Same spec → see 08_Form_Specifications.md for complete field tables; screens use identical layout chrome to Login (single card, brand lockup).

7. Security Hub

ListTile rows with Trailing = badge (count or TOTP ON), each icon map: 2FA (Icons.shield), Devices (Icons.devices, count badge = #sessions), API keys (Icons.key, count). Desktop master-detail reveal. Refresh on return.

8. Sessions/Devices Screen

ConcernSpecification
Groups"This device" pinned first (match on stored device fingerprint/local marker) then others
RowAppListTile leading device icon by platform (phone/tablet/desktop), title deviceName, subtitle "browser · OS · location · ip", trailing AppMenu (Log out this session)
Date linebodySmall "Last active 3h ago · expires in 5d" from lastActivityAt/expiresAt (user-session.schema.ts:36,39)
EmptyAppEmptyState icon devices_other, "No other sessions"
LoadingAppSkeleton(list)
ErrorAppErrorState.code UNAUTHENTICATED/INTERNAL
PullAppRefreshIndicator
CTA (bottom, fullWidth)Log out all devicesAppDialog confirm → POST /auth/logout-all
Row actionDELETE /auth/sessions/:id b/ revoke dialog (destructive-style) then optimistic remove + AppSnackbar + UNDO (reenable only via re-login; mark no undo — revoke is irreversible server-side)

A11y: row semantics button + device + action hint; affordance of confirm menu; dynamic type sizing.

Dark: identical tokens; sessions differ by primaryContainer for current device.

Tablet: two-column (list+detail) master-detail at ≥840px; desktop command key Cmd+Backspace revoke (proposed).

9. API Keys List / Create / Revoke

ScreenSpec
ListHeader headlineMedium + subtitle; AppFAB "Add key" (extended, Icons.add); row AppKeyCard (AppListTile): prefix in mono, scopes as AppChips (readonly), lastUsedAt/createdAt line, AppMenu: Revoke. Empty: AppEmptyState
Create (bottom sheet, 2/3 height)AppTextField name (autofocus), scopes AppChips input (optional; free text to CreateApiKeyDto.scopes? string[]), CTA "Create key"; submit loading; on 409/400 banner; success → reveal state: full-key text mono in AppCard + "Copy" button (confirmation Snackbar "Copied") + warning "Save your key — it's shown once" + Done replaces sheet. This card replaces the form; dismiss clears key
RevokeAppDialog "Delete this key?" → DELETE /auth/api-keys/:id → optimistic removal + Snackbar

10. 2FA Detail Screen

UISpec
Off state (default)AppBanner "Two-factor is off"; TonalButton "Turn on" → POST /auth/2fa/enable → returns {secret, qrCodeUri}
Setup stageQR card (device-render qrCodeUri), secret mono + copy+backup note; "I've saved my secret" CTA
Verify stage6-digit TOTP input; "Enable" → POST /auth/2fa/verify; 401 → red ring + error; success → status ON with green badge; AppSnackbar "2FA enabled"
On statestatus row, Disable (destructive text) → enables TOTP input again → POST /auth/2fa/disable (code required by server auth.service.ts:376)
Safe areaQR fits safe areas; landscape SingleChildScrollView

Global dark/light, dynamic type (200%), a11y on all – used tokens are surface, primaryContainer, outline, onSurfaceVariant; error errorContainer.

Shared micro-interaction & motion notes (all screens)

  • Mo-m-base route transitions; AppBar elevation El-1; cards El-e1.
  • Snackbar durations: success < 3 s, error ≥ 4s (00-shared/03 A).
  • No FAB on pre-auth; FAB appears only ApiKeys list, hide-on-scroll-down.
  • Keyboard: default TextInputAction.next, last field .done; Enter submits; error fields focus jump.
  • Safe areas: bottom nav-height respected only in authed; pre-auth uses MediaQuery padding.