15 — Flutter Implementation Guide (Auth Module)
- 1. Folder structure
- 2. Dependencies
- 3. Cubits
- 4. AuthRepository (single)
- 5. Navigation
- 6. Theme
- 7. Extensions
- 8. Localization keys
- 9. Secure token storage
- 10. Testing
- 11. Performance
- 12. Proposals flagged to the team
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_storageforaccessToken/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
GoRoutefor/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 inScaffold(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: keysauth.access,auth.refresh,auth.user,auth.expiry.- No direct
SharedPreferences(that would be a leak vector).
10. Testing
- Unit:
AuthCubitstate transition matrix;LoginCubitform validation; mapper DTO→model. - Widget: login states (idle/loading/error/rate/offline);
TotpInputpaste/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;
constconstructors; QR image lazy-build off-screen; clipboard micro-delay.
12. Proposals flagged to the team
- When server adds first-login-verify gate + 2FA challenge, enable
LoginCubitchallenge state (OQ-1). - When
lockout/family-revoke/recovery-codesland (IMPLEMENTATION_PLAN.md), add the associated screens. - Analytics wiring waits share
99(AnalyticsServiceinterface).