supabase / supabase-flutter

Flutter integration for Supabase. This package makes it simple for developers to build secure and scalable products.
https://supabase.com/
MIT License
742 stars 184 forks source link

Flutter: Apple sign-up does not work #955

Open abhayvc opened 5 months ago

abhayvc commented 5 months ago

Describe the bug In my Flutter application everything was working. Sign-up/Sign-In using apple ID. When I released the bundle on app store, apple rejected it, saying the functionality is incomplete and Sign-in using apple id is not working. And now it HAS stopped working.

In logs/debug prints, I get id-token successfully, that means Apple is authenticating the request, but all the other fields in the credentials are null. I have a trigger that creates an entry in supabase table upon successful entry in auth/Users. I understand that the trigger may fail but even the entry is auth/Users fails since there is no email/name in the credentials returned or somehow the sign_in_with_apple or supabase_flutter libraries are not communicating with each other properly.

If I sign-up user using email id that is same as apple id and then subsequently try to login, the login is successful. Not sure how to get out of this soup.

Expected behavior Even when user does not exist in auth/Users after trying to signin with apple, the entry should be made in that table and user should be created/signed-up.

Screenshots Simulator Screenshot - iPhone 15 - 2024-06-22 at 22 57 05

Simulator Screenshot - iPhone 15 - 2024-06-22 at 22 57 15

Simulator Screenshot - iPhone 15 - 2024-06-22 at 22 57 23

Simulator Screenshot - iPhone 15 - 2024-06-22 at 22 57 30

The scaffold error is my exception handling. This is what I see on debugPrint

flutter: object idToken eyJraWQiOiJUOHRJSjF6U3JPIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoibmV0LnNhYXJpbmMud2V0aGVmcmllbmRzIiwiZXhwIjoxNzE5MTk3MDE2LCJpYXQiOjE3MTkxMTA2MTYsInN1YiI6IjAwMTM5Ni5mN2I1NTk0ZDgwMmM0OTU0YTgxMjI3NWI1ZjliZmI0Yy4xNTQ4Iiwibm9uY2UiOiJkY2RkM2RhMDM3ZmNmYTU0NDgzZWQwZWQ1MmRhNWI4YjQ2MjNiNmQ0NTljNTFhOGNjNzhmMzJhYzRjNWFmMWI0IiwiY19oYXNoIjoibTUtdWRxR19sQUo0N2xETk9DMjkyUSIsImF1dGhfdGltZSI6MTcxOTExMDYxNiwibm9uY2Vfc3VwcG9ydGVkIjp0cnVlfQ.KhLzpDqyZFNyGuWXwyRiRXpLtSZeAW5cwnj10OecepDsr4gnbsDxSwbWF3qAkUOHobYMaEgXNCVS70Oe4MG4u27jxZYWALxvLAJuSYYFpNc87ZBz32Uw0y5I8mQrlw-Dmoo_MuafTMzB8v_iJhE9rswL8QRIwCPqi02DKcOXk5YrhmCJkC-MpfY8IDoHyNfsldCGlI19Sz11x4EpRaM4tKhgZcINVb6TODpwN8VGjVpOdezrEUQrAI3CB4AF6qdaNhsOvAyURp_tNMJN4VLAOsmFvleJRnp0TnZ-1FvYDynaeNsXRqocmfiW0Do2kH_Jpmv9P8qtM2PZyf_sQ7Ww flutter: object email null flutter: Apple Sign-In Error: AuthException(message: Internal Server Error, statusCode: 400)

Version (please complete the following information): On Linux/macOS Please run dart pub deps | grep -E "supabase|gotrue|postgrest|storage_client|realtime_client|functions_client" in your project directory and paste the output here.

├── supabase_flutter 2.5.6 │ ├── supabase 2.2.2 │ │ ├── functions_client 2.2.0 │ │ ├── gotrue 2.8.1 │ │ ├── postgrest 2.1.2 │ │ ├── realtime_client 2.1.0 │ │ ├── storage_client 2.0.2

On Windows Please run dart pub deps | findstr "supabase gotrue postgrest storage_client realtime_client functions_client" in your project directory and paste the output here.

Additional context Add any other context about the problem here.

dshukertjr commented 5 months ago

The trigger sounds like the cause. If the trigger fails, the sign-in fails.

Could you share the definition of your trigger?

abhayvc commented 5 months ago

Hi Tyler,

I deduced that from the logs and I made adjustments to the trigger to make it work but that is a work around. If I do not get name and/or email from apple, then I use a dummy value and prompt users to change it in account settings. But that is still a work around and not the best user experience.

This is how I modified the trigger function:

begin insert into public.user_profiles (id, name, email, bio) values ( new.id, coalesce(new.raw_user_meta_data->>'full_name', new.raw_user_meta_data->>'name', 'Unknown'), coalesce(new.raw_user_meta_data->>'email', new.email, 'unknown@unknown.com'), new.raw_user_meta_data->>'bio' ); return new; end;

I can close this request but I have a question do we know for sure in raw_user_meta_data does apple send name in "name" or "full_name" tag? I know for sure that google sends same value in both the tags. Login with google never fails. Login with Apple is inconsistent. Besides apple sends username and email only for with the first response. Now, I want to delete a user and all his/her data completely when a user decides to delete an account. If the users decides to create an account again, apple only sends id token but no email or username that makes it tricky. I'm not sure if people have encountered such problems before or if they have tested such scenarios.

I will wait for your response and then will close it. Thanks for getting back to me.

Thanks, Abhay

dshukertjr commented 5 months ago

Besides apple sends username and email only for with the first response.

Yup, that's how Apple sign-in works, and there is not much anyone besides Apple to do anything about it.

abhayvc commented 5 months ago

Thanks again!

abhayvc commented 1 month ago

Hello Tyler,

I believe the documentation around this functionality is incomplete. When we invoke: final credential = await SignInWithApple.getAppleIDCredential( scopes: [ AppleIDAuthorizationScopes.email, AppleIDAuthorizationScopes.fullName, ], webAuthenticationOptions: WebAuthenticationOptions( redirectUri: Uri.parse( 'https://XXXXXX.supabase.co/auth/v1/callback', ), clientId: 'XXXXXXXX', ), nonce: hashedNonce, ); The returned credentials do contain credential.givenName and credential.familyName however credential.identityToken does not contain that information. And that is why I never saw that information in Authentication table and I'm not sure if we can ever achieve it only by using:

final authResponse = await supaClient.auth.signInWithIdToken( provider: OAuthProvider.apple, idToken: idToken, nonce: rawNonce, );

Even if we write a trigger in Supabase, since identityToken does not have name, it will never get there. Hence I let the trigger populate the "user_profiles" table (since it works for sign in with google). An additional step is needed for apple sign up. I achieved it as below:

final userId = authResponse.session?.user.id; if (userId != null) { debugPrint('object userId $userId'); if (credential.givenName != null && credential.givenName!.isNotEmpty && credential.familyName != null && credential.familyName!.isNotEmpty) { debugPrint( 'object username ${credential.givenName} ${credential.familyName}'); final username = '${credential.givenName} ${credential.familyName}'; await supaClient.from('user_profiles').update({ 'name': username, }).eq('id', userId); } }

Please let me know if I am doing something wrong, if not, including this in documentation can help future developers a big deal.

Thanks, Abhay