-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Support account linking #309
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
Conversation
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.
Stuff that needs to be addressed before the PR is merged.
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); | ||
if (user != null) { | ||
if (!user.isAnonymous()) { | ||
startActivity(SignedInActivity.createIntent(this)); |
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.
Should I make SignedInActivity
and AuthUiActivity
fragments and put them in a FragmentStatePagerAdapter
so that people can link multiple accounts?
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.
Let's discuss changes to the sample last, I want to focus on the changes to the library at first since that will have effects on how the sample is structured.
@@ -309,6 +311,10 @@ public SignInIntentBuilder createSignInIntentBuilder() { | |||
return new SignInIntentBuilder(); | |||
} | |||
|
|||
public void notifyOnMergeFailedListeners(String prevUid) { |
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'm pretty sure this is not okay, but I don't know how to get rid of it. How would I serialize the listener into FlowParameters
?
@@ -417,6 +425,15 @@ public SignInIntentBuilder setIsSmartLockEnabled(boolean enabled) { | |||
return this; | |||
} | |||
|
|||
/** | |||
* Links the current user to a new one. |
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.
TODO: bad wording
FirebaseAuth auth = FirebaseAuth.getInstance(); | ||
FirebaseUser user = auth.getCurrentUser(); | ||
if (!task.isSuccessful() && user != null && mPrevCredential != null) { | ||
user.delete(); |
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.
This is terrible idea, but user
will be the new user if I call delete()
in the OnCompleteListener
because the variable is a reference.
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 you explain exactly what you're doing in this bit? I'm not clear on what you are trying to achieve.
public void onComplete(@NonNull Task<AuthResult> task) { | ||
if (task.isSuccessful()) { | ||
AuthUI.getInstance() | ||
.notifyOnMergeFailedListeners(prevUid); |
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.
This is where I want to call user.delete()
.
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.
In this block you are notifying the merge failed listeners on successful sign in, why is that the case?
FirebaseUser user = auth.getCurrentUser(); | ||
if (!task.isSuccessful() && user != null && mPrevCredential != null) { | ||
user.delete(); | ||
final String prevUid = user.getUid(); |
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.
Need this because of java references.
@@ -57,7 +57,8 @@ public static FlowParameters getFlowParameters(Context context, List<String> pro | |||
AuthUI.getDefaultTheme(), | |||
AuthUI.NO_LOGO, | |||
null, | |||
true); | |||
true, | |||
false); |
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.
We should probably make a test for this.
I have one bug that I know of that isn't fixed yet: when going from an anonymous account that doesn't have a name or profile picture to a Google account, the profile picture and name don't get added to the account. Only the email gets added. Does anyone know why this is happening? I could just add a |
@SUPERCILEX thank you (again!) for the big contribution, we really appreciate how involved you are with this repo lately. I will take a look and leave comments, but as you know, we try to keep FirebaseUI-Android in sync with FirebaseUI-iOS, so before this feature could be released we'd have to get some commitment there. That said if things look good we can always put this on a feature branch and merge it back into |
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); | ||
if (user != null) { | ||
if (!user.isAnonymous()) { | ||
startActivity(SignedInActivity.createIntent(this)); |
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.
Let's discuss changes to the sample last, I want to focus on the changes to the library at first since that will have effects on how the sample is structured.
/** | ||
* Links the current user to a new one. | ||
*/ | ||
public SignInIntentBuilder linkWithCurrentUser(OnMergeFailedListener onMergeFailedListener) { |
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 find the pattern of adding a listener to an Intent
builder strange. The result of the action in the Intent
should be returned via onActivityResult
if the Activity
that fired it did so for result.
FirebaseAuth auth = FirebaseAuth.getInstance(); | ||
FirebaseUser user = auth.getCurrentUser(); | ||
if (!task.isSuccessful() && user != null && mPrevCredential != null) { | ||
user.delete(); |
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 you explain exactly what you're doing in this bit? I'm not clear on what you are trying to achieve.
public void onComplete(@NonNull Task<AuthResult> task) { | ||
if (task.isSuccessful()) { | ||
AuthUI.getInstance() | ||
.notifyOnMergeFailedListeners(prevUid); |
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.
In this block you are notifying the merge failed listeners on successful sign in, why is that the case?
} | ||
} | ||
|
||
private void addListeners(Task<AuthResult> task, final String name, final String password) { |
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.
IMO this is a confusing abstraction, why not just add the listeners directly as before?
|
||
if (user != null && mActivityHelper.getFlowParams().shouldLinkUser) { | ||
user | ||
.linkWithCredential(credential) |
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.
style nit: prefer not to have a continuation for the first method call in a chain if it would fit on the previous line. user
is just kinda floating out there.
.addOnCompleteListener(handler); | ||
} else { | ||
firebaseAuth | ||
.signInWithCredential(credential) |
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.
Seems like in this if/else
block you are attaching the same complete/failure listener but switching on the Task type. Would be clearer to do:
// Either sign the user in or link the credential
Task<AuthResult> authTask;
if (user != null && mActivityHelper.getFlowParams().shouldLinkUser) {
authTask = user.linkWithCredential(credential);
} else {
authTask = firebaseAuth.signInWithCredential(credential);
}
task.addOnFailureListener(logger);
task.addOnCompleteListener(handler);
@@ -0,0 +1,11 @@ | |||
package com.firebase.ui.auth.util; | |||
|
|||
public interface OnMergeFailedListener { |
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.
Building on my previous comment, I don't think we need to use the listener pattern for merge failures. The only information returned on a merge failure is the previous user ID, so why not just do it through Activity results? The developer could handle it like this?
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN) {
if (resultCode == RESULT_MERGE_FAILED) {
String prevUid = data.getStringExtra(EXTRA_PREV_UID);
// Handle merge failure ....
}
}
}
This also solves the issue you raised of needing to somehow store the listener reference in the FlowParams, which won't work (as you mentioned).
assertEquals(
TestConstants.EMAIL,
nextIntent.intent.getExtras().getString(ExtraConstants.EXTRA_EMAIL) Not sure why... |
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.
Addressed all your comments. Thanks!
public Intent getMergeFailedIntent() { | ||
FirebaseUser user = getCurrentUser(); | ||
if (mFlowParams.shouldLinkUser && user != null) { | ||
if (user.isAnonymous()) user.delete(); |
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.
Is this the expected behavior or should I be deleting any user anonymous or not?
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 don't think there's a need to delete anonymous users 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.
I don't think we want to delete the anonymous user.
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'm deleting the anonymous user here because the expected usage of getMergeFailedIntent()
is that a non anonymous user will be signed into immediately after this method is called. If I don't delete the anonymous users, there will be a bunch of them hanging around that will never be user again. AFAIK, you can't log back into an existing anonymous user so not deleting them would make a messy user base in the Firebase console.
What I would really like to do is to delete the anonymous user once I'm signed in, but I can't figure out how to do that because I keep getting references to the newly signed in user.
If you believe we should play it safe and not delete the anonymous user, that's fine with me but I had one last idea: maybe I could delete the anonymous user and if signing into the new user fails, I just create a new anonymous user and pass down the old anonymous user's id through the Intent. What do you think? It would probably just be easier to not delete the anonymous user, but again, I take issue with the messy Firebase console. Do you know if Firebase automatically delete anonymous users if they haven't logged in for a while? That would solve the issue.
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.
We are looking for ways to clean up "zombie" anonymous users in the Firebase console after a while, so let's not delete the users and count on that happening eventually.
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.
Done!
break; | ||
case RC_PLAY_SERVICES: | ||
if (resultCode != RESULT_OK) { | ||
finish(resultCode, new Intent()); |
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.
Was there a reason for these new intents? I need them to pass the uid down the chain of activities. This is where #320 would come in handy
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.
You can use these intents to plumb data back through to the calling activities, I think you're doing the right thign.
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.
Your change is correct, the new Intent()
was in the absence of anything important to plumb down.
public void onComplete(@NonNull Task<AuthResult> task) { | ||
if (task.isSuccessful()) { | ||
mActivityHelper.dismissDialog(); | ||
finish(Activity.RESULT_OK, getIntent()); |
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.
This stuff is much clearer now.
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'm actually not sure what's going on here. It looks like if we fail to link with a credential we try to sign in anyway. If the sign in succeeds (but the linking did not) we say it was a success?
public void onComplete(@NonNull Task<AuthResult> task) { | ||
FirebaseAuth auth = FirebaseAuth.getInstance(); | ||
if (!task.isSuccessful() && task.getException() instanceof FirebaseAuthUserCollisionException) { | ||
setIntent(new Intent().putExtras(mActivityHelper.getMergeFailedIntent())); |
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.
The is the new way I'm managing sending the old uid.
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.
nit: any reason not to just drop the new Intent()
piece?
public void onComplete(@NonNull Task<AuthResult> task) { | ||
FirebaseAuth auth = FirebaseAuth.getInstance(); | ||
if (!task.isSuccessful() && task.getException() instanceof FirebaseAuthUserCollisionException) { | ||
setIntent(new Intent().putExtras(mActivityHelper.getMergeFailedIntent())); |
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.
The new version is a little bit different, but I'm basically doing the same thing as before.
If the task isn't successful, it means the link failed so I need to delete the anonymous user and get the old uid. delete()
also signs out the user so I then need to reauthenticate the user and pass on the intent containing the old uid to be manually merged. This scenario only happens when the Google/Facebook/Twitter etc. account already exists.
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.
This looks better now. Left a few more comments.
@@ -150,6 +161,42 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { | |||
@MainThread | |||
private void handleSignInResponse(int resultCode, Intent data) { | |||
if (resultCode == RESULT_OK) { | |||
final String prevUid = data.getStringExtra(ExtraConstants.EXTRA_MERGE_FAILED); | |||
if (prevUid != null) { | |||
FirebaseDatabase.getInstance() |
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 remove all this? There shouldn't be anything referencing the DB or the chat sample in the AuthUI 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.
We can remove it, but I think it should be moved to the documentation so that beginners know what to do with the previous user id.
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.
Ok that's fine, documentation comes last anyway. Let's save that for another time.
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.
K
@@ -0,0 +1,28 @@ | |||
package com.firebase.uidemo.database; |
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.
nit: let's just leave chat where it was. We don't need to access it outside of that context anyway.
* | ||
* <p>Linking is disabled by default. | ||
*/ | ||
public SignInIntentBuilder linkWithCurrentUser(boolean shouldLinkUser) { |
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.
nit: name this setShouldLinkUser
or something more consistent with setIsSmartLockEnabled
and the others.
public Intent getMergeFailedIntent() { | ||
FirebaseUser user = getCurrentUser(); | ||
if (mFlowParams.shouldLinkUser && user != null) { | ||
if (user.isAnonymous()) user.delete(); |
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 don't think we want to delete the anonymous user.
break; | ||
case RC_PLAY_SERVICES: | ||
if (resultCode != RESULT_OK) { | ||
finish(resultCode, new Intent()); |
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.
Your change is correct, the new Intent()
was in the absence of anything important to plumb down.
public void onComplete(@NonNull Task<AuthResult> task) { | ||
FirebaseAuth auth = FirebaseAuth.getInstance(); | ||
if (!task.isSuccessful() && task.getException() instanceof FirebaseAuthUserCollisionException) { | ||
setIntent(new Intent().putExtras(mActivityHelper.getMergeFailedIntent())); |
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.
nit: any reason not to just drop the new Intent()
piece?
@Override | ||
public void onComplete(@NonNull Task<AuthResult> task) { | ||
FirebaseAuth auth = FirebaseAuth.getInstance(); | ||
if (!task.isSuccessful() && task.getException() instanceof FirebaseAuthUserCollisionException) { |
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.
nit: prefer separate onSuccess
and onFailure
listeners here, rather than checking isSuccessful()
in the onCompleteListener
null /* password */, mResponse.getProviderType()); | ||
SmartlockUtil.saveCredentialOrFinish(mActivity, | ||
mSaveCredentialsResultCode, | ||
new Intent(), |
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.
nit: I'd prefer to pass null /* intent */
here and have saveCredentialOrFinish
handle the null
case.
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.
Addressed your comments.
@@ -150,6 +161,42 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { | |||
@MainThread | |||
private void handleSignInResponse(int resultCode, Intent data) { | |||
if (resultCode == RESULT_OK) { | |||
final String prevUid = data.getStringExtra(ExtraConstants.EXTRA_MERGE_FAILED); | |||
if (prevUid != null) { | |||
FirebaseDatabase.getInstance() |
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.
We can remove it, but I think it should be moved to the documentation so that beginners know what to do with the previous user id.
public Intent getMergeFailedIntent() { | ||
FirebaseUser user = getCurrentUser(); | ||
if (mFlowParams.shouldLinkUser && user != null) { | ||
if (user.isAnonymous()) user.delete(); |
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'm deleting the anonymous user here because the expected usage of getMergeFailedIntent()
is that a non anonymous user will be signed into immediately after this method is called. If I don't delete the anonymous users, there will be a bunch of them hanging around that will never be user again. AFAIK, you can't log back into an existing anonymous user so not deleting them would make a messy user base in the Firebase console.
What I would really like to do is to delete the anonymous user once I'm signed in, but I can't figure out how to do that because I keep getting references to the newly signed in user.
If you believe we should play it safe and not delete the anonymous user, that's fine with me but I had one last idea: maybe I could delete the anonymous user and if signing into the new user fails, I just create a new anonymous user and pass down the old anonymous user's id through the Intent. What do you think? It would probably just be easier to not delete the anonymous user, but again, I take issue with the messy Firebase console. Do you know if Firebase automatically delete anonymous users if they haven't logged in for a while? That would solve the issue.
FlowParameters parameters, | ||
FirebaseUser firebaseUser, | ||
@Nullable String password, | ||
@Nullable String provider) { | ||
if (data != null) { | ||
data = new Intent(); | ||
} |
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.
Is this what you were looking for?
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 think you mean if (data == null)
not !=
.
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 fixed it in a newer diff.
Any idea why the test is failing? |
Figured out why the test was failing: it might be a bug in the Android framework. This works: data.putExtras(SaveCredentialsActivity.createIntent(
activity,
parameters,
firebaseUser,
password,
provider)); This doesn't: SaveCredentialsActivity.createIntent(
activity,
parameters,
firebaseUser,
password,
provider).putExtras(data); I have no idea why one works, but not the other. Any thoughts? |
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.
The above didn't work either, so as a workaround, I reset the Intent in each Activity calling saveCredentialOrFinish. I don't like this, see my comment below.
* | ||
* @param activity the calling Activity. | ||
* @param requestCode request code to use when starting the save operation. | ||
* @param data An Intent to be merged. Must be a {@code new Intent().putExtras(...)}. |
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'm really not a fan of this: I had to switch everything to be a new Intent()
to workaround the merge bug. Do you think we should just make a parameter prevUid
?
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 you explain a little what happened when you did the merge the other way? What's the buggy behavior you're seeing?
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.
See pics:
The two differeent ways of merging intents:
The left intent merge up top produces the left version below and vice versa:
So basically, the left version works in terms of keeping the email alive, but the it doesn't in the sense that it says it comes from the class that it originated from eg. SignInActivity
instead of SaveCredentialsActivity
(the test expects SaveCredentialsActivity
).
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.
Ok I just looked through some Android source and here's whats happening. When you merge the extras of two bundles (say dst.Extras(src)
) it basically does this (pseudocode):
for (String key : src.getExtras().getKeys()) {
dst.putExtra(key, src.getExtra(key));
}
This means for a given key
if src.getExtra(key)
is null
then it will erase the extra with the same key in dst
. I think that's why your email value is disappearing.
Maybe you want to create a utility method to merge extras only if they're non-null? I don't want to reverse the order of operations here just to get around the bug, as you said it makes the result of the intent counter-intuitive.
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.
Thanks for looking into that! Do you have any ideas on how to not use instanceof
?
I'm stuck with something like this:
for (String key : intent.getExtras().keySet()) {
Object o = intent.getExtras().get(key);
if (o != null) {
if (o instanceof String) {
data.putExtra(key, (String) o);
} else if...
// More sadness
}
}
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.
Oooh yeah that's not gonna be pretty, you're right. Not sure there's any way around that.
Maybe let's find out why there's a null extra in this situation and just prevent it from happening in the first place?
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.
YESSSSS!!!! It took me like a half hour of debugging, but it finally dawned on me to actually look at what the test was doing:
Real life in AcquireEmailHelper
:
if (email != null && !email.isEmpty()) {
// Only proceeds if email is not null
}
Test:
Intent startIntent = SignInActivity.createIntent(
RuntimeEnvironment.application,
TestHelper.getFlowParameters(
RuntimeEnvironment.application,
Collections.<String>emptyList()),
***null***);
So anyway, I fixed the test and life is good again. The end.
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.
@SUPERCILEX 👍 💥
@amandle can you take a look at this CL as well? It's very complex and I would like a second set of eyes. |
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.
Thanks for the pull request! I have just a few questions/suggestions
public void onComplete(@NonNull Task<AuthResult> task) { | ||
if (task.isSuccessful()) { | ||
mActivityHelper.dismissDialog(); | ||
finish(Activity.RESULT_OK, getIntent()); |
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'm actually not sure what's going on here. It looks like if we fail to link with a credential we try to sign in anyway. If the sign in succeeds (but the linking did not) we say it was a success?
@@ -114,6 +113,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { | |||
|
|||
private void next(String email, final String password) { | |||
final FirebaseAuth firebaseAuth = mActivityHelper.getFirebaseAuth(); | |||
setIntent(getIntent().putExtras(mActivityHelper.getMergeFailedIntent())); |
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 think this might be clearer if this were closer to where it is used on line 145
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.
It can't be used anywhere after signInWithEmailAndPassword
because then I wouldn't be saving the old uid.
.addOnFailureListener(new TaskFailureLogger(TAG, "Error creating user")) | ||
FirebaseUser user = firebaseAuth.getCurrentUser(); | ||
Task<AuthResult> task; | ||
setIntent(getIntent().putExtras(mActivityHelper.getMergeFailedIntent())); |
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.
this only needs to happen in the true case of the if statement on line 145
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.
Woops! That's actually not supposed to happen at all since we're linking with a new credential (eg the account doesn't exist yet).
My "approval" is a hand off to @amandle who understands the logic best. I just have the following final requests:
|
@amandle Because Sam was confused too, I added an explanation to what I'm doing in |
@samtstern Thanks for all your help so far!!!
|
auth/README.md
Outdated
@@ -197,14 +197,27 @@ startActivityForResult( | |||
RC_SIGN_IN); | |||
``` | |||
|
|||
If anonymous user account conversion to permanent is required: |
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.
Thoughts on documentation?
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 would expand this sentence as there are a lot of advanced concepts at play here. Something like this:
"The default FirebaseUI sign-in flow shows UI to either create a new account or sign into an existing account. If you are using anonymous authentication (link) in your application before calling FirebaseUI, you may want to link the anonymous account to the permanent account the user selects in the UI flow."
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.
Done
* | ||
* <p>Linking is disabled by default. | ||
*/ | ||
public SignInIntentBuilder setShouldLinkAccounts(boolean shouldLinkAccounts) { |
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.
Thoughts on the new name? setShouldLinkAccounts
vs setShouldLinkUser
. I prefer the first but it's up to debate.
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 the new name, since we call the feature "Account Linking"
any update of this feature? we need this function. Thanks!! |
This waiting on #357 |
} else { | ||
((TextView) findViewById(R.id.idp_secret)).setText(secret); | ||
} | ||
} |
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 did some cleanup here so that if IdpResponse
is null
, we don't show the layouts.
if (prevUid == null) { | ||
findViewById(R.id.prev_uid_layout).setVisibility(View.GONE); | ||
} else { | ||
((TextView) findViewById(R.id.prev_uid)).setText(prevUid); |
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.
Is this an appropriate update to the sample?
response.getEmail(), | ||
response.getIdpToken(), | ||
response.getIdpSecret(), | ||
prevUid); |
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.
This is a mess, but I don't know how to get around it. I need to add the previous user id to an existing IdpResponse
and I chose not to use a setter since this is public api. Any thoughts on a better way to do this? Should I be using a setter? Also, if we stick with this, what is the convention for this kind of constructor? (response.getProviderType()
vs response.mProviderId
)
* See the <a href="https://github.com/SUPERCILEX/FirebaseUI-Android/blob/master/auth/README.md#handling-account-link-failures">README</a> | ||
* for a much more detailed explanation. | ||
* | ||
* @see AuthUI.SignInIntentBuilder#setShouldLinkAccounts(boolean) |
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.
Is this clear enough?
@@ -83,8 +83,6 @@ protected void onCreate(Bundle savedInstanceState) { | |||
TextView bodyTextView = ((TextView) findViewById(R.id.welcome_back_password_body)); | |||
bodyTextView.setText(spannableStringBuilder); | |||
|
|||
((TextInputLayout) findViewById(R.id.password_layout)).setPasswordVisibilityToggleEnabled(false); |
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.
See the support lib release notes. In 25.0.1, they set the default to false.
# Conflicts: # auth/README.md # auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneActivity.java # auth/src/main/java/com/firebase/ui/auth/ui/phone/SubmitConfirmationCodeFragment.java # auth/src/main/java/com/firebase/ui/auth/util/AuthHelper.java # auth/src/main/java/com/firebase/ui/auth/viewmodel/idp/LinkingSocialProviderResponseHandler.java # auth/src/test/java/com/firebase/ui/auth/testhelpers/AuthHelperShadow.java # auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneActivityTest.java
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
This reverts commit a97133b Signed-off-by: Alex Saveau <[email protected]>
CLAs look good, thanks! |
Signed-off-by: Alex Saveau <[email protected]>
As I am not fully of the full release process here, when could this be expected time wise: Are we talking about days, weeks, month or years here? Don't get me wrong, I am not blaming - I am actually very thankful that such projects exist and that people contribute so much. I just need to know if if it makes sense to wait until this is released for my project or I have to move on to an alternative. |
Please ignore my previous post (now deleted), was the wrong tab. 😳😊 @Borbarad13 because this depends on the other platforms implementing this feature too (which I'm not familiar with), waiting on it is not a viable solution. However, lots of people have been using my fork in #123 (comment) to handle every possible merge case. I'll post some new commit hashes tomorrow. |
@Borbarad13 Done: #123 (comment) |
Is someone authorized to proceed, can resolve the conflicts and merge the pull request ? This feature is what I am looking for ! |
Hi, any update here? Is this feature avialble already? Doesn't seem to be the case. Thanks |
@samtstern can you proceed, and resolve the conflicts and merge the pull request? |
@danijorda1 this code is from 2016. Even the legendary @SUPERCILEX could not possibly resolve the merge conflicts and make this PR functional again, the library has been significantly refactored over the years. We already have some account linking functionality in FirebaseUI. If you need specific improvements to that functionality, please file a new feature request or bug report. |
Hi @samtstern which account linking functionality are you referring to? Couldn't find anything in the Documentation. Thanks |
As far as I can see only Upgrading anonymous users is supported currently while 309, 1823, 894, 1235 and 123 are all related to being able to use Firebase Auth UI to link accounts. That is, when a user is signed in, the user has the possibility to press a Link Account button where a new Sign in activity will be launched allowing the user to link accounts with the selected Provider. As far as I can see all these feature requests/bug reports have been closed and no solution has been provided except for @SUPERCILEX solution that was never merged.... Will Account linking (other than Anonymous upgrade) be supported at some point? Thanks in advance. |
You're right. The other features are not yet supported. I am no longer working on this project so I can't say if they ever will be. |
Do you know who is working on this project so I can directly ask him/her? |
If this has been discussed in an issue, make sure to mention the issue number here. If not, go file an issue about this to make sure this is a desirable change.
If this is a new feature please co-ordinate with someone on FirebaseUI-iOS to make sure that we can implement this on both platforms and maintain feature parity.
TODO:
IdpResponse