withAuth<T> method

Future<T> withAuth<T>(
  1. Future<T> call(), {
  2. List<PortalServiceCode> sso = const [],
})

Executes call with automatic re-authentication on session expiry.

If sso is provided, ensures SSO sessions are established for the listed services before calling call. SSO state is cached per service and only re-established when the portal session is refreshed.

If call fails with a non-DioException error (indicating session expiry or auth failure), this method re-authenticates using stored credentials, re-establishes SSO sessions, and retries call once. Concurrent re-authentication attempts coalesce — only the first caller triggers the actual login; subsequent callers await the same result.

On auth failure (missing or rejected credentials), destroys the session (triggering router guard redirect) and returns a never-completing future. Callers only need to handle DioException for network errors.

Implementation

Future<T> withAuth<T>(
  Future<T> Function() call, {
  List<PortalServiceCode> sso = const [],
}) async {
  try {
    try {
      await _ensureSso(sso);
      return await call();
    } on DioException catch (e) {
      // Dio wraps all interceptor exceptions; unwrap SessionExpiredException
      // so the outer catch-all triggers re-authentication.
      if (e.error is SessionExpiredException) {
        Error.throwWithStackTrace(e.error!, e.stackTrace);
      }
      rethrow;
    }
  } on DioException {
    rethrow;
  } catch (_) {
    try {
      await _reauthenticate();
    } on _AuthFailedException {
      return Completer<T>().future;
    }

    await _ensureSso(sso);
    return await call();
  }
}