Files
sxchnge/frontend/packages/sxchnge_auth/README.md

7.1 KiB

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):

dependencies:
  sxchnge_auth:
    path: ../packages/sxchnge_auth
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:

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.