131 lines
3.6 KiB
Dart
131 lines
3.6 KiB
Dart
import "dart:async";
|
|
|
|
import "package:flutter/foundation.dart";
|
|
import "package:supabase_flutter/supabase_flutter.dart";
|
|
|
|
SupabaseClient get supabase => Supabase.instance.client;
|
|
|
|
class SupabaseProvider extends ChangeNotifier {
|
|
Session? _session;
|
|
StreamSubscription<AuthState>? _authSub;
|
|
bool _isSessionValidated = false;
|
|
bool _isValidatingSession = false;
|
|
|
|
SupabaseProvider() {
|
|
_session = supabase.auth.currentSession;
|
|
unawaited(_validateSession(_session));
|
|
|
|
_authSub = supabase.auth.onAuthStateChange.listen((data) {
|
|
unawaited(_validateSession(data.session));
|
|
});
|
|
}
|
|
|
|
Session? get session => _session;
|
|
bool get isAuthenticated => _session != null && _isSessionValidated;
|
|
bool get isValidatingSession => _isValidatingSession;
|
|
SupabaseClient get client => supabase;
|
|
|
|
|
|
Future<void> signInWithPassword({
|
|
required String email,
|
|
required String password,
|
|
}) async {
|
|
await supabase.auth.signInWithPassword(email: email, password: password);
|
|
}
|
|
|
|
Future<bool> signUpWithPassword({
|
|
required String email,
|
|
required String password,
|
|
}) async {
|
|
final res = await supabase.auth.signUp(email: email, password: password);
|
|
|
|
// returns true if email confirmation is needed
|
|
return res.session == null;
|
|
}
|
|
|
|
Future<void> verifySignUpOtp({
|
|
required String email,
|
|
required String token,
|
|
}) async {
|
|
await supabase.auth.verifyOTP(
|
|
email: email,
|
|
token: token,
|
|
type: OtpType.signup,
|
|
);
|
|
}
|
|
|
|
Future<void> resendSignUpOtp({required String email}) async {
|
|
await supabase.auth.resend(type: OtpType.signup, email: email);
|
|
}
|
|
|
|
Future<void> signOut() async {
|
|
try {
|
|
await supabase.auth.signOut(scope: SignOutScope.local);
|
|
} catch (error, stackTrace) {
|
|
debugPrint("[SupabaseProvider] signOut failed: $error");
|
|
debugPrintStack(stackTrace: stackTrace);
|
|
}
|
|
|
|
_session = null;
|
|
_isSessionValidated = false;
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> _validateSession(Session? incomingSession) async {
|
|
_session = incomingSession ?? supabase.auth.currentSession;
|
|
|
|
if (_session == null) {
|
|
_isSessionValidated = false;
|
|
_isValidatingSession = false;
|
|
notifyListeners();
|
|
return;
|
|
}
|
|
|
|
_isValidatingSession = true;
|
|
notifyListeners();
|
|
|
|
try {
|
|
final userResponse = await supabase.auth.getUser();
|
|
if (userResponse.user != null) {
|
|
_session = supabase.auth.currentSession ?? _session;
|
|
_isSessionValidated = true;
|
|
_isValidatingSession = false;
|
|
notifyListeners();
|
|
return;
|
|
}
|
|
} catch (error, stackTrace) {
|
|
debugPrint("[SupabaseProvider] validateSession/getUser failed: $error");
|
|
debugPrintStack(stackTrace: stackTrace);
|
|
}
|
|
|
|
try {
|
|
final refreshed = await supabase.auth.refreshSession();
|
|
_session = refreshed.session ?? supabase.auth.currentSession;
|
|
final refreshedUser = await supabase.auth.getUser();
|
|
_isSessionValidated = refreshedUser.user != null;
|
|
} catch (error, stackTrace) {
|
|
debugPrint("[SupabaseProvider] validateSession/refresh failed: $error");
|
|
debugPrintStack(stackTrace: stackTrace);
|
|
_isSessionValidated = false;
|
|
}
|
|
|
|
if (!_isSessionValidated) {
|
|
try {
|
|
await supabase.auth.signOut(scope: SignOutScope.local);
|
|
} catch (error, stackTrace) {
|
|
debugPrint("[SupabaseProvider] validateSession/signOut failed: $error");
|
|
debugPrintStack(stackTrace: stackTrace);
|
|
}
|
|
_session = null;
|
|
}
|
|
|
|
_isValidatingSession = false;
|
|
notifyListeners();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_authSub?.cancel();
|
|
super.dispose();
|
|
}
|
|
}
|