k_card/k_phone/lib/session_manager.dart

70 lines
2.1 KiB
Dart

// Session token management — mirrors k_proxy_app.py session logic.
// Tokens are 32-byte hex strings; stored in memory only.
import 'dart:math';
class SessionEntry {
final String username;
final DateTime expires;
SessionEntry({required this.username, required this.expires});
}
class SessionManager {
final Map<String, SessionEntry> _sessions = {};
static const Duration _ttl = Duration(seconds: 300);
/// Issue a new session token for [username].
/// _purgeExpired is only called here, not on every lookup, so tokens accumulate
/// until the next login — acceptable for the low-traffic embedded use case.
String issue(String username) {
_purgeExpired();
final token = _randomToken();
_sessions[token] = SessionEntry(
username: username,
expires: DateTime.now().add(_ttl),
);
return token;
}
/// Returns the session entry for [token], or null if missing/expired.
SessionEntry? getSession(String token) {
final s = _sessions[token];
if (s == null) return null;
if (DateTime.now().isAfter(s.expires)) {
_sessions.remove(token);
return null;
}
return s;
}
/// Returns true if [token] is known and not expired.
bool isValid(String token) => getSession(token) != null;
/// Revoke [token] immediately.
void revoke(String token) => _sessions.remove(token);
/// Returns true if at least one session is currently active (not expired).
/// Used by gated-proxy forwarding: personal-device model means any live
/// login counts as authorisation for the proxied request.
bool hasAnyActiveSession() {
_purgeExpired();
return _sessions.isNotEmpty;
}
/// Revoke all sessions for [username].
void revokeAll(String username) {
_sessions.removeWhere((_, s) => s.username == username);
}
void _purgeExpired() {
final now = DateTime.now();
_sessions.removeWhere((_, s) => now.isAfter(s.expires));
}
String _randomToken() {
final rng = Random.secure();
final bytes = List.generate(32, (_) => rng.nextInt(256));
return bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
}
}