Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistent behavior of providers using GoogleProvider() and AppleProvider() #253

Open
1 task done
anxiety24 opened this issue Jan 9, 2024 · 20 comments
Open
1 task done
Labels
auth blocked: customer response Waiting for customer response, e.g. more information was requested. bug Something isn't working

Comments

@anxiety24
Copy link

Is there an existing issue for this?

  • I have searched the existing issues and found no duplicates.

What plugin is this bug for?

Firebase UI OAuth Google, Firebase UI OAuth Apple

What platform(s) does this bug affect?

Android, iOS

List of dependencies used.

flutter pub deps -s list
  firebase_core: ^2.10.0
  firebase_auth: 4.12.1
  firebase_ui_auth: ^1.1.8
  firebase_ui_oauth: ^1.1.8
  firebase_ui_oauth_google: ^1.0.15
  firebase_ui_oauth_apple: ^1.0.15

Steps to reproduce

Setup the standard SignInScreen of FirebaseUIAuth with successfully configured providers GoogleProvider(clientId:) and AppleProvider() and login the existing user with the signInAnonymously method beforehand.

On the SignInScreen, using the "Sign in with Apple" option, the account is permanently created in the user management and successfully upgraded / linked to the provider. Logging in on another device after that (while being again anonymously logged in before, too) the anonymous login is overwritten in favor of the recently created account, linked with the apple provider.

However, trying the same using the GoogleProvider, the behavior seems to differ and I hope someone can either explain this behavior to me or look into the issue.

Expected Behavior

It is expected that the GoogleProvider works exactly the same as the AppleProvider in case of Sign-up, linking and especially signing in again on a different device using the same credentials.

Actual Behavior

Signing in with the GoogleProvider on another device leads to an "credentials-already-in-use" error of the framework, indicating that the credentials belong to another account and therefore cannot be linked to another (anonymous) user.

In contrast to AppleProvider, the GoogleProvider doesn't seem to recognize that the user tries to sign-in with existing credentials, so it seems impossible to sign-in with GoogleProvider like it is with AppleProvider.

Same holds true if you log out of your existing account, being automatically logged in anonymously again immediately by the app, then trying to sign-in using the same credentials used some minutes ago.

Additional Information

No response

@anxiety24 anxiety24 changed the title Inconsistent behavior for of providers using GoogleProvider() and AppleProvider() Inconsistent behavior of providers using GoogleProvider() and AppleProvider() Jan 9, 2024
@danagbemava-nc
Copy link
Contributor

Hi @anxiety24, in your sign in with apple options, do you have hide my email enabled?

@danagbemava-nc danagbemava-nc added the blocked: customer response Waiting for customer response, e.g. more information was requested. label Jan 10, 2024
@anxiety24
Copy link
Author

Hi @danagbemava-nc, thanks for your response. Can you be more specific in what you mean by "...your Sign in with Apple options"? I didn't find this option in the AppleProvider() object, nor in the Apple Developer Portal - identifier configuration for "Sign in with Apple".

Or do you mean what was selected by the user while signing in with Apple on the respective UI? (so using the anonymize feature of Sign in with Apple?) If that's the case I can already confirm that this doesn't affect or alter the experienced behavior as I already tested both in order to resolve the issue.

@danagbemava-nc
Copy link
Contributor

danagbemava-nc commented Jan 17, 2024

Or do you mean what was selected by the user while signing in with Apple on the respective UI? (so using the anonymize feature of Sign in with Apple?) If that's the case I can already confirm that this doesn't affect or alter the experienced behavior as I already tested both in order to resolve the issue.

Yes, I was referring to the anonymizing feature provided by the sign in with apple.

How are you handling the AuthStateChangeAction, can you share the code for that?

Also, do you have email-enumeration-protection enabled? https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection

@anxiety24
Copy link
Author

anxiety24 commented Jan 17, 2024

email-enumeration-protection is disabled as the project was created way before its introduction.

Actually, I'm not handling the AuthStateChangeAction in particular tbh, and leave almost everything to the plugin itself. I'm basically just intersecting specific states in order to navigate the user out of the signup/signin flow. So really nothing special here and it does work perfectly for all signup/signin actions except the case with GoogleProvider as mentioned. Here's the code for my SignInScreen-Widget (auth providers are configured elsewhere fyi):

SignInScreen(
        headerBuilder: (context, constraints, _) {
          return Padding(
            padding: const EdgeInsets.all(20),
            child: AspectRatio(
              aspectRatio: 1,
              child: Image.asset('assets/images/app_logo.png'),
            ),
          );
        },
        footerBuilder: (context, action) {
          return Padding(
            padding: const EdgeInsets.only(top: 16),
            child: Text(S.current.account_signup_toc_agreement, style: const TextStyle(color: Colors.grey), textAlign: TextAlign.center),
          );
        },
        //providers: AppUser.providerConfigs,
        actions: [
          AuthStateChangeAction((context, AuthState state) {
            debugPrint("AuthStateChangeAction => $state");
          }),
          AuthStateChangeAction<SignedIn>((context, state) {
            debugPrint("SignedIn");
            Navigator.pushReplacementNamed(context, '/');
          }),
          AuthStateChangeAction<UserCreated>((context, state) {
            debugPrint('UserCreated');
            Navigator.pushReplacementNamed(context, '/');
          }),
          AuthStateChangeAction<CredentialLinked>((context, state) {
            debugPrint('CredentialLinked');
            Navigator.pushReplacementNamed(context, '/');
          }),
        ],
      )

Additionally, I have a Model listening to changes of the Firebase User object to handle a seamless experience between permanent and anonymous sign in. Don't believe this affects the issue, but maybe of help for completeness:

FirebaseAuth.instance
        .userChanges()
        .listen((User? user) {

          debugPrint("userChanged $user");

          if (user == null) {
            signInAnonymous();
          } else {
            currentUser = user;
            updatePurchases();
            updateUserSettings();
          }

          notifyListeners();
        });

Hope this helps understanding the use case and current implementation better!

@danagbemava-nc
Copy link
Contributor

Hi @anxiety24, do you have One account per email address enabled in your auth settings? If so, I think you might be running into https://groups.google.com/g/firebase-talk/c/ms_NVQem_Cw/m/8g7BFk1IAAAJ

@anxiety24
Copy link
Author

Hey @danagbemava-nc, yep that setting is enabled and I already stumbled upon the scenario you're referring to. (and it makes total sense from a security pov)

However, that doesn't quite explain the different behavior between the two providers mentioned, as it works perfectly fine using the EmailAuth- and AppleProvider, where the latter is definitely also a trusted provider.

To further narrow down the issue, I also tried subclassing the GoogleProvider class and overriding its property shouldUpgradeAnonymous manually to false. Funnily, this resolves the signin issue (signin with Google then replaces the current anonymous user successfully), but of course causes problems while signing up then (anonymous user obviously won't be upgraded as required)

Debugging the AuthStateChangeActions while trying to sign in into an existing account linked to the respective provider, the sequence of events is as follows:

AppleProvider:

<SigningIn>
<CredentialsReceived>
<SignedIn>

GoogleProvider:

<SigningIn>
<CredentialsReceived>
<AuthFailed> => error: credentials-already-in-use

Therefore, there really seem to be differences in the implementation of the providers under the hood when it comes to this scenario. I hope that helps researching this behavior in greater detail

@danagbemava-nc
Copy link
Contributor

Thanks for the details.

Labeling this for further insight from the team.

cc @lesnitsky

@danagbemava-nc danagbemava-nc added bug Something isn't working auth and removed blocked: customer response Waiting for customer response, e.g. more information was requested. in triage labels Jan 19, 2024
@yoman07
Copy link

yoman07 commented Feb 15, 2024

I have the same problem. It looks that merging anonymous account using AppleProvider doesn't work. It works for Google.

@basti12354
Copy link

Same here. What did I try:

  1. Mail: I created a new anonymous account --> later linked it with mail and password --> anonymous account is merged into a mail account (AuthStateChangeAction((context, state) async { is getting called)

  2. Google: I created a new anonymous account --> later linked it with a Google Account --> anonymous account is merged into Google account (AuthStateChangeAction((context, state) async { is getting called)

  3. Apple: I created a new anonymous account --> trying to link it with an Apple Account --> AuthStateChangeAction((context, state) async was NOT called --> Instead there is a new Apple account created and the anonymous account is still there.

@yoman07
Copy link

yoman07 commented Feb 20, 2024

Hi,
I did fixes here #288 it works like expected after that.

@russellwheatley
Copy link
Member

Happy to look into this if someone can provide a full reproduction with steps to take to reproduce. Thanks 🙏

@russellwheatley russellwheatley added the blocked: customer response Waiting for customer response, e.g. more information was requested. label Mar 13, 2024
@yoman07
Copy link

yoman07 commented Mar 21, 2024

@russellwheatley Hi!
Here are the simplest steps:

  1. Create a new anonymous account
  2. Try to link it with an Apple Account
    AR: a new account created
    ER: anonymous account is merged with Apple Account

@georgeselkhoury
Copy link

Same behavior here. Anonymous account gets linked on Google but not on Apple.

@georgeselkhoury
Copy link

@yoman07 did you find a workaround?

@russellwheatley still blocked on customer response? Did you get a repro?

@russellwheatley
Copy link
Member

Hey @georgeselkhoury - still waiting for code reproduction which I can drop into main.dart and reproduce bug 👍

@georgeselkhoury
Copy link

georgeselkhoury commented Sep 4, 2024

@russellwheatley

  1. Sign Anonymously:
    await auth.signInAnonymously();

  2. In Build return either RegisterScreen or SignInScreen with Apple Provider

  3. Sign in with Apple

Expected Result: Anonymous user is linked to Apple (Just like it does on Google or Email Provider)

Actual Result: Anonymous user is not linked and a new user with Apple behavior is created.

Is this okay or do you need only code repro?

@georgeselkhoury
Copy link

georgeselkhoury commented Sep 4, 2024

I have been digging a bit into the code. I think this is not being called for Apple:
https://github.com/firebase/FirebaseUI-Flutter/blob/main/packages/firebase_ui_auth/lib/src/providers/auth_provider.dart#L183

There is also fix #288 that @yoman07 has created. Either this or when whenever we initiate it we setup action as Link if we have a logged in user with anonymous true.

@georgeselkhoury
Copy link

georgeselkhoury commented Sep 4, 2024

Impact of not fixing this is that we have to rebuild the UI just to support the linking flow for Apple.

@georgeselkhoury
Copy link

georgeselkhoury commented Sep 5, 2024

Here is a workaround that worked for me. More inheritance than composition but it works.

class AppleProviderLinkOnly extends AppleProvider {
  AppleProviderLinkOnly({super.scopes});

  @override
  void mobileSignIn(AuthAction action) {
    super.mobileSignIn(AuthAction.link);
  }
}

I still think this should be fixed though. It has different behavior than Google. I think all Oauth should behave the same.

@russellwheatley russellwheatley removed the blocked: customer response Waiting for customer response, e.g. more information was requested. label Sep 17, 2024
@russellwheatley russellwheatley added the Needs Attention OP created or responded to issue and it needs attention. label Sep 17, 2024
@russellwheatley
Copy link
Member

@yoman07 has provided a fix for this bug: #288

Could you let me know if this fixes your issue? @anxiety24 @georgeselkhoury

@russellwheatley russellwheatley added blocked: customer response Waiting for customer response, e.g. more information was requested. and removed Needs Attention OP created or responded to issue and it needs attention. labels Oct 28, 2024
@github-actions github-actions bot added the Needs Attention OP created or responded to issue and it needs attention. label Oct 28, 2024
@russellwheatley russellwheatley removed the Needs Attention OP created or responded to issue and it needs attention. label Oct 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auth blocked: customer response Waiting for customer response, e.g. more information was requested. bug Something isn't working
Projects
None yet
Development

No branches or pull requests

6 participants