78 lines
2.9 KiB
Dart
78 lines
2.9 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
|
|
import '../domain/auth_service.dart';
|
|
|
|
/// Custom URL scheme used to bring the user back into the app after an OAuth
|
|
/// or magic-link round trip on native platforms.
|
|
///
|
|
/// The scheme mirrors the platform bundle identifiers
|
|
/// (`com.glpcompanion.glpcompanion`) and must be registered in the iOS/macOS
|
|
/// `Info.plist`, the Android manifest intent filters, and the Windows runner
|
|
/// so the operating system can route the redirect callback back to the app.
|
|
/// [SupabaseAuth] (bundled with `supabase_flutter`) observes the deep link and
|
|
/// exchanges the returned auth code for a session automatically.
|
|
const kAuthCallbackRedirectUrl =
|
|
'com.glpcompanion.glpcompanion://login-callback/';
|
|
|
|
/// Resolves the redirect URL embedded in OAuth and magic-link flows.
|
|
///
|
|
/// On web the browser returns to the current origin; native platforms use the
|
|
/// registered deep-link scheme above.
|
|
String resolveAuthRedirectUrl() {
|
|
return kIsWeb ? Uri.base.origin : kAuthCallbackRedirectUrl;
|
|
}
|
|
|
|
/// [IAuthService] implementation backed by Supabase GoTrue.
|
|
///
|
|
/// Only passwordless sign-in modes are modeled: federated OAuth (Google,
|
|
/// Apple, Microsoft Identity via Supabase's `azure` provider) and email magic
|
|
/// links. Direct username/password credentials are intentionally absent in
|
|
/// line with the platform's security posture.
|
|
class SupabaseAuthService implements IAuthService {
|
|
/// Creates the service. Pass [client] in tests to inject a mock; production
|
|
/// code resolves the global Supabase client lazily on first use.
|
|
SupabaseAuthService({SupabaseClient? client})
|
|
: _client = client ?? Supabase.instance.client;
|
|
|
|
final SupabaseClient _client;
|
|
|
|
@override
|
|
Future<void> signInWithGoogle() => _signInWithOAuth(OAuthProvider.google);
|
|
|
|
@override
|
|
Future<void> signInWithApple() => _signInWithOAuth(OAuthProvider.apple);
|
|
|
|
/// Microsoft Identity (Entra ID) is exposed by Supabase Auth as the
|
|
/// `azure` OAuth provider.
|
|
@override
|
|
Future<void> signInWithMicrosoft() => _signInWithOAuth(OAuthProvider.azure);
|
|
|
|
@override
|
|
Future<void> signInWithMagicLink(String email) async {
|
|
await _client.auth.signInWithOtp(
|
|
email: email,
|
|
emailRedirectTo: resolveAuthRedirectUrl(),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<void> signOut() => _client.auth.signOut();
|
|
|
|
/// Emits the authenticated user's id on every auth transition and `null`
|
|
/// once the user is signed out.
|
|
@override
|
|
Stream<String?> get authStateChanges =>
|
|
_client.auth.onAuthStateChange.map((state) => state.session?.user.id);
|
|
|
|
Future<void> _signInWithOAuth(OAuthProvider provider) async {
|
|
final launched = await _client.auth.signInWithOAuth(
|
|
provider,
|
|
redirectTo: resolveAuthRedirectUrl(),
|
|
);
|
|
if (!launched) {
|
|
throw AuthException('Could not launch the ${provider.name} sign-in page.');
|
|
}
|
|
}
|
|
}
|