Skip to content

Add support for GitHub IDP #1199

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

Merged
merged 27 commits into from
Jun 19, 2018
Merged

Add support for GitHub IDP #1199

merged 27 commits into from
Jun 19, 2018

Conversation

SUPERCILEX
Copy link
Collaborator

@SUPERCILEX SUPERCILEX commented Mar 22, 2018

Fixes #346

@samtstern Alright, I'm so pumped right now! I figured out how to get GitHub working and I proved the flexibility and scalability of the new architecture—die two birds with my one stone! ☠️😆

Anyway, ignore this PR for now since I've already got you saturated everywhere else.

@SUPERCILEX SUPERCILEX requested a review from samtstern as a code owner March 22, 2018 05:26
@SUPERCILEX
Copy link
Collaborator Author

Oh, and I forgot to mention that the build is going to fail because of translation issues. @samtstern Could you possibly kick off that process for these strings: https://github.com/firebase/FirebaseUI-Android/pull/1199/files#diff-059a92f7313626f64a0470b20e08afa4?

@SUPERCILEX SUPERCILEX mentioned this pull request Mar 22, 2018
Signed-off-by: Alex Saveau <[email protected]>
@samtstern samtstern added this to the 3.4.0 milestone Mar 26, 2018
# Conflicts:
#	auth/src/main/java/com/firebase/ui/auth/data/remote/SignInKickstarter.java
#	auth/src/main/java/com/firebase/ui/auth/ui/HelperActivityBase.java
#	auth/src/main/java/com/firebase/ui/auth/util/ExtraConstants.java
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
# Conflicts:
#	auth/src/main/java/com/firebase/ui/auth/viewmodel/RequestCodes.java
@SUPERCILEX SUPERCILEX changed the base branch from version-3.3.0-dev to master March 28, 2018 21:49
@samtstern samtstern changed the base branch from master to version-3.3.1-dev March 29, 2018 15:29
@samtstern samtstern changed the base branch from version-3.3.1-dev to version-3.4.0-dev April 13, 2018 19:48
@samtstern samtstern changed the base branch from version-3.4.0-dev to version-4.0.0-dev May 4, 2018 17:23
# Conflicts:
#	library/quality/quality.gradle
@samtstern samtstern modified the milestones: 4.0.0, 4.1.0 May 15, 2018
@SUPERCILEX SUPERCILEX changed the base branch from version-4.0.0-dev to version-4.1.0-dev May 29, 2018 00:00
# Conflicts:
#	auth/build.gradle
#	auth/src/main/res/values/styles.xml
# Conflicts:
#	auth/build.gradle
#	library/quality/quality.gradle
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
@SUPERCILEX
Copy link
Collaborator Author

@samtstern Think we can get the ball rolling on this one?

@samtstern
Copy link
Contributor

@SUPERCILEX sure, happy to get this one back on track.

Copy link
Contributor

@samtstern samtstern left a comment

Choose a reason for hiding this comment

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

Just some first-pass comments.


private interface GitHubOAuth {
@POST(KEY_ACCESS_TOKEN)
Call<JsonObject> getAuthToken(@Header("Accept") String header,
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we get stronger typing than JsonObject here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

WHAT!!!? How has no one ever told me about this before now??? For some reason, it never clicked that I could use something other than JsonObject. 🤦‍♂️ Heck, I even use Gson to deserialize objects outside of Retrofit! Well, time to go update my projects again. 😆 @samtstern Thanks a ton for jostling those brain cells around! ❤️

TL;DR: yes. 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

Hah that's funny that you didn't know because in my mind that's all Retrofit is good for!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Haha, yeah, I realize that now. 😊

.enableUrlBarHiding()
.setToolbarColor(ContextCompat.getColor(this, R.color.colorPrimary))
.build()
.launchUrl(this, (Uri) getIntent().getParcelableExtra(ExtraConstants.PARAMS));
Copy link
Contributor

Choose a reason for hiding this comment

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

We're casting the whole PARAMS to a Uri? Maybe it should come with a different ExtraConstants key?

if (mShouldCloseCustomTab) { // User pressed back
finish(RESULT_CANCELED, null);
}
mShouldCloseCustomTab = true;
Copy link
Contributor

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 why this is set to true on each onResume?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, I'll explain this and the recursive launch comment below. Basically, I've copied-ish all of this from Facebook since they actually did a really nice job with CCT. So here's what we're trying to do:

  • Launch CCT
  • If user presses back, close resources related to CCT
  • Same for success and failure, but send result data too

Given that CCT is going to redirect to our activity, we need a wrapper with special stuff like singleTop and ignored config changes so the stack doesn't nest itself. Now that we're guaranteed to have a single activity with a CCT layer on top, we can safely assume that onCreate is the only place to start CCT. So the current flow looks like this:

  • Launch CCT in onCreate
  • Receive redirects in onNewIntent

That creates a problem though: how do we close CCT? Android doesn't give you a nice way to close all activities on top of the stack AFAIK, so we're force to relaunch our wrapper activity with the CLEAR_TOP flag. That will recurse while killing CCT to bring us back to onNewIntent again where we check for the refresh action. At that point, we can finally finish with our result.

Now for the onResume stuff. Remember how we always have a CCT layer on top? That means onResume will only ever be called once... unless the user presses back. At that point, our wrapper activity gains focus for the second time and we can safely kill it knowing it was a back event.

Whew! 😌 I realize that's a painful explanation. I dunno, Facebook's code was even more complicated and took me forever to even distill down to this point. They had 2 activities they were juggling around—never understood why. Anyway, if you have any ideas to simplify this stuff, please send them over! 😄

Copy link
Contributor

Choose a reason for hiding this comment

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

That's all super cool! Can you just encode some of this in code comments? If you want you can literally copy what you just said to a class-level comment.

.putExtra(GitHubSignInHandler.KEY_GITHUB_CODE, code);
}

// Force a recursive launch to clear the Custom Tabs activity
Copy link
Contributor

Choose a reason for hiding this comment

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

What's going on here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

tools:ignore="ResourceName">
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="ResourceName">
<!-- Get access to the google-services project id. -->
<item name="project_id" type="string"/>
Copy link
Contributor

Choose a reason for hiding this comment

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

What does adding this item tag do?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh boy, forgot about that. Time to remove my hacks and write some docs! 😉

@SUPERCILEX
Copy link
Collaborator Author

So you added your project URL to the app config.xml and the asset links have the correct SHA-256? Usually, the non redirect means the hash doesn't match up with how the app was signed.

FYI gonna wait to see how you get through this to address your feedback 👍.

@SUPERCILEX
Copy link
Collaborator Author

Oh, just saw your edit, gimme a sec

@SUPERCILEX
Copy link
Collaborator Author

@samtstern can you check the app module's merged manifest? Is the value being pulled in or is lint just mad?

@samtstern
Copy link
Contributor

Here's what I see in the merged manifest:

$ cat app/build/intermediates/merged_manifests/debug/processDebugManifest/merged/AndroidManifest.xml | grep -B1 -A15 GitHub
        <activity
            android:name="com.firebase.ui.auth.ui.provider.GitHubLoginActivity"
            android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
            android:label=""
            android:launchMode="singleTop"
            android:theme="@style/FirebaseUI.Transparent" >
            <intent-filter android:autoVerify="true" >
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />

                <data
                    android:host="@string/firebase_web_host"
                    android:path="/__/auth/handler"
                    android:scheme="https" />
            </intent-filter>

Which looks fine to me? I don't think it should be a string literal at that point.

@SUPERCILEX
Copy link
Collaborator Author

Oh yeah, sorry, I guess you'll have to use the APK analyzer to see the finished product.

@samtstern
Copy link
Contributor

@SUPERCILEX here's what I see in APK analyzer:

        <activity
            android:theme="@ref/0x7f0f00d4"
            android:label="@string/0x21"
            android:name="com.firebase.ui.auth.ui.provider.GitHubLoginActivity"
            android:launchMode="1"
            android:configChanges="0x5b0">

            <intent-filter
                android:autoVerify="true">

                <action
                    android:name="android.intent.action.VIEW" />

                <category
                    android:name="android.intent.category.DEFAULT" />

                <category
                    android:name="android.intent.category.BROWSABLE" />

                <data
                    android:scheme="https"
                    android:host="@ref/0x7f0e005f"
                    android:path="/__/auth/handler" />
            </intent-filter>
        </activity>

And for comparison, Facebook:

        <activity
            android:theme="@ref/0x7f0f01b6"
            android:label="@ref/0x7f0e001f"
            android:name="com.facebook.FacebookActivity"
            android:configChanges="0x5b0" />

        <activity
            android:name="com.facebook.CustomTabActivity"
            android:exported="true">

            <intent-filter>

                <action
                    android:name="android.intent.action.VIEW" />

                <category
                    android:name="android.intent.category.DEFAULT" />

                <category
                    android:name="android.intent.category.BROWSABLE" />

                <data
                    android:scheme="@ref/0x7f0e005a" />
            </intent-filter>
        </activity>

So this may be a red herring.

@samtstern
Copy link
Contributor

samtstern commented Jun 18, 2018

If I hardcode the value in auth/AndroidManifest.xml to my firebaseapp domain I get a little farther, to this point. Which makes me think we need more info in the error messages:

2018-06-18 11:03:53.223 13023-13023/com.firebase.uidemo E/AuthUI: A sign-in error occurred.
    com.firebase.ui.auth.data.model.UserCancellationException: Unknown error
        at com.firebase.ui.auth.data.remote.GitHubSignInHandler.onActivityResult(GitHubSignInHandler.java:109)
        at com.firebase.ui.auth.ui.idp.AuthMethodPickerActivity.onActivityResult(AuthMethodPickerActivity.java:234)
        at android.app.Activity.dispatchActivityResult(Activity.java:7276)
        at android.app.ActivityThread.deliverResults(ActivityThread.java:4264)
        at android.app.ActivityThread.handleSendResult(ActivityThread.java:4312)
        at android.app.ActivityThread.-wrap19(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1644)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
2018-06-18 11:03:53.225 13023-13023/com.firebase.uidemo E/AuthUI: A sign-in error occurred.
    com.firebase.ui.auth.data.model.UserCancellationException: Unknown error
        at com.firebase.ui.auth.data.remote.GitHubSignInHandler.onActivityResult(GitHubSignInHandler.java:109)
        at com.firebase.ui.auth.ui.idp.AuthMethodPickerActivity.onActivityResult(AuthMethodPickerActivity.java:234)
        at android.app.Activity.dispatchActivityResult(Activity.java:7276)
        at android.app.ActivityThread.deliverResults(ActivityThread.java:4264)
        at android.app.ActivityThread.handleSendResult(ActivityThread.java:4312)
        at android.app.ActivityThread.-wrap19(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1644)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

Edit: Ignore this, I think this was a debugging artifact.

@SUPERCILEX
Copy link
Collaborator Author

So is the custom tab just closing itself? Or is it still stuck at the redirect URI? I'm pretty sure your Sha hash is wrong...

@SUPERCILEX
Copy link
Collaborator Author

Actually, if you want to test that the activity is at least registered correctly, click the open in chrome menu option when you're in the CCT. It should let you choose between chrome or FUI.

@samtstern
Copy link
Contributor

samtstern commented Jun 18, 2018

Now the CCT just closes itself. One interesting log line that appears:

2018-06-18 11:12:06.867 17569-17632/com.firebase.uidemo E/GraphResponse: {HttpStatus: 404, errorCode: 803, subErrorCode: -1, errorType: OAuthException, errorMessage: (#803) Cannot query users by their username (CHANGE-ME)}

Edit: I think that's unrelated Facebook junk.

@SUPERCILEX
Copy link
Collaborator Author

Mmmm... So if you go to your GitHub settings and revoke the tokens then log in, it brings you straight back to FUI (not the redirect URI)?

@samtstern
Copy link
Contributor

I think this is just a typo. I did .wellknown not .well-known ... sigh gonna clear a bunch of caches and try again. Sorry for the time waste!

@SUPERCILEX
Copy link
Collaborator Author

Nooooooooooooooo!!! Sam! 😂

@samtstern
Copy link
Contributor

samtstern commented Jun 18, 2018

I got it to work! Well I had to do the "Three Dots > Open In App > (select demo app)" but then it not only signed in with Github but also linked to my existing email/password account and perfectly updated the profile picture. Very impressed.

So remaining action items:

  1. I still think my documentation comments are worth fixing.
  2. I am gonna wait and try and get it to work without having to manually redirect the CCT. I assume I just have a bad cache somewhere from all my fiddling.

Update: Tried this on a fresh device and (1) is not an issue at all.

@SUPERCILEX
Copy link
Collaborator Author

Woot! 👏🍾 Yeah, I still want to improve the docs with your feedback and add a "WTF!? It's not working" section 😂

@SUPERCILEX
Copy link
Collaborator Author

@samtstern The last two commits include a bunch of docs improvements, what do you think?

@samtstern
Copy link
Contributor

@SUPERCILEX those are great! Final thing: let's just tone down the Github section a little bit to make it less scary. Removing things like "It's Github's fault" and "shooting yourself in the foot". Google has a really good relationship with Github and I don't wanna be out here criticizing them, even though it's good-natured.

@SUPERCILEX
Copy link
Collaborator Author

Haha, yeah, it could be taken the wrong way. 👍

@samtstern
Copy link
Contributor

@SUPERCILEX good to go!

@samtstern samtstern merged commit 45c12b1 into firebase:version-4.1.0-dev Jun 19, 2018
@SUPERCILEX
Copy link
Collaborator Author

Side note: I actually got into contact with GitHub yesterday, and they pointed me to the OAuth spec they use with the security reasons behind their decision (which I've linked to in the README)—really interesting stuff. In any case, I pointed out that the spec does allow configuring multiple redirect URIs so they were kind enough to put that on the backlog... Down the road, we might be able to simplify all this! 👏

TL;DR: I wasn't really being fair anyway now that I understand their reasoning. 😊

@SUPERCILEX SUPERCILEX deleted the github branch June 19, 2018 20:07
@SUPERCILEX
Copy link
Collaborator Author

Awesome sauce! And there aren't even any conflicts with #1358. 🙌

@samtstern
Copy link
Contributor

@SUPERCILEX that's good to hear that you got in contact with them. Also congrats on the first big feature on top of the new architecture. Very impressive how it scaled.

@SUPERCILEX
Copy link
Collaborator Author

Thanks! Yeah, I'm super happy we didn't get stuck at that 95%. 😉

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.

2 participants