import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import '../../domain/entities/pos_session.dart'; /// Where the signed-in session lives between launches. /// /// The platform keystore, not SQLite — Keychain on macOS, Credential Manager /// on Windows, the Android Keystore on a tablet. The response carries a bearer /// token and every staff PIN in the clear, and the SQLite file sits on a /// machine behind a shop counter readable by anything that can open it. /// /// Stored as one blob rather than field by field so [clear] is a single /// delete. A sign-out that leaves half a session behind is worse than one that /// leaves none. class SessionStore { SessionStore({FlutterSecureStorage? secureStorage}) : _secure = secureStorage ?? const FlutterSecureStorage(); final FlutterSecureStorage _secure; static const String _key = 'pos.session'; /// The stored session, or null if there is none, it cannot be read, or it /// has expired. /// /// An expired token is treated as absent and swept: carrying it forward only /// moves the failure to the first call that uses it, which is a cashier /// discovering it mid-sale rather than at the login screen. Future read() async { String? raw; try { raw = await _secure.read(key: _key); } on Object catch (e) { // No keystore — a headless test host, or a Linux box with no secret // service. The terminal still runs, it just asks for credentials. debugPrint('SessionStore: keystore unavailable ($e)'); return null; } if (raw == null || raw.isEmpty) return null; PosSession session; try { session = PosSession.fromJson(jsonDecode(raw) as Map); } on Object catch (e) { // A blob this build cannot parse — an upgrade that changed the shape. // Drop it rather than failing every launch from here on. debugPrint('SessionStore: unreadable session dropped ($e)'); await clear(); return null; } if (session.token.isEmpty || session.isExpired) { await clear(); return null; } return session; } Future save(PosSession session) async { try { await _secure.write(key: _key, value: jsonEncode(session.toJson())); } on Object catch (e) { // Not fatal: the session is live in memory and this shift carries on. // The next launch just asks for credentials again. debugPrint('SessionStore: could not persist session ($e)'); } } /// Removes the session. Called on every sign-out, and on an expired or /// rejected token. Future clear() async { try { await _secure.delete(key: _key); } on Object catch (e) { debugPrint('SessionStore: could not clear session ($e)'); } } }