fix: added reauth popup when using soo (#174)
This commit is contained in:
@@ -12,6 +12,7 @@ import 'package:http/io_client.dart';
|
||||
import 'package:calibre_web_companion/core/exceptions/redirect_exception.dart';
|
||||
import 'package:calibre_web_companion/core/services/connection_diagnostics.dart';
|
||||
import 'package:calibre_web_companion/core/services/digest_auth.dart';
|
||||
import 'package:calibre_web_companion/core/services/session_reauth_service.dart';
|
||||
import 'package:calibre_web_companion/features/book_view/data/datasources/book_view_remote_datasource.dart';
|
||||
|
||||
enum AuthMethod { none, cookie, basic, auto }
|
||||
@@ -32,6 +33,7 @@ class ApiService {
|
||||
String? _userAgent;
|
||||
|
||||
bool _allowSelfSigned = false;
|
||||
bool _isSsoSession = false;
|
||||
Future<bool>? _reauthFuture;
|
||||
final DigestAuth _digest = DigestAuth();
|
||||
|
||||
@@ -104,6 +106,7 @@ class ApiService {
|
||||
_basePath = prefs.getString('base_path') ?? '';
|
||||
_userAgent = prefs.getString('user_agent'); // User Agent laden
|
||||
_allowSelfSigned = prefs.getBool('allow_self_signed') ?? false;
|
||||
_isSsoSession = prefs.getBool('is_sso_session') ?? false;
|
||||
|
||||
_digest.reset();
|
||||
|
||||
@@ -292,6 +295,10 @@ class ApiService {
|
||||
|
||||
_logger.e('Invalid JSON from "$endpoint" (status ${response.statusCode})');
|
||||
if (_looksLikeHtml(response.body)) {
|
||||
if (_isSsoSession) {
|
||||
_logger.w('SSO session lost; requesting re-auth web view.');
|
||||
unawaited(SessionReauthService().requestReauth());
|
||||
}
|
||||
throw Exception(
|
||||
'Session not accepted by the server (received a login page instead of data).',
|
||||
);
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'dart:async';
|
||||
|
||||
class SessionReauthService {
|
||||
static final SessionReauthService _instance =
|
||||
SessionReauthService._internal();
|
||||
factory SessionReauthService() => _instance;
|
||||
SessionReauthService._internal();
|
||||
|
||||
Future<bool> Function()? _handler;
|
||||
Future<bool>? _inFlight;
|
||||
|
||||
void registerHandler(Future<bool> Function()? handler) {
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
Future<bool> requestReauth() {
|
||||
final existing = _inFlight;
|
||||
if (existing != null) return existing;
|
||||
|
||||
final handler = _handler;
|
||||
if (handler == null) return Future.value(false);
|
||||
|
||||
final future = handler();
|
||||
_inFlight = future;
|
||||
return future.whenComplete(() => _inFlight = null);
|
||||
}
|
||||
}
|
||||
@@ -168,6 +168,7 @@ class LoginRemoteDataSource {
|
||||
final isSuccess = !response.body.contains('flash_danger');
|
||||
|
||||
if (isSuccess) {
|
||||
await prefs.setBool('is_sso_session', false);
|
||||
if (response.headers.containsKey('set-cookie')) {
|
||||
final cookie = response.headers['set-cookie']!;
|
||||
await prefs.setString('calibre_web_session', cookie);
|
||||
@@ -341,6 +342,7 @@ class LoginRemoteDataSource {
|
||||
await prefs.remove('user_agent');
|
||||
await prefs.remove('calibre_library_id');
|
||||
await prefs.remove('calibre_library_map');
|
||||
await prefs.remove('is_sso_session');
|
||||
await apiService.reset();
|
||||
}
|
||||
|
||||
@@ -545,6 +547,7 @@ class LoginRemoteDataSource {
|
||||
await prefs.setString('user_agent', userAgent);
|
||||
await prefs.setString('calibre_web_cookie', cookieHeader);
|
||||
await prefs.remove('calibre_web_session');
|
||||
await prefs.setBool('is_sso_session', true);
|
||||
|
||||
if (username != null && username.isNotEmpty) {
|
||||
await prefs.setString('username', username);
|
||||
|
||||
@@ -19,6 +19,7 @@ class WebViewLoginPage extends StatefulWidget {
|
||||
final String baseUrl;
|
||||
final String? username;
|
||||
final String? password;
|
||||
final bool isReauth;
|
||||
|
||||
const WebViewLoginPage({
|
||||
super.key,
|
||||
@@ -26,6 +27,7 @@ class WebViewLoginPage extends StatefulWidget {
|
||||
required this.baseUrl,
|
||||
this.username,
|
||||
this.password,
|
||||
this.isReauth = false,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -164,12 +166,17 @@ class _WebViewLoginPageState extends State<WebViewLoginPage> {
|
||||
return BlocListener<LoginBloc, LoginState>(
|
||||
listener: (context, state) {
|
||||
if (state.status == LoginStatus.success) {
|
||||
_logger.i('SSO Login finalized via Bloc. Navigating home.');
|
||||
context.showSnackBar(localizations.loginSuccessfull, isError: false);
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
MaterialPageRoute(builder: (context) => const HomePage()),
|
||||
(route) => false,
|
||||
);
|
||||
if (widget.isReauth) {
|
||||
_logger.i('SSO session renewed via Bloc. Returning to caller.');
|
||||
Navigator.of(context).pop(true);
|
||||
} else {
|
||||
_logger.i('SSO Login finalized via Bloc. Navigating home.');
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
MaterialPageRoute(builder: (context) => const HomePage()),
|
||||
(route) => false,
|
||||
);
|
||||
}
|
||||
} else if (state.status == LoginStatus.failure) {
|
||||
_logger.e('SSO Finalization failed: ${state.errorMessage}');
|
||||
setState(() => _isExtracting = false);
|
||||
|
||||
@@ -343,6 +343,7 @@
|
||||
"ok": "Ok",
|
||||
"attentionSSLCertificate": "Achtung: Selbstsignierte Zertifikate können unsicher sein. Verwende sie nur, wenn du die Risiken verstehst.",
|
||||
"ssoLogin": "SSO-Anmeldung",
|
||||
"sessionExpiredReauth": "Sitzung abgelaufen – bitte erneut anmelden",
|
||||
"pleaseLoginWithYourSSOAccount": "Bitte melde dich mit deinem SSO-Konto an. Du wirst nach der Anmeldung automatisch zur App zurückgeleitet.",
|
||||
"loginWithSSO": "Mit SSO anmelden",
|
||||
"pleaseEnterSSOUrl": "Bitte gib die SSO-URL ein",
|
||||
|
||||
@@ -343,6 +343,7 @@
|
||||
"ok": "Ok",
|
||||
"attentionSSLCertificate": "Attention: Self-signed certificates can be insecure. Use them only if you understand the risks.",
|
||||
"ssoLogin": "SSO-Login",
|
||||
"sessionExpiredReauth": "Session expired — please sign in again",
|
||||
"pleaseLoginWithYourSSOAccount": "Please login with your SSO account. You will be redirected back to the app after successful login.",
|
||||
"loginWithSSO": "Login with SSO",
|
||||
"pleaseEnterSSOUrl": "Please enter the SSO URL",
|
||||
|
||||
@@ -15,6 +15,9 @@ import 'package:calibre_web_companion/l10n/app_localizations.dart';
|
||||
import 'package:calibre_web_companion/core/di/injection_container.dart' as di;
|
||||
import 'package:calibre_web_companion/core/services/api_service.dart';
|
||||
import 'package:calibre_web_companion/core/services/app_transition.dart';
|
||||
import 'package:calibre_web_companion/core/services/session_reauth_service.dart';
|
||||
import 'package:calibre_web_companion/core/services/snackbar.dart';
|
||||
import 'package:calibre_web_companion/features/login/presentation/widgets/web_view_login_page.dart';
|
||||
import 'package:calibre_web_companion/core/services/connectivity_service.dart';
|
||||
import 'package:calibre_web_companion/core/services/widget_service.dart';
|
||||
import 'package:calibre_web_companion/core/services/download_manager.dart';
|
||||
@@ -174,14 +177,44 @@ class _MyAppState extends State<MyApp> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_setupWidgetLaunch();
|
||||
SessionReauthService().registerHandler(_openSsoReauth);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_widgetClickSub?.cancel();
|
||||
SessionReauthService().registerHandler(null);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<bool> _openSsoReauth() async {
|
||||
final navigator = navigatorKey.currentState;
|
||||
if (navigator == null) return false;
|
||||
|
||||
final prefs = getIt<SharedPreferences>();
|
||||
final baseUrl = prefs.getString('base_url');
|
||||
if (baseUrl == null || baseUrl.isEmpty) return false;
|
||||
|
||||
final message = AppLocalizations.of(navigator.context)?.sessionExpiredReauth;
|
||||
if (message != null) {
|
||||
navigator.context.showSnackBar(message);
|
||||
}
|
||||
|
||||
final result = await navigator.push<bool>(
|
||||
MaterialPageRoute(
|
||||
builder:
|
||||
(_) => WebViewLoginPage(
|
||||
redirectUrl: baseUrl,
|
||||
baseUrl: baseUrl,
|
||||
username: prefs.getString('username'),
|
||||
password: prefs.getString('password'),
|
||||
isReauth: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
return result ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
|
||||
Reference in New Issue
Block a user