Skip to content

Conversation

@amandle
Copy link
Contributor

@amandle amandle commented Feb 9, 2017

Adds reauthentication as discussed in #563

  • There is now AuthUI.createReauthIntentBuilder() which returns an intent builder very similar to AuthUI.createSignInIntent() but adds the methods setReauthReason and setReauthEmail and removes setAllowNewEmailAccounts. Both builders extend a common builder which contains the common methods.
  • Account creation is skipped during the reauth flow
  • Account picker screen only lists the methods the user has linked.
  • The reauth reason gets displayed directly above the auth method buttons. This is totally open for discussion but it's where made the most sense to me.

private boolean mAllowNewEmailAccounts = true;

private SignInIntentBuilder() {
@SuppressWarnings(value="unchecked")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amandle the build should pass if you put space around the =.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Collaborator

@SUPERCILEX SUPERCILEX left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amandle You're back! 😄 I left a few comments about style nits and some improvements I think could be made.

getSelectedTheme(),
getSelectedProviders(),
getSelectedTosUrl(),
mEnableSmartLock.isEnabled())));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amandle Maybe make the new SignedInActivity.SignedInConfig(...) part a method since it's a copy paste of the same thing above? (Actually, I think you could do a Ctrl + Alt + M on the whole startActivity thing and Intellij should only generate a response parameter which would be nicer than having a method for just the SignedInConfig part.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, done

* Set an explanation for why reauth was requested e.g. "To delete your account you must
* reauthenticate."
*
* @param reauthReason A string explaining why reauthentication was requested.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're referring to the empty return? I have now removed it.


public final boolean allowNewEmailAccounts;

public final boolean isReauth;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amandle Couldn't this be a method instead? It seems weird to have a parameter just for something that can be inferred programmatically.

Something like:

public boolean isReauth() {
    return !TextUtils.isEmpty(reauthEmail);
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, I thought the reauthenticate method required an email but it doesn't so this won't work.

if (visibleProviders.size() == 0) {
Log.e(TAG, "There are no configured providers associated with the user's account");
}
populateIdpList(visibleProviders);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before this happens I think we should check to see if visibleProviders.size() == 1 and just launch the sign in right away if that is the case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed and I think we already have this logic somewhere to steal.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (mActivityHelper.getFlowParams().isReauth) {
String reauthEmail = mActivityHelper.getFlowParams().reauthEmail;
if (reauthEmail == null) {
Log.e(TAG, "Attempting to reauth with email but no reauthEmail provided");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the build() method should prevent this and throw an IllegalArgumentException if the dev tries to do this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per Sam's comment reauthEmail is now removed, and the user is required to be logged in to start, so this whole block can go away

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome!

android:layout_width="wrap_content"
android:orientation="vertical">

<TextView
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is just copy past from the landscape layout so we could use a <merge /> tag for this.

true /* allowNewEmailAccounts */,
false /* isReauth */,
null /* reauthEmail */,
null /* reauthReason */);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

RC_EMAIL_DIRECT_SIGN_IN);
} else {
startActivityForResult(
RegisterEmailActivity.createIntent(this, mActivityHelper.getFlowParams()),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amandle I'm just being nit picky here (Sorry! 😄), but I feel like mActivityHelper.getFlowParams() could be Ctrl + Alt + Ved into something like params.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

"theme identifier is unknown or not a style definition");
mTheme = theme;
return this;
return (T) this;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sigh. I looked at this and thought "there's no way that Java really requires this generic + cast situation" but it looks like we do!

*
* @param reauthEmail The email address of the account for which reauthentication is being
* requested
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to do re-auth and not use the currently signed in user? In which case this email would be FirebaseAuth.getInstance().getCurrentUser().getEmail() I feel like we could remove this setter and therefore reduce the chance of error/misunderstanding.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, that simplifies things considerably.

}
List<IdpConfig> visibleProviders = new ArrayList<>();
if (mActivityHelper.getFlowParams().isReauth) {
// display the reauthReason if available
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should ask UX about this, but instinctively a dialog feels like the "normal" reauth flow.

  1. User clicks a button requiring reauth ("Delete Account" for example)
  2. A dialog shows up that says "This action requires you to sign in again, would you like to continue"
  3. Yes launches the flow, No returns RESULT_CANCELED

// their account.
List<String> providerIds = mActivityHelper.getCurrentUser().getProviders();
if (providerIds.size() == 0) {
// zero providers indicates that it is an email account
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't the providers array contain just "password" in this case or is that only when you call fetchProvidersForEmail()?

new ArrayList<>(mProviders),
mTheme,
mLogo,
mTosUrl,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amandle Have you tried reauthenticating with smart lock enabled? Given @samtstern's comment about showing a dialog before we show auth stuff, I feel like things will get complicated with smart lock enabled. @samtstern @amandle Am I missing something or could this be a problem?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's a little complicated/confusing, but I think we have to have it as an option for the case where the person doesn't know their password but has it stored in smartlock

@samtstern
Copy link
Contributor

samtstern commented Feb 17, 2017

@amandle FYI there are merge conflicts between your branch and the base.

I left one more comment, but other than that (and the conflicts) this LGTM

// their account.
List<String> providerIds = mHelper.getCurrentUser().getProviders();
if (providerIds.size() == 0) {
// zero providers indicates that it is an email account
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For an email account when you do fetchProvidersForEmail you get a list that's just ["password"]. Is this different when you access the providers from the user object directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does seem to be different, asking some of the Firebase folks for more information

@samtstern
Copy link
Contributor

I checked out the branch and tried a few things. I am noticing some strange behavior when using Google Sign In:

  • If I have SmartLock enabled I get the "choose an account saved with Google Smart Lock" dialog, but if I click "None of the above" then the whole reauth flow ends. This does not happen with password login.
  • If I don't have SmartLock enabled I still get the dialog above when I try to reauth. It does not seem that I can actually disable SmartLock (maybe this is just a sample issue?)

Also the re authentication dialog does not seem to respect my theme colors, the buttons are always straight black. Ideally this dialog would inherit the theme passed to the sign in flow.


boolean smartLockEnabled = smartLockEnabledInt != 0;
boolean allowNewEmailAccounts = allowNewEmailAccountsInt != 0;
boolean isReauth = isReauthInt != 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amandle The merge conflicts are my fault, sorry about that! I removed this unnecessary abstraction so this should work once you merge:

boolean smartLockEnabled = in.readInt() != 0;
boolean allowNewEmailAccounts = in.readInt() != 0;
boolean isReauth = in.readInt() != 0;
String reauthReason = in.readString();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@amandle
Copy link
Contributor Author

amandle commented Feb 17, 2017

If I have SmartLock enabled I get the "choose an account saved with Google Smart Lock" dialog, but if I click "None of the above" then the whole reauth flow ends. This does not happen with password login.

The reauth does happen though. Since the user is already signed in to Google and we know the email address it happens automatically. You can verify this by putting a breakpoint at IdpSignInContainer.java:134

If I don't have SmartLock enabled I still get the dialog above when I try to reauth. It does not seem that I can actually disable SmartLock (maybe this is just a sample issue?)

Ah, yep this is a bug in the sample app. Should be fixed now.

Also the re authentication dialog does not seem to respect my theme colors, the buttons are always straight black. Ideally this dialog would inherit the theme passed to the sign in flow.

Hmm, I'm not sure how to do this without going back to using standard activities instead of AlertDialogs. The RecoveryEmailSentDialog uses the same theme:

return new AlertDialog.Builder(getContext(), R.style.FirebaseUI_Dialog)

@samtstern samtstern modified the milestones: 1.2.0, 2.0.0 Feb 21, 2017
@samtstern
Copy link
Contributor

@amandle I moved this to target the 2.0.0 release since this is a notable enough feature that we should pursue iOS parity. Hopefully @yramanchuk can implement a mirror of this.

The only remaining concern I still have is the bad UX with SmartLock and "None of the Above" where GSI succeeds even though the user never really did anything. Can we clarify this somehow? Options include:

  • For reauth change SmartLock settings to only include password accounts
  • Display a Toast after reauth success
  • Other (?)

@amandle
Copy link
Contributor Author

amandle commented Mar 10, 2017

@samtstern I think this is ready to review now. I added a toast when the automatic Google Sign In happens and skip Smart Lock if it's a reauth with a non-password account.

@samtstern
Copy link
Contributor

@amandle approved! Going to squash and merge.

@yramanchuk @morganchen12 @bojeil-google FYI we need to coordinate the same abilities across platforms. This is merging into our next release branch.

@samtstern samtstern merged commit 9ae131a into firebase:version-2.0.0-dev Mar 13, 2017
@develuxe
Copy link

develuxe commented Jun 1, 2018

Would love to have this feature back but can't find it in the repo. Was it removed?

@samtstern
Copy link
Contributor

samtstern commented Jun 1, 2018 via email

@develuxe
Copy link

develuxe commented Jun 1, 2018

Thanks for your quick response. Is there any best practice on how to reauthenticate the user?

@ViniciusR
Copy link

So sad they abandoned this feature. I'm actually facing this situation, it's so much complex to retrieve credentials from Google and other providers, Firebase's reauthenticate function is pretty easy, but it requires that credentials.

I think the easiest way is forcing sign out and ask user to sign in again - it would needed to login back to reauthenticate anyway.

@bojeil-google
Copy link

Hey folks, we have not abandoned our plans to support re-authentication in FirebaseUI (it is indeed a missing key functionality that we should support). The priority has been lowered in favor of some other popular/critical feature requests such as support for email link sign-in and account management UI.

@ViniciusR
Copy link

That's good!

@jackz314
Copy link

Any updates on this feature? And would there be an ETA for this?

@Heldenkrieger01
Copy link

Will this be released any time soon?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants