summaryrefslogtreecommitdiff
path: root/player-android/lib/providers/auth_state_provider.dart
diff options
context:
space:
mode:
Diffstat (limited to 'player-android/lib/providers/auth_state_provider.dart')
-rw-r--r--player-android/lib/providers/auth_state_provider.dart41
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());
}
}