102 lines
7.1 KiB
Markdown
102 lines
7.1 KiB
Markdown
# sxchnge_auth
|
|
|
|
Shared Flutter authentication providers and service contracts for web and mobile. Private package, version 0.1.0; Flutter 3.47+ / Dart 3.13+.
|
|
|
|
## Install and use
|
|
|
|
From `frontend/app` (or a sibling `frontend/mobile` app):
|
|
|
|
```yaml
|
|
dependencies:
|
|
sxchnge_auth:
|
|
path: ../packages/sxchnge_auth
|
|
```
|
|
|
|
```dart
|
|
import 'package:sxchnge_auth/sxchnge_auth.dart';
|
|
|
|
final login = LoginProvider(const DummyLoginService());
|
|
login.addListener(() {
|
|
// Render login.state.status, result, or typed error in your own UI.
|
|
});
|
|
await login.login(
|
|
email: 'demo@example.com',
|
|
password: 'example-password',
|
|
locale: 'en',
|
|
);
|
|
login.dispose();
|
|
```
|
|
|
|
`SignupProvider` accepts a `SignupService` and exposes `signup(name:, email:, password:, locale:)`. Both providers extend ChangeNotifier and can be installed with `ChangeNotifierProvider`, ListenableBuilder, or another Flutter state binding. The package includes providers and a reusable Material two-factor page. Clients own routes and localization; the package contains no web-only code, network client, or storage.
|
|
|
|
## Lifecycle and services
|
|
|
|
Providers expose immutable `AuthState`: idle, loading, challenge, success, or error. They trim email/name, preserve the password verbatim, pass the locale to services, suppress duplicate submissions while loading, and clear old errors before retrying. Reset invalidates older responses and allows a new request; disposal ignores all later completions. Reset does not cancel remote work. Do not treat a provider reset as undoing a production signup.
|
|
|
|
Services return `AuthResult` or throw `AuthException` with a typed `AuthError`. Unexpected exceptions are reduced to `unexpected`; raw errors and credentials are not retained in provider state. Credentials exist only as ephemeral request input. Localize error codes in each consuming application.
|
|
|
|
`DummyLoginService` and `DummySignupService` default to 600ms latency and simulated success. They do not validate credentials against an account registry, create accounts, issue tokens, open sessions, send requests, or persist data. Configure errors explicitly:
|
|
|
|
```dart
|
|
final service = DummyLoginService(
|
|
delay: Duration.zero,
|
|
failure: AuthError.invalidCredentials,
|
|
);
|
|
```
|
|
|
|
Supported failure codes: `invalidCredentials`, `emailInUse`, `unavailable`, and `unexpected`. Inject another implementation for deterministic retry scenarios or production integration. `AuthResult.isSimulated` distinguishes dummy responses; a successful operation alone must never be treated as an authorization decision. The package does not implement authenticated application/session state yet.
|
|
|
|
## Sendico reuse
|
|
|
|
Adapted the ChangeNotifier lifecycle, service separation, locale-carrying request, and typed-outcome patterns from Sendico's `pshared/lib/provider/account.dart`, `models/auth/login_outcome.dart`, and `service/authorization/client.dart`. These were patterns rather than directly portable implementations: the originals depend on Sendico account/organization models, device IDs, static API services, token storage, and verification flows. Those dependencies are deliberately absent here. The B2B organization signup contract is not copied into the consumer product.
|
|
|
|
When a supported sxchnge BFF contract exists, implement transport DTOs and mappers behind the service interfaces, returning domain results. Dummy responses also pass through explicit DTOs and mappers, following the repository architecture. Keep web cookie and mobile credential handling in production platform adapters, not the providers.
|
|
|
|
## Validation
|
|
|
|
`flutter analyze` and `flutter test` from this directory; `make test-frontend` from the repository root also runs all consuming app and theme tests. Tests cover duplicate submission, request forwarding, typed/unexpected failures, retry, reset races, disposal, and both dummy implementations.
|
|
|
|
## Email verification
|
|
|
|
`VerificationService` exposes `send(email, locale)` and `confirm(token)` as named-argument methods. Inject an implementation into `VerificationProvider`, shared by web and mobile. The provider exposes immutable `VerificationState` (idle/sending/sent/checking/verified/expired/invalid/error), suppresses concurrent duplicate operations, and ignores completions after reset or disposal. Typed `VerificationException` maps confirmation failures; unknown exceptions become internal errors without exposing exception text. Sending failures always become the general error state.
|
|
|
|
`DummyVerificationService` supports `demo-valid`, `demo-expired`, and `demo-internal` confirmation tokens; other tokens are invalid. Configure `delay` and `sendFailure` for tests. Results are explicitly simulated. It performs no network, storage, mail delivery, or account mutation. A production adapter must implement backend validation, delivery, rate limits, and safe token handling; this client provider is not an authorization boundary.
|
|
|
|
`InstrumentedAuthServices` decorates the three auth service interfaces with a
|
|
shared `JourneyRecorder` from `sxchnge_analytics`. It preserves results/exceptions
|
|
for providers while emitting only typed semantic outcomes. `SimulationInfo`
|
|
allows decorators and UI to identify preview services before the first result.
|
|
|
|
## Two-factor verification
|
|
|
|
`TwoFactorService` owns settings, challenge creation/resend, verification and
|
|
one-use approval consumption. DTOs map to `FactorSettings`, `FactorChallenge`
|
|
and `FactorGrant`; `SecurityProvider` and `TwoFactorProvider` coordinate state.
|
|
`TwoFactorPage` accepts the provider, client-localized `TwoFactorLabels`, callbacks
|
|
and an optional transaction summary. Its route owner must dispose the provider;
|
|
the page disposes its own code/focus controllers. No code or grant is persisted.
|
|
|
|
`FactorMethod.email` shows masked delivery and resend controls;
|
|
`FactorMethod.authenticator` uses app-code instructions without delivery UI.
|
|
The contract is ready for a future authenticator/TOTP adapter. Enrollment, secret
|
|
provisioning, method selection and recovery require a backend contract first.
|
|
|
|
`GuardedLoginService` wraps a credentials service and exposes a pending challenge
|
|
instead of successful login. `LoginProvider.complete` completes through
|
|
`PendingLoginService` only after scope-bound authorization. Production adapters
|
|
must return backend-owned pending login results and enforce session issuance
|
|
there; this local wrapper is not a production authorization boundary.
|
|
|
|
The initial `DummyTwoFactorService` is disabled by default. Enable it in account
|
|
settings using fixture code **123456**. It resets on reload, sends no email and
|
|
does not validate real TOTP. Challenges expire after five minutes; email delivery
|
|
has a 30-second cooldown; five wrong attempts lock that purpose for five minutes.
|
|
Resending cannot reset attempts. Tests can inject `now`, `delay`, `method`,
|
|
`enabled` and `nextError` to exercise states.
|
|
|
|
Use the same service instance for login, settings and the dummy wallet. A custom
|
|
`WalletService` must validate the passed approval at execution and preserve
|
|
idempotency; attaching the page alone does not protect funds. Production checks
|
|
must bind approvals to the authenticated account and immutable operation details,
|
|
validate expiry/policy, and atomically consume approval with execution.
|