07 — Component Library (Auth Module)
- A. Brand & chrome
- B. TOTP & code entry
- C. Session representation
- D. API-key artifacts
- E. Status & feedback
- F. Only elements the API supports
- G. Flutter widget map (module)
- H. Golden tests (base
00-shared/03 G)
Module-specific components built from shared
App*primitives (00-shared/03). Props, states, animation, a11y, tokens, Flutter mapping. Reuse over new — only components that don't exist in the shared library are defined here.
A. Brand & chrome
AppBrandMark (Auth-specific)
- Purpose: StudyLyon lockup at top of pre-auth screens; differentiator + trust.
- Props:
expanded(bool, tablet/desktop),onTap(unused on pre-auth). - State: single, tinted (
primaryContainertile). a11y:Semantics(image, label: "StudyLyon"). - Flutter:
AppAvatar-styleCircleAvatar+Textwith themed lockup; branding override fromorganization.branding(future).
AppPasswordField
- Purpose: obscured input with reveal toggle — used in login/register/reset.
- Props:
label,hint?,validator,textInputAction,onSubmitted,autofillHints(new-password|current-password),enabled,errorText,helperText(suggested account lockspace),strengthHintoptional (register). - State: base / focus / error / disabled / loading(suffix spinner) / reveal.
- Animation:
Mo-fasticon swap; error slide-in. - a11y: label +
Semantics(error), error announced; reveal buttonSemantics(toggled:obscure). - Flutter:
TextFormField+ suffixIconButton(visibility/visibility_off) withTooltip"Show password".
B. TOTP & code entry
AppTotpInput (6-digit)
- Purpose: enter TOTP 6–8 digits (backend default 6
env.ts:78, accept 6/8 sloppy withauthenticator.checktolerant drift). - Props:
length(default 6),onCompleted(code),error,loading,autofocus,readOnly. - Behaviors: 6 boxes, auto-advance, overwrite, paste full code,
Backspaceto previous; digit-only filter. - States: empty, partial, valid, error (
errorring) — validates againstauthenticator.check(code, secret)only server-side; client validatesRegExp(r'^\d{6,8}$')locally. - A11y: single grouped field label "One-time code",
Semanticsgroups boxes; talkback reads "digit 2 of 6". - Flutter: custom
Rowof 6TextField48dp focused with movement arrows / singleTextFieldwithLetterSpacing8px + lengthFormatter (recommend single hidden-real-width input). - Motion:
Mo-fastslide between boxes; errorAnimatedSwitchershake.
AppTotpChip (status pill)
AppBadge variant: ON → success, OFF → outline.
C. Session representation
AppSessionCard
- Purpose: one authenticated device row.
- Props:
deviceName?,browser?,os?,platform?,ip?,location?,expiresAt,activityAt,isCurrent,onLogout,menu?. - Renders: leading icon from
platformmap, title, subtitle ("browser · OS · ip · location"), metalastActivityAt/expiresAt,AppMenu(Log out). - Current device marker:
primaryContainertile + "You" chip. data-*fields: exactly theuser_sessiondoc fields (user-session.schema.ts:15-29).- Flutter:
ListTileinsideAppCard(elevatione-0), icon map fn.
AppActiveBadge (AppBadge) — enumerates session count for Security Hub.
D. API-key artifacts
AppApiKeyCard
| Field | Spec |
|---|---|
| Purpose | One non-revoked key summary |
| Props | prefix, name, scopes[], createdAt, lastUsedAt?, onRevoke |
| Rendering | name titleMedium, prefix**** mono + copy-prefix icon, chips of scopes, footer ${lastUsed ?? 'never used'} · created ${createdAt} in bodySmall |
| a11y | card button + menu; Semantics(label: "API key <name>") |
| Tokens | surfaceContainerLow, R-md, El-e-1 |
AppSecretReveal (one-time key reveal)
| Field | Spec |
|---|---|
| Purpose | Display raw key after create; warning that it's shown once (auth.service.ts:414). |
| Props | secret, copyable, dismissible |
| Rendering | mono body, wrapped card with copy; warning AppBanner(warning) "Copy now – you won't see this again." |
| Copy | Clipboard.setData + Snackbar("Copied"), lightImpact. |
| a11y | Semantics(liveRegion) on copy result; scrolled-into view. |
E. Status & feedback
- AppSecurityBanner — security anniversary: "Change your password" / "Verify email" prompts.
- Reuses
AppBanner,AppSnackbar,AppErrorState,AppEmptyState,AppOfflineBannerfrom 00-shared/03 with no module changes.
F. Only elements the API supports
- Sessions/API keys lists → no items (directly empty) — no infinite scroll needed
(
findByUserIdreturns full arrays,user-session.repository.ts:19,api-key.repository.ts:20).
G. Flutter widget map (module)
| Component | Flutter widgets |
|---|---|
| AppAuthHeader | Wrap + Text in Padding |
| AppPasswordField | TextFormField + IconButton |
| AppTotpInput | custom FormField<int?> over a single TextField 6-digit mask |
| AppSessionCard | Card + ListTile + MenuAnchor |
| AppApiKeyCard | Card + Row + ActionChips |
| AppSecretReveal | Card + SelectableText + Clipboard |
| AppTotpChip | `Badge`` |
| AppSecurityBanner | MaterialBanner |
H. Golden tests (base 00-shared/03 G)
Every module component: golden at phone/tablet/desktop + dark mode; error/all-secret states for AppTotpInput, AppSecretReveal, AppSessionCard.