diff options
Diffstat (limited to 'player-android/lib/providers/auth_state_provider.dart')
| -rw-r--r-- | player-android/lib/providers/auth_state_provider.dart | 41 |
1 files changed, 26 insertions, 15 deletions
diff --git a/player-android/lib/providers/auth_state_provider.dart b/player-android/lib/providers/auth_state_provider.dart index 0e0c6fb..96c89d8 100644 --- a/player-android/lib/providers/auth_state_provider.dart +++ b/player-android/lib/providers/auth_state_provider.dart @@ -1,6 +1,5 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import 'api_client_provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; /// All possible authentication states for the app. /// @@ -56,31 +55,43 @@ class AuthState { /// [AsyncNotifier] is used because the initial state check is async (it reads /// the secure token store). Downstream consumers can call [login] and /// [logout] to drive route redirects via the router's [refreshListenable]. +// SharedPreferences key for the session-presence marker. Used in place of the +// previous bearer-token-in-secure-storage hack: the server authenticates the +// session via an HttpOnly cookie, so the client has no token to persist. We +// only need a tiny boolean to drive the router redirect on cold start. +const _kAuthSessionPresentKey = 'auth_session_present'; + class AuthStateNotifier extends AsyncNotifier<AuthState> { @override Future<AuthState> build() async { - // Determine whether a token already exists on app startup. This drives - // the initial route decision inside the go_router redirect callback. - final storage = ref.read(tokenStorageProvider); - final token = await storage.readToken(); - - return token != null && token.isNotEmpty + // The auth state on cold start is derived from a SharedPreferences marker + // rather than from any stored bearer token. Writing the username into + // SecureTokenStorage (the previous behaviour) caused _AuthInterceptor to + // attach `Authorization: Bearer <username>` to every request, which the + // server checks before falling back to the session cookie — yielding 401 + // on every API call after login despite a valid cookie being sent. + final prefs = await SharedPreferences.getInstance(); + final marked = prefs.getBool(_kAuthSessionPresentKey) ?? false; + return marked ? const AuthState.authenticated() : const AuthState.unauthenticated(); } - /// Called after a successful login; persists [token] and updates state. + /// Called after a successful login. The [token] parameter is accepted for + /// backwards compatibility with the call site but is intentionally unused; + /// the real authentication artefact is the session cookie set by the server + /// and stored by the Dio CookieManager. See [build] for why. Future<void> login(String token) async { - final storage = ref.read(tokenStorageProvider); - await storage.writeToken(token); + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool(_kAuthSessionPresentKey, true); state = const AsyncData(AuthState.authenticated()); } - /// Called on explicit logout or after [_UnauthorizedInterceptor] purges the - /// token. Clears the stored token and moves to the unauthenticated state. + /// Called on explicit logout or after the API returns 401. Clears the + /// session marker so the next cold start redirects to /login. Future<void> logout() async { - final storage = ref.read(tokenStorageProvider); - await storage.deleteToken(); + final prefs = await SharedPreferences.getInstance(); + await prefs.remove(_kAuthSessionPresentKey); state = const AsyncData(AuthState.unauthenticated()); } } |
