123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- import 'dart:async';
- import 'package:appflowy/env/env.dart';
- import 'package:appflowy/user/application/auth/appflowy_auth_service.dart';
- import 'package:appflowy/user/application/auth/auth_service.dart';
- import 'package:appflowy/user/application/user_service.dart';
- import 'package:appflowy_backend/dispatch/dispatch.dart';
- import 'package:appflowy_backend/log.dart';
- import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
- import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
- import 'package:dartz/dartz.dart';
- import 'package:supabase_flutter/supabase_flutter.dart';
- import 'auth_error.dart';
- class SupabaseAuthService implements AuthService {
- SupabaseAuthService();
- SupabaseClient get _client => Supabase.instance.client;
- GoTrueClient get _auth => _client.auth;
- final AppFlowyAuthService _appFlowyAuthService = AppFlowyAuthService();
- @override
- Future<Either<FlowyError, UserProfilePB>> signUp({
- required String name,
- required String email,
- required String password,
- AuthTypePB authType = AuthTypePB.Supabase,
- Map<String, String> map = const {},
- }) async {
- if (!isSupabaseEnable) {
- return _appFlowyAuthService.signUp(
- name: name,
- email: email,
- password: password,
- );
- }
- // fetch the uuid from supabase.
- final response = await _auth.signUp(
- email: email,
- password: password,
- );
- final uuid = response.user?.id;
- if (uuid == null) {
- return left(AuthError.supabaseSignUpError);
- }
- // assign the uuid to our backend service.
- // and will transfer this logic to backend later.
- return _appFlowyAuthService.signUp(
- name: name,
- email: email,
- password: password,
- authType: authType,
- map: {
- AuthServiceMapKeys.uuid: uuid,
- },
- );
- }
- @override
- Future<Either<FlowyError, UserProfilePB>> signIn({
- required String email,
- required String password,
- AuthTypePB authType = AuthTypePB.Supabase,
- Map<String, String> map = const {},
- }) async {
- if (!isSupabaseEnable) {
- return _appFlowyAuthService.signIn(
- email: email,
- password: password,
- );
- }
- try {
- final response = await _auth.signInWithPassword(
- email: email,
- password: password,
- );
- final uuid = response.user?.id;
- if (uuid == null) {
- return Left(AuthError.supabaseSignInError);
- }
- return _appFlowyAuthService.signIn(
- email: email,
- password: password,
- authType: authType,
- map: {
- AuthServiceMapKeys.uuid: uuid,
- },
- );
- } on AuthException catch (e) {
- Log.error(e);
- return Left(AuthError.supabaseSignInError);
- }
- }
- @override
- Future<Either<FlowyError, UserProfilePB>> signUpWithOAuth({
- required String platform,
- AuthTypePB authType = AuthTypePB.Supabase,
- Map<String, String> map = const {},
- }) async {
- if (!isSupabaseEnable) {
- return _appFlowyAuthService.signUpWithOAuth(
- platform: platform,
- );
- }
- final provider = platform.toProvider();
- final completer = Completer<Either<FlowyError, UserProfilePB>>();
- late final StreamSubscription<AuthState> subscription;
- subscription = _auth.onAuthStateChange.listen((event) async {
- final user = event.session?.user;
- if (event.event != AuthChangeEvent.signedIn || user == null) {
- completer.complete(left(AuthError.supabaseSignInWithOauthError));
- } else {
- final Either<FlowyError, UserProfilePB> response = await setupAuth(
- map: {
- AuthServiceMapKeys.uuid: user.id,
- AuthServiceMapKeys.email: user.email ?? user.newEmail ?? ''
- },
- );
- completer.complete(response);
- }
- subscription.cancel();
- });
- final Map<String, String> query = {};
- if (provider == Provider.google) {
- query['access_type'] = 'offline';
- query['prompt'] = 'consent';
- }
- final response = await _auth.signInWithOAuth(
- provider,
- queryParams: query,
- redirectTo:
- 'io.appflowy.appflowy-flutter://login-callback', // can't use underscore here.
- );
- if (!response) {
- completer.complete(left(AuthError.supabaseSignInWithOauthError));
- }
- return completer.future;
- }
- @override
- Future<void> signOut({
- AuthTypePB authType = AuthTypePB.Supabase,
- }) async {
- if (!isSupabaseEnable) {
- return _appFlowyAuthService.signOut();
- }
- await _auth.signOut();
- await _appFlowyAuthService.signOut(
- authType: authType,
- );
- }
- @override
- Future<Either<FlowyError, UserProfilePB>> signUpAsGuest({
- AuthTypePB authType = AuthTypePB.Supabase,
- Map<String, String> map = const {},
- }) async {
- // supabase don't support guest login.
- // so, just forward to our backend.
- return _appFlowyAuthService.signUpAsGuest();
- }
- // @override
- // Future<Either<FlowyError, UserProfilePB>> getUser() async {
- // final loginType = await getIt<KeyValueStorage>()
- // .get(KVKeys.loginType)
- // .then((value) => value.toOption().toNullable());
- // if (!isSupabaseEnable || (loginType != null && loginType != 'supabase')) {
- // return _appFlowyAuthService.getUser();
- // }
- // final user = await getSupabaseUser();
- // return user.map((r) => r.toUserProfile());
- // }
- @override
- Future<Either<FlowyError, UserProfilePB>> getUser() async {
- return UserBackendService.getCurrentUserProfile();
- }
- Future<Either<FlowyError, User>> getSupabaseUser() async {
- final user = _auth.currentUser;
- if (user == null) {
- return left(AuthError.supabaseGetUserError);
- }
- return Right(user);
- }
- Future<Either<FlowyError, UserProfilePB>> setupAuth({
- required Map<String, String> map,
- }) async {
- final payload = ThirdPartyAuthPB(
- authType: AuthTypePB.Supabase,
- map: map,
- );
- return UserEventThirdPartyAuth(payload)
- .send()
- .then((value) => value.swap());
- }
- }
- extension on String {
- Provider toProvider() {
- switch (this) {
- case 'github':
- return Provider.github;
- case 'google':
- return Provider.google;
- case 'discord':
- return Provider.discord;
- default:
- throw UnimplementedError();
- }
- }
- }
|