mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
84 lines
2.4 KiB
Dart
84 lines
2.4 KiB
Dart
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
|
|
import '../relay/relay.dart';
|
|
import 'credential_storage.dart';
|
|
|
|
enum AuthStatus { unknown, unauthenticated, authenticated, offline }
|
|
|
|
class AuthState {
|
|
final AuthStatus status;
|
|
final StoredCredentials? credentials;
|
|
|
|
const AuthState({required this.status, this.credentials});
|
|
}
|
|
|
|
class AuthNotifier extends AsyncNotifier<AuthState> {
|
|
@override
|
|
Future<AuthState> build() async {
|
|
final storage = CredentialStorage();
|
|
final creds = await storage.load();
|
|
if (creds == null) {
|
|
return const AuthState(status: AuthStatus.unauthenticated);
|
|
}
|
|
|
|
// Validate stored credentials against the relay.
|
|
final client = RelayClient(baseUrl: creds.relayUrl, apiToken: creds.token);
|
|
try {
|
|
await client.get('/api/users/me/profile');
|
|
// Credentials are valid — propagate to relay config.
|
|
ref
|
|
.read(relayConfigProvider.notifier)
|
|
.update(
|
|
baseUrl: creds.relayUrl,
|
|
apiToken: creds.token,
|
|
devPubkey: null,
|
|
nsec: creds.nsec,
|
|
);
|
|
return AuthState(status: AuthStatus.authenticated, credentials: creds);
|
|
} on RelayException {
|
|
// Token is invalid or expired — clear and require re-pairing.
|
|
await storage.clear();
|
|
return const AuthState(status: AuthStatus.unauthenticated);
|
|
} catch (_) {
|
|
// Network error — keep credentials and let the user retry.
|
|
return AuthState(status: AuthStatus.offline, credentials: creds);
|
|
} finally {
|
|
client.dispose();
|
|
}
|
|
}
|
|
|
|
/// Retry credential validation (e.g. after a network error).
|
|
Future<void> retry() async {
|
|
ref.invalidateSelf();
|
|
await future;
|
|
}
|
|
|
|
Future<void> authenticate(StoredCredentials creds) async {
|
|
final storage = CredentialStorage();
|
|
await storage.save(creds);
|
|
|
|
ref
|
|
.read(relayConfigProvider.notifier)
|
|
.update(
|
|
baseUrl: creds.relayUrl,
|
|
apiToken: creds.token,
|
|
devPubkey: null,
|
|
nsec: creds.nsec,
|
|
);
|
|
|
|
state = AsyncData(
|
|
AuthState(status: AuthStatus.authenticated, credentials: creds),
|
|
);
|
|
}
|
|
|
|
Future<void> signOut() async {
|
|
final storage = CredentialStorage();
|
|
await storage.clear();
|
|
state = const AsyncData(AuthState(status: AuthStatus.unauthenticated));
|
|
}
|
|
}
|
|
|
|
final authProvider = AsyncNotifierProvider<AuthNotifier, AuthState>(
|
|
AuthNotifier.new,
|
|
);
|