-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Account Linking Phase 1 #1185
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
Account Linking Phase 1 #1185
Conversation
Change-Id: I50b05d1e50865c9f2dc5e231569e237c631e0003
01f1ca1
to
cab8213
Compare
Change-Id: If381ab96faa6684a38de7316f6a825abe0f40625
Change-Id: Ib85147724ce25ef9e757e3d7a496cc3dea8f94c8
@SUPERCILEX am I correct interpreting #309 to allow linking between any pre-signed-in account and not just anonymous? |
I am also a little confused how to handle email. Let's say after But, we want to make sure that in the merge conflict handler the Seems like in #309 you'd give the user a chance to read all data from the anon account before attempting the link, but not sure how it would work here. Edit 1: after talking to @bojeil-google we need to create a temporary Edit 2: Implemented working version in 5876159 |
Change-Id: I15adb6fab8306420fa7ca6a9a837e2260691369e
Change-Id: I51da641853cc581ea90aa2d31c2908bed3737e76
Change-Id: I245ca7ee28e7d0b06bb7b691f44a3a51e1bca4dd
Change-Id: Ic253a2f5cac30e51baef2f33aa0cc88b00db95c6
Change-Id: Ibb5c3f19a8f63c59b7685728e4a92e84d6b7690d
@@ -0,0 +1,197 @@ | |||
package com.firebase.uidemo.auth; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Decided to do a new sample here since this is an "advanced" feature and I don't want to clutter the main sample.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooof! You sure about this? In my PR, I just added a checkbox that said something like "Allow account linking" and a button to open the AuthUiActivity
from the signed in one so devs could fully customize the linking experience (I noticed you hardcoded the providers and all that).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we were going your way with this feature (service, data callbacks, etc) then I'd agree with merging the samples. However in this case since you have to write extra code and really understand the merge-failure-upgrade flow I wanted to give it dedicated UI and keep the code separate so as not so confuse users of the 'basic' AuthUI flow with all the extra stuff.
If you try out the sample, you can see how it makes the various states extra clear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotya, that totally makes sense so 👍.
@@ -129,6 +129,7 @@ public void onSaveInstanceState(Bundle outState) { | |||
@Override | |||
public void onSuccess(@NonNull final IdpResponse response) { | |||
AuthCredential credential = ProviderUtils.getAuthCredential(response); | |||
// TODO: Probably need to allow linking here |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self: need to check out this piece.
protected void onUpgradeFailure(@NonNull FirebaseAuthUserCollisionException e) { | ||
// In the case of Phone Auth the exception contains an updated credential we can | ||
// user since the other credential is expired after one use. | ||
AuthCredential credential = e.getUpdatedCredential() != null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this is a cool trick I learned: you get a collision exception from Phone Auth the exception gives you a try-again credential so you don't have to show the UI again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooooooh, fancy!
(In your comment, I think you meant use
, not user
. 😉 Everything starts looking like a user in Firebase Auth! 😆)
Change-Id: I7db94a0b5c5f7fc515278dcb82c2e51a1ac5da48
Removing this from |
Oh noes! 😞 I completely forgot about this PR, sorry man! |
@SUPERCILEX haha no worries, I knew exactly what I was getting into. |
Change-Id: Ibdf3e61f6dc1e1578640cb66562a217bf2fd74c0
Change-Id: Ib7956224986259b388b570d59c2f65b00251f8dc
Change-Id: I7eb7675c29c5d03c292499d9517d064bd3a02315
@SUPERCILEX I've got the merge conflicts squared away. The two places I am not sure how to apply the linking logic are in Mind taking a look? |
Will do! I just realized I forgot some loading stuff in the providers... so PR incoming for that first! 😊 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alrighty, I'm starting off with some general comments.
The gist I'm getting is you're creating a temporary, fake firebase app to test whether or not a credential already exists, thus seeing if there's going to be a collision. While that's crazy cool and I learned something new, I think it's a bit over the top . (Show off! 😆) In my PR, I literally just added an if (e instanceof FirebaseAuthUserCollisionException)
in the welcome back stuff and returned an error if it was the case. I'm probably missing something though...
So for the SocialProviderResponseHandler
, we'll want to add this:
https://github.com/SUPERCILEX/FirebaseUI-Android/blob/460f4307089d3933e2517058758147d725eea567/auth/src/main/java/com/firebase/ui/auth/ui/idp/CredentialSignInHandler.java#L105-L127
Except instead of performing the service merge, you'll return the pending credential.
For the LinkingSocialProviderResponseHandler
, that's where you simply add the if statement and return the pending credential if it fails.
PS: I've forgotten, is the service thing permanently out of the picture? Or why aren't we doing it here? I feel like we've already talked about this somewhere, so sorry for the repetition.
@@ -0,0 +1,197 @@ | |||
package com.firebase.uidemo.auth; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooof! You sure about this? In my PR, I just added a checkbox that said something like "Allow account linking" and a button to open the AuthUiActivity
from the signed in one so devs could fully customize the linking experience (I noticed you hardcoded the providers and all that).
@@ -61,25 +63,36 @@ public IdpResponse createFromParcel(Parcel in) { | |||
|
|||
private final FirebaseUiException mException; | |||
|
|||
private final AuthCredential mPendingCredential; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you move this above the exception?
if (mException != null ? !mException.equals(response.mException) : response.mException != null) { | ||
return false; | ||
} | ||
if (mPendingCredential != null ? !mPendingCredential.equals(response.mPendingCredential) : response.mPendingCredential != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These if
statements are kinda confusing... Also, the AuthCredential
doesn't implement equals
properly so adding that will make the response never have an equal. I think if you just add this line it'll work out:
&& (mPendingCredential == null ? response.mPendingCredential == null : mPendingCredential.getProvider().equals(response.mPendingCredential.getProvider()))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest I just let IntelliJ write this, and since these things are now marked @Nullable
this is what it came up with.
But good call on the AuthCredential
part.
result = 31 * result + (mToken != null ? mToken.hashCode() : 0); | ||
result = 31 * result + (mSecret != null ? mSecret.hashCode() : 0); | ||
result = 31 * result + (mException != null ? mException.hashCode() : 0); | ||
result = 31 * result + (mPendingCredential != null ? mPendingCredential.hashCode() : 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, we should just hash code the provider:
result = 31 * result + (mPendingCredential == null ? 0 : mPendingCredential.getProvider().hashCode());
setResult(Resource.forSuccess(response)); | ||
} else { | ||
// TODO: Will this correctly pass back anonymous errors? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm pretty sure it will.
import com.firebase.ui.auth.util.data.ProviderUtils; | ||
import com.google.android.gms.tasks.OnSuccessListener; | ||
import com.google.firebase.auth.AuthCredential; | ||
import com.google.firebase.auth.AuthResult; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?. Probs refactor mistake
protected void onUpgradeFailure(@NonNull FirebaseAuthUserCollisionException e) { | ||
// In the case of Phone Auth the exception contains an updated credential we can | ||
// user since the other credential is expired after one use. | ||
AuthCredential credential = e.getUpdatedCredential() != null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooooooh, fancy!
(In your comment, I think you meant use
, not user
. 😉 Everything starts looking like a user in Firebase Auth! 😆)
|
||
protected void onUpgradeFailure(@NonNull IdpResponse response) {} | ||
|
||
protected abstract void onNonUpgradeFailure(@NonNull Exception e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about calling it onUnknownFailure
?
} | ||
} | ||
|
||
protected void onUpgradeFailure(@NonNull FirebaseAuthUserCollisionException e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make this final? I got confused about why you weren't calling super until I realized there was an overload. Or make it private and call it handleUpgradleFailure
?
/** | ||
* Uses type system to enforce the proper failure listener. | ||
*/ | ||
public static class UpgradeTaskWrapper<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this idea, but it doesn't really scale. Or is the plan to force us to always add a failure listener first?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that was the plan, to make sure that we always handle that error case. But you're right that it doesn't quite work, because in some places we have continuations.
Huh, I guess embedded code snippets don't work on forks. 😭 |
@SUPERCILEX thanks for the comments. The temporary As for why we're not doing the Service thing: basically because in the API discussion for doing this feature cross-platform the only thing we could agree on in the short term was something minimal and correct like this, while leaving room to improve the developer experience in the future. |
Ohhhhhhhhh, I see. Thanks for clarifying! Re-reading your comments though, it does look like you're checking for a conflict. Maybe change them to say we're expecting a conflict, but want to ensure the credential is usable? As for the service, thanks, I remember now. |
Closing this PR, it's very very conflicty and @lsirac is going to take over this task. |
See #1179 and #309
TODO: