84 lines
2.4 KiB
Dart
84 lines
2.4 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
|
|
/// A guardian login request waiting for its code or link. Persisted because
|
|
/// Android often kills the app while the user reads the mail.
|
|
class PendingGuardianRequest {
|
|
static const int defaultCodeLength = 6;
|
|
|
|
final String requestId;
|
|
final String email;
|
|
final String deviceSecret;
|
|
final DateTime expiresAt;
|
|
final DateTime resendAvailableAt;
|
|
final int codeLength;
|
|
|
|
const PendingGuardianRequest({
|
|
required this.requestId,
|
|
required this.email,
|
|
required this.deviceSecret,
|
|
required this.expiresAt,
|
|
required this.resendAvailableAt,
|
|
this.codeLength = defaultCodeLength,
|
|
});
|
|
|
|
bool isExpired(DateTime now) => !now.isBefore(expiresAt);
|
|
|
|
Map<String, Object> toJson() => {
|
|
'requestId': requestId,
|
|
'email': email,
|
|
'deviceSecret': deviceSecret,
|
|
'expiresAt': expiresAt.toIso8601String(),
|
|
'resendAvailableAt': resendAvailableAt.toIso8601String(),
|
|
'codeLength': codeLength,
|
|
};
|
|
|
|
static PendingGuardianRequest? fromJson(Map<String, dynamic> json) {
|
|
try {
|
|
return PendingGuardianRequest(
|
|
requestId: json['requestId'] as String,
|
|
email: json['email'] as String,
|
|
deviceSecret: json['deviceSecret'] as String,
|
|
expiresAt: DateTime.parse(json['expiresAt'] as String),
|
|
resendAvailableAt: DateTime.parse(json['resendAvailableAt'] as String),
|
|
codeLength: json['codeLength'] as int? ?? defaultCodeLength,
|
|
);
|
|
} on Object {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
class PendingGuardianRequestStore {
|
|
static const _key = 'guardian_login_pending_request';
|
|
static const FlutterSecureStorage _storage = FlutterSecureStorage(
|
|
iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock),
|
|
);
|
|
|
|
const PendingGuardianRequestStore();
|
|
|
|
Future<PendingGuardianRequest?> read() async {
|
|
try {
|
|
final raw = await _storage.read(key: _key);
|
|
if (raw == null) return null;
|
|
return PendingGuardianRequest.fromJson(
|
|
jsonDecode(raw) as Map<String, dynamic>,
|
|
);
|
|
} on Object {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
Future<void> write(PendingGuardianRequest request) =>
|
|
_storage.write(key: _key, value: jsonEncode(request.toJson()));
|
|
|
|
Future<void> clear() async {
|
|
try {
|
|
await _storage.delete(key: _key);
|
|
} on Object {
|
|
// Nothing stored or keystore unavailable.
|
|
}
|
|
}
|
|
}
|