-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Differentiate between user cancellation and actual errors #426
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
@@ -34,7 +34,7 @@ | |||
import com.firebase.ui.auth.AuthUI; | |||
import com.firebase.ui.auth.AuthUI.IdpConfig; | |||
import com.firebase.ui.auth.IdpResponse; | |||
import com.firebase.ui.auth.ui.ResultCodes; | |||
import com.firebase.ui.auth.ResultCodes; |
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 library version should probably be bumped to 1.1.0
since this is slight breakage.
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 would technically require a major version (if you're going by semver) since old code would break when updated.
Could we keep the .ui.ResultCodes
class but have it be all @Deprecated
constants that are just symlinks to the new class? Then it's fully backwards compatible.
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.
Awesome idea!
PS: how does deprecation work? When the library hits 2.0, will we remove the deprecated stuff or are we stuck with it for life?
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 deprecated things after a major version change (if we want to). Or we can leave it for life. This will be the first example of deprecation in this library so there's no precedent for us, and every library does it a bit differently.
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, thanks for the info! (I'm pretty sure we also deprecated setProviders(String... providers)
)
/** | ||
* Sign in succeeded | ||
**/ | ||
public static final int OK = Activity.RESULT_OK; |
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 decided to unify result codes and make ResultCodes
a central place for those.
I decided to use the existing activity result codes to not break anything.
/** | ||
* Sign in failed due to lack of network connection | ||
**/ | ||
public static final int NO_NETWORK = 10; |
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 also removed the RESULT_
bit since the class is named ResultCodes
and it looks nicer without the extra RESULT_
.
/** | ||
* An unknown error has occurred | ||
**/ | ||
public static final int UNKNOWN_ERROR = 20; |
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 we be more specific here? The errors finished with this code vary widely and we log each of them so I didn't think it was necessary. The only thing people want to know is if the user cancelled the sign in or an error occurred.
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 fine for now, it would be nice to be more expressive in the long term but I don't want to overwhelm people with 50 error codes.
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.
That's what I was thinking: we can expand later, but for now at least we have something to work with.
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 should now be moved to another file like ErrorCodes.java
or something since it's no longer a "response code" which should only be codes returned to onActivityResult
directly and not the codes contained by IdpResponse.
I also added an |
Turns out there's no way to know if an IDP is a new user so I changed the method name to |
/** | ||
* @return true if the user was created in the sign in flow, false if it already existed. | ||
*/ | ||
public boolean isNewEmailUser() { |
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 we should delay this feature because with a little more effort (which would be out of scope here) we could do a more complete job with the "new or existing user" problem.
At the time when we acquire the users email (either directly or through an IDP response) we could do a query for providers from email and use that to determine if it's a new or existing user. It comes at the cost of an extra request but at least it would be correct.
The other option is just to recommend that developers keep an index of known users in the Realtime Database, which is what ~90% of devs are doing anyway (for various reasons). We could even provide a FBUI helper function to manage this.
In any case, we should discuss it thoroughly.
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 learned something new today! I thought fetchProvidersForEmail
only worked for email/password accounts.
I agree, since this is going to be much more complicated, I'll save it for another PR.
@@ -82,7 +83,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { | |||
super.onActivityResult(requestCode, resultCode, data); | |||
|
|||
if (requestCode == RC_CHOOSE_PHOTO) { | |||
if (resultCode == RESULT_OK) { | |||
if (resultCode == ResultCodes.OK) { |
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 seems like an IntelliJ refactor bug, in this case I am not using AuthUI I want to see if the Camera returned Activity.RESULT_OK
@@ -204,8 +204,8 @@ startActivityForResult( | |||
|
|||
#####Response codes | |||
The authentication flow only provides three response codes: |
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 sentence is no longer true ("only three response codes..."). Maybe we should just point to the ResponseCodes file here and rely on JavaDoc comments so we don't have to keep updating this.
@@ -69,7 +69,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) { | |||
|
|||
if (providerConfig == null) { | |||
// we don't have a provider to handle this | |||
finish(Activity.RESULT_CANCELED, new Intent()); | |||
finish(ResultCodes.UNKNOWN_ERROR, 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.
Changing all of the CANCELED to UNKNOWN_ERROR is still a breaking API change, as people checking for CANCELED in code will no longer see the same behavior.
This makes me think maybe we should simplify to three cases:
- OK
- CANCELED (with optional extras describing the error)
- NO_NETWORK (just to keep backwards compat, otherwise I'd say we lump this into CANCELED)
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 that, what about getErrorCode
in IdpResponse
?
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.
That seems like the right answer 👍
// user is signed in! | ||
startActivity(new Intent(this, WelcomeBackActivity.class)); | ||
finish(); | ||
if (data == 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.
nit: let's check if (requestCode != ResultCodes.OK && data == null)
here to more clearly indicate what happened.
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 extracted ErrorCodes
so ResultCodes
now only has the activity codes so should we just remove that class altogether or keep it in case we want to add another result code?
`ResultCodes.NO_NETWORK` if sign in failed due to a lack of network connectivity, | ||
and `ResultCodes.UNKNOWN_ERROR` for all other errors. | ||
Typically, the only recourse for most apps if sign in fails is to ask | ||
the user to sign in again later, or proceed with anonymous sign-in if supported. |
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.
Here's the updated README
startActivity(SignedInActivity.createIntent(this, IdpResponse.fromResultIntent(data))); | ||
finish(); | ||
return; | ||
if (data != 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.
What do you think of this way of handling the sign in response?
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.
There are a few things here:
- I'd like checking for RESULT_OK to be a top-level check. That's our contract, not the nullity of
data
. - When the resultCode is CANCELED we will have two quick snackbars right? One because the IdpResponse contains UNKNOWN_ERROR and one because it was canceled.
} | ||
if (requestCode == RC_SIGN_IN) { | ||
if (data != null) { | ||
IdpResponse response = IdpResponse.fromResultIntent(data); |
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:
if (requestCode == RC_SIGN_IN) {
IdpResponse response = (data == null) ? null : IdpResponse.fromResultIntent(data);
if (resultCode == ResultCodes.OK) {
startActivity(SignedInActivity.createIntent(this, response));
finish();
return
} else if (resultCode == ResultCodes.CANCELED) {
// Canceled...
} else if (response != null) {
// All the error handling ...
}
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.
That's cleaner, but it doesn't work. Since we are now returning ResultCodes.CANCELED
with an added error code, it's a little different. So how about this?
/** | ||
* Sign in failed due to lack of network connection | ||
**/ | ||
public static final int NO_NETWORK = 10; |
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.
Isn't this one actually a result code and not an error? We still need to return this via onActivityResult in order to honor our previous API contract.
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.
No, I don't think so. It should be an error code, but to keep backwards compatibility I'm returning it in SignInDelegate
which the old ResultCodes
points to.
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.
Ah ok i see what you did here. So you're returning it in both places for SignInDelegate, with the recommendation being that from here on out people use the errorCode to make decisions when the result is not OK. That makes sense!
Everything LGTM. I need to figure out what the next milestone will look like before I merge this into master, we might want a dev branch instead. |
@samtstern Sounds good to me! |
return resultIntent.getParcelableExtra(ExtraConstants.EXTRA_IDP_RESPONSE); | ||
} else { | ||
return 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.
Since fromResultIntent
is annotated as @Nullable
, I think this was the original intention.
@samtstern I added 1 commit since you last approved my PR. |
# Conflicts: # auth/README.md
Your added commit looks good to me, thanks for flagging it. See #418 (comment) |
…codes # Conflicts: # app/src/main/res/values/strings.xml # auth/src/main/java/com/firebase/ui/auth/ui/ResultCodes.java # auth/src/main/java/com/firebase/ui/auth/ui/accountlink/WelcomeBackIdpPrompt.java # auth/src/main/java/com/firebase/ui/auth/ui/email/EmailHintContainerActivity.java # auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailActivity.java
Signed-off-by: Alex Saveau <[email protected]>
@samtstern looks like we're good to go! |
#405