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

15 — Flutter Implementation Guide (Auth Module)

How to build the Auth feature in the Flutter client on top of 00-shared/11. Forward- looking spec; no client repo exists yet.


1. Folder structure

features/auth/
├── domain/
│   ├── models/
│   │   ├── authenticated_user.dart   # id, tenantId, roles[]
│   │   ├── session.dart              # user_session mirror
│   │   └── api_key_ref.dart          # id, name, prefix, scopes, createdAt, lastUsedAt
│   └── exceptions/auth_auth_exceptions.dart # typed ApiException*
├── data/
│   ├── dto/
│   │   ├── login_dto.dart
│   │   ├── register_dto.dart
│   │   ├── totp_dto.dart
│   │   └── api_key_create_dto.dart
│   └── repositories/
│       └── auth_repository.dart
└── presentation/
    ├── cubit/
    │   ├── auth_cubit.dart
    │   ├── login_cubit.dart
    │   ├── register_cubit.dart
    │   ├── sessions_cubit.dart
    │   ├── api_keys_cubit.dart
    │   └── tfa_detail_cubit.dart
    ├── pages/
    │   ├── login_page.dart
    │   ├── register_page.dart
    │   ├── forgot_page.dart
    │   ├── reset_page.dart
    │   ├── verify_email_page.dart
    │   ├── security_hub_page.dart
    │   ├── sessions_page.dart
    │   ├── api_keys_page.dart
    │   ├── api_key_create_sheet.dart
    │   └── tfa_detail_page.dart
    └── widgets/
        ├── auth_header.dart
        ├── totp_input.dart
        ├── session_card.dart
        ├── api_key_card.dart
        └── secret_reveal.dart

2. Dependencies

flutter_bloc, dio (AppDio with refresh/error interceptors), go_router, get_it, secure_storage, intl, qr (client-side QR for qrCodeUri, no network). TOTP verification client-side never (server owns secret) — only input automation.

3. Cubits

  • AuthCubit: state machine (§13) flutter_secure_storage for accessToken/refreshToken/tenantId/user. On boot: restore()refresh().
  • UI cubits call AuthRepository; never direct dio.

4. AuthRepository (single)

class AuthRepository {
  // throws ApiException(code,status)
  Future<TokenPair> login(LoginDto);
  Future<TokenPair> register(RegisterDto);
  Future<TokenPair> refresh(String refreshToken);
  Future<void> logout(String refreshToken);
  Future<void> logoutAll();
  Future<void> verifyEmail(String token);
  Future<void> resendVerification();
  Future<void> forgotPassword(String email);
  Future<void> resetPassword(String token, String password);
  Future<TotpSetup> enable2fa();
  Future<void> verify2fa(String code);
  Future<void> disable2fa(String code);
  Future<List<Session>> sessions();
  Future<void> revokeSession(String id);
  Future<List<ApiKeyRef>> apiKeys();
  Future<CreatedApiKey> createApiKey(String name, List<String> scopes);
  Future<void> revokeApiKey(String id);
}

All through AppDio; the refresh interceptor is global (not here).

5. Navigation

  • go_router GoRoute for /login, /register, /verify-email, /forgot-password, /reset-password, /settings/security.
  • Route guards read AuthCubit; pre-auth stack = simple list; redirect rule: state.redirect → authed user → /home, unauthed → public.
  • Deep link Uri → token params passe to Verify/Reset pages.

6. Theme

  • AppTheme.light()/dark() unchanged; pre-auth pages wrap in Scaffold(primary: true)? No — reuse global theme; auth adds no tokens (11_Design_System_Mapping.md).

7. Extensions

  • String.maskedEmail() for welcome/footer.
  • DateTime.toRelative() for "Last used 3 h ago".
  • Session.platformIcon() mapping.

8. Localization keys

auth.login.*… Full list in 08_Form_Specifications.md; all server messages mapped to keys, fallback to message for business 4xx only.

9. Secure token storage

  • flutter_secure_storage: keys auth.access, auth.refresh, auth.user, auth.expiry.
  • No direct SharedPreferences (that would be a leak vector).

10. Testing

  • Unit: AuthCubit state transition matrix; LoginCubit form validation; mapper DTO→model.
  • Widget: login states (idle/loading/error/rate/offline); TotpInput paste/advance; sessions empty/loaded; key reveal one-time.
  • Golden: components + pages light/dark × 3 sizes (00-shared/10 §9).
  • Integration: register (with mock server) → home; expired refresh → sessionExpired → login; revoke-all → login.
  • E2E (P0): register → verify → enable 2FA → create key → revoke → logout on device cloud.

11. Performance

  • List builders for sessions/keys; no rebuild of whole page on CTA; const constructors; QR image lazy-build off-screen; clipboard micro-delay.

12. Proposals flagged to the team

  1. When server adds first-login-verify gate + 2FA challenge, enable LoginCubit challenge state (OQ-1).
  2. When lockout/family-revoke/recovery-codes land (IMPLEMENTATION_PLAN.md), add the associated screens.
  3. Analytics wiring waits share 99 (AnalyticsService interface).