From 375800a144fc566b5273866d4215e82dab046a2b Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Sat, 24 Mar 2018 23:28:13 -0700 Subject: [PATCH 01/19] Add support for GitHub IDP Signed-off-by: Alex Saveau --- .../firebase/uidemo/auth/AuthUiActivity.java | 90 ++++++-- .../uidemo/auth/SignedInActivity.java | 4 + app/src/main/res/layout/auth_ui_layout.xml | 30 +++ app/src/main/res/values/config.xml | 29 ++- app/src/main/res/values/strings.xml | 6 + auth/build.gradle | 4 + auth/src/main/AndroidManifest.xml | 32 ++- .../java/com/firebase/ui/auth/AuthUI.java | 40 +++- .../auth/data/remote/GitHubSignInHandler.java | 194 ++++++++++++++++++ .../auth/data/remote/SignInKickstarter.java | 2 + .../ui/auth/ui/HelperActivityBase.java | 4 +- .../auth/ui/idp/AuthMethodPickerActivity.java | 9 + .../ui/auth/ui/idp/SingleSignInActivity.java | 7 + .../ui/auth/ui/idp/WelcomeBackIdpPrompt.java | 9 + .../auth/ui/provider/GitHubLoginActivity.java | 81 ++++++++ .../firebase/ui/auth/util/ExtraConstants.java | 1 + .../ui/auth/util/data/ProviderUtils.java | 8 + .../ui/auth/viewmodel/RequestCodes.java | 5 +- .../res/drawable/fui_ic_github_white_24dp.xml | 12 ++ .../fui_idp_button_background_github.xml | 9 + .../main/res/layout/fui_idp_button_github.xml | 7 + auth/src/main/res/values/config.xml | 22 +- auth/src/main/res/values/strings.xml | 2 + auth/src/main/res/values/styles.xml | 6 + .../ui/auth/testhelpers/TestHelper.java | 6 + library/quality/quality.gradle | 1 + 26 files changed, 569 insertions(+), 51 deletions(-) create mode 100644 auth/src/main/java/com/firebase/ui/auth/data/remote/GitHubSignInHandler.java create mode 100644 auth/src/main/java/com/firebase/ui/auth/ui/provider/GitHubLoginActivity.java create mode 100644 auth/src/main/res/drawable/fui_ic_github_white_24dp.xml create mode 100644 auth/src/main/res/drawable/fui_idp_button_background_github.xml create mode 100644 auth/src/main/res/layout/fui_idp_button_github.xml diff --git a/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java b/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java index 9e26d06d7..d6746e8df 100644 --- a/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java +++ b/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java @@ -64,13 +64,14 @@ public class AuthUiActivity extends AppCompatActivity { @BindView(R.id.google_provider) CheckBox mUseGoogleProvider; @BindView(R.id.facebook_provider) CheckBox mUseFacebookProvider; @BindView(R.id.twitter_provider) CheckBox mUseTwitterProvider; + @BindView(R.id.github_provider) CheckBox mUseGitHubProvider; @BindView(R.id.email_provider) CheckBox mUseEmailProvider; @BindView(R.id.phone_provider) CheckBox mUsePhoneProvider; - @BindView(R.id.default_theme) RadioButton mUseDefaultTheme; - @BindView(R.id.green_theme) RadioButton mUseGreenTheme; - @BindView(R.id.purple_theme) RadioButton mUsePurpleTheme; - @BindView(R.id.dark_theme) RadioButton mUseDarkTheme; + @BindView(R.id.default_theme) RadioButton mDefaultTheme; + @BindView(R.id.green_theme) RadioButton mGreenTheme; + @BindView(R.id.purple_theme) RadioButton mPurpleTheme; + @BindView(R.id.dark_theme) RadioButton mDarkTheme; @BindView(R.id.firebase_logo) RadioButton mFirebaseLogo; @BindView(R.id.google_logo) RadioButton mGoogleLogo; @@ -82,13 +83,17 @@ public class AuthUiActivity extends AppCompatActivity { @BindView(R.id.google_privacy) RadioButton mUseGooglePrivacyPolicy; @BindView(R.id.firebase_privacy) RadioButton mUseFirebasePrivacyPolicy; - @BindView(R.id.google_scopes_header) TextView mGoogleScopesLabel; + @BindView(R.id.google_scopes_header) TextView mGoogleScopesHeader; @BindView(R.id.google_scope_drive_file) CheckBox mGoogleScopeDriveFile; @BindView(R.id.google_scope_youtube_data) CheckBox mGoogleScopeYoutubeData; - @BindView(R.id.facebook_permissions_header) TextView mFacebookScopesLabel; - @BindView(R.id.facebook_permission_friends) CheckBox mFacebookScopeFriends; - @BindView(R.id.facebook_permission_photos) CheckBox mFacebookScopePhotos; + @BindView(R.id.facebook_permissions_header) TextView mFacebookPermissionsHeader; + @BindView(R.id.facebook_permission_friends) CheckBox mFacebookPermissionFriends; + @BindView(R.id.facebook_permission_photos) CheckBox mFacebookPermissionPhotos; + + @BindView(R.id.github_permissions_header) TextView mGitHubPermissionsHeader; + @BindView(R.id.github_permission_repo) CheckBox mGitHubPermissionRepo; + @BindView(R.id.github_permission_gist) CheckBox mGitHubPermissionGist; @BindView(R.id.credential_selector_enabled) CheckBox mEnableCredentialSelector; @BindView(R.id.hint_selector_enabled) CheckBox mEnableHintSelector; @@ -141,12 +146,27 @@ public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { mUseTwitterProvider.setText(R.string.twitter_label_missing_config); } - if (isGoogleMisconfigured() || isFacebookMisconfigured() || isTwitterMisconfigured()) { + if (!isGitHubConfigured()) { + mUseGitHubProvider.setChecked(false); + mUseGitHubProvider.setEnabled(false); + mUseGitHubProvider.setText(R.string.github_label_missing_config); + setGitHubScopesEnabled(false); + } else { + setGitHubScopesEnabled(mUseGoogleProvider.isChecked()); + mUseGitHubProvider.setOnCheckedChangeListener(new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { + setGitHubScopesEnabled(checked); + } + }); + } + + if (isGoogleMisconfigured() || isFacebookMisconfigured() || isTwitterMisconfigured() || !isGitHubConfigured()) { showSnackbar(R.string.configuration_required); } if (AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES) { - mUseDarkTheme.setChecked(true); + mDarkTheme.setChecked(true); } } @@ -214,7 +234,7 @@ private void startSignedInActivity(IdpResponse response) { @OnClick({R.id.default_theme, R.id.purple_theme, R.id.green_theme, R.id.dark_theme}) public void toggleDarkTheme() { - int mode = mUseDarkTheme.isChecked() ? + int mode = mDarkTheme.isChecked() ? AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_AUTO; AppCompatDelegate.setDefaultNightMode(mode); getDelegate().setLocalNightMode(mode); @@ -222,11 +242,11 @@ public void toggleDarkTheme() { @StyleRes private int getSelectedTheme() { - if (mUseGreenTheme.isChecked()) { + if (mGreenTheme.isChecked()) { return R.style.GreenTheme; } - if (mUsePurpleTheme.isChecked()) { + if (mPurpleTheme.isChecked()) { return R.style.PurpleTheme; } @@ -261,6 +281,12 @@ private List getSelectedProviders() { selectedProviders.add(new IdpConfig.TwitterBuilder().build()); } + if (mUseGitHubProvider.isChecked()) { + selectedProviders.add(new IdpConfig.GitHubBuilder() + .setPermissions(getGitHubPermissions()) + .build()); + } + if (mUseEmailProvider.isChecked()) { selectedProviders.add(new IdpConfig.EmailBuilder() .setRequireName(mRequireName.isChecked()) @@ -308,16 +334,31 @@ private boolean isTwitterMisconfigured() { return twitterConfigs.contains(AuthUI.UNCONFIGURED_CONFIG_VALUE); } + private boolean isGitHubConfigured() { + List gitHubConfigs = Arrays.asList( + getString(R.string.github_client_id), + getString(R.string.github_client_secret) + ); + + return !gitHubConfigs.contains(AuthUI.UNCONFIGURED_CONFIG_VALUE); + } + private void setGoogleScopesEnabled(boolean enabled) { - mGoogleScopesLabel.setEnabled(enabled); + mGoogleScopesHeader.setEnabled(enabled); mGoogleScopeDriveFile.setEnabled(enabled); mGoogleScopeYoutubeData.setEnabled(enabled); } private void setFacebookScopesEnabled(boolean enabled) { - mFacebookScopesLabel.setEnabled(enabled); - mFacebookScopeFriends.setEnabled(enabled); - mFacebookScopePhotos.setEnabled(enabled); + mFacebookPermissionsHeader.setEnabled(enabled); + mFacebookPermissionFriends.setEnabled(enabled); + mFacebookPermissionPhotos.setEnabled(enabled); + } + + private void setGitHubScopesEnabled(boolean enabled) { + mGitHubPermissionsHeader.setEnabled(enabled); + mGitHubPermissionRepo.setEnabled(enabled); + mGitHubPermissionGist.setEnabled(enabled); } private List getGoogleScopes() { @@ -333,15 +374,26 @@ private List getGoogleScopes() { private List getFacebookPermissions() { List result = new ArrayList<>(); - if (mFacebookScopeFriends.isChecked()) { + if (mFacebookPermissionFriends.isChecked()) { result.add("user_friends"); } - if (mFacebookScopePhotos.isChecked()) { + if (mFacebookPermissionPhotos.isChecked()) { result.add("user_photos"); } return result; } + private List getGitHubPermissions() { + List result = new ArrayList<>(); + if (mGitHubPermissionRepo.isChecked()) { + result.add("repo"); + } + if (mGitHubPermissionGist.isChecked()) { + result.add("gist"); + } + return result; + } + private void showSnackbar(@StringRes int errorMessageRes) { Snackbar.make(mRootView, errorMessageRes, Snackbar.LENGTH_LONG).show(); } diff --git a/app/src/main/java/com/firebase/uidemo/auth/SignedInActivity.java b/app/src/main/java/com/firebase/uidemo/auth/SignedInActivity.java index dfe10f089..c2f8815e2 100644 --- a/app/src/main/java/com/firebase/uidemo/auth/SignedInActivity.java +++ b/app/src/main/java/com/firebase/uidemo/auth/SignedInActivity.java @@ -41,6 +41,7 @@ import com.google.firebase.auth.FacebookAuthProvider; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.auth.GithubAuthProvider; import com.google.firebase.auth.GoogleAuthProvider; import com.google.firebase.auth.PhoneAuthProvider; import com.google.firebase.auth.TwitterAuthProvider; @@ -166,6 +167,9 @@ private void populateProfile() { case TwitterAuthProvider.PROVIDER_ID: providers.add(getString(R.string.providers_twitter)); break; + case GithubAuthProvider.PROVIDER_ID: + providers.add(getString(R.string.providers_github)); + break; case EmailAuthProvider.PROVIDER_ID: providers.add(getString(R.string.providers_email)); break; diff --git a/app/src/main/res/layout/auth_ui_layout.xml b/app/src/main/res/layout/auth_ui_layout.xml index f04e55725..d159e12ce 100644 --- a/app/src/main/res/layout/auth_ui_layout.xml +++ b/app/src/main/res/layout/auth_ui_layout.xml @@ -66,6 +66,13 @@ android:checked="true" android:text="@string/providers_twitter" /> + + + + + + + + - CHANGE-ME + 654633568031642 - - fbYOUR_APP_ID + + fb654633568031642 - CHANGE-ME + ojwOB05fmN2TiNoaZnVQRAp4N - CHANGE-ME + di6UQRwDaxsRQ7RT5M5QcUgq19Dn4NY04QLMjaDhyxqmUy92Pe + + + a10c87f4aacc7e5ab05a + + + 583b444835198b8e025062eda2031f89ed62ed07 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 69e00a1e5..2cdd29854 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -19,6 +19,7 @@ Auth providers Google Facebook + GitHub Twitter Email Phone @@ -50,6 +51,10 @@ Friends Photos + Example extra GitHub scopes + Repo + Gist + Other Options Enable Smart Lock\'s credential selector Enable Smart Lock\'s hint selector @@ -60,6 +65,7 @@ Google configuration missing Facebook configuration missing Twitter configuration missing + GitHub configuration missing Sign in cancelled No internet connection diff --git a/auth/build.gradle b/auth/build.gradle index 97d4536cf..771593c36 100644 --- a/auth/build.gradle +++ b/auth/build.gradle @@ -50,6 +50,10 @@ dependencies { implementation "com.android.support:customtabs:$supportLibraryVersion" compileOnly("com.twitter.sdk.android:twitter-core:3.1.1@aar") { transitive = true } + // Retrofit + compile 'com.squareup.retrofit2:retrofit:2.4.0' + compile 'com.squareup.retrofit2:converter-gson:2.4.0' + testImplementation 'junit:junit:4.12' //noinspection GradleDynamicVersion testImplementation 'org.mockito:mockito-core:2.15.+' diff --git a/auth/src/main/AndroidManifest.xml b/auth/src/main/AndroidManifest.xml index 841702f93..31157073a 100644 --- a/auth/src/main/AndroidManifest.xml +++ b/auth/src/main/AndroidManifest.xml @@ -29,6 +29,32 @@ android:exported="false" android:theme="@style/FirebaseUI.Transparent" /> + + + + + + + + + + + + + + + - - (Arrays.asList( GoogleAuthProvider.PROVIDER_ID, FacebookAuthProvider.PROVIDER_ID, - TwitterAuthProvider.PROVIDER_ID))); + TwitterAuthProvider.PROVIDER_ID, + GithubAuthProvider.PROVIDER_ID))); @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public static final String UNCONFIGURED_CONFIG_VALUE = "CHANGE-ME"; @@ -831,6 +835,34 @@ public TwitterBuilder() { R.string.twitter_consumer_secret); } } + + /** + * {@link IdpConfig} builder for the GitHub provider. + */ + public static final class GitHubBuilder extends Builder { + public GitHubBuilder() { + //noinspection deprecation taking a hit for the backcompat team + super(GithubAuthProvider.PROVIDER_ID); + Preconditions.checkConfigured(getApplicationContext(), + "GitHub provider unconfigured. Make sure to add your client id and secret." + + " See the docs for more info:" + + " https://github.com/firebase/FirebaseUI-Android/blob/master/auth/README.md#github", + R.string.github_client_id, + R.string.github_client_secret); + } + + /** + * Specifies the additional permissions to be requested. Available permissions can be + * found here. + */ + @SuppressWarnings({"deprecation", "NullableProblems"}) // For backcompat + @NonNull + public GitHubBuilder setPermissions(@NonNull List permissions) { + getParams().putStringArrayList( + ExtraConstants.EXTRA_GITHUB_PERMISSIONS, new ArrayList<>(permissions)); + return this; + } + } } /** diff --git a/auth/src/main/java/com/firebase/ui/auth/data/remote/GitHubSignInHandler.java b/auth/src/main/java/com/firebase/ui/auth/data/remote/GitHubSignInHandler.java new file mode 100644 index 000000000..d43864a4b --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/data/remote/GitHubSignInHandler.java @@ -0,0 +1,194 @@ +package com.firebase.ui.auth.data.remote; + +import android.app.Application; +import android.content.Intent; +import android.net.Uri; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.RestrictTo; +import android.text.TextUtils; + +import com.firebase.ui.auth.AuthUI; +import com.firebase.ui.auth.ErrorCodes; +import com.firebase.ui.auth.FirebaseUiException; +import com.firebase.ui.auth.IdpResponse; +import com.firebase.ui.auth.R; +import com.firebase.ui.auth.data.model.Resource; +import com.firebase.ui.auth.data.model.User; +import com.firebase.ui.auth.data.model.UserCancellationException; +import com.firebase.ui.auth.ui.HelperActivityBase; +import com.firebase.ui.auth.ui.provider.GitHubLoginActivity; +import com.firebase.ui.auth.util.ExtraConstants; +import com.firebase.ui.auth.viewmodel.RequestCodes; +import com.firebase.ui.auth.viewmodel.idp.ProviderSignInBase; +import com.google.firebase.auth.GithubAuthProvider; +import com.google.gson.JsonObject; + +import java.util.ArrayList; +import java.util.List; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; +import retrofit2.http.GET; +import retrofit2.http.Header; +import retrofit2.http.POST; +import retrofit2.http.Query; + +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +public class GitHubSignInHandler extends ProviderSignInBase + implements Callback { + public static final String REDIRECT_ACTION = "github_redirect"; + public static final String RESULT_CODE = "result_code"; + public static final String KEY_GITHUB_CODE = "github_code"; + + private static final String SCHEME = "https"; + private static final String AUTHORITY = "github.com"; + private static final String OAUTH = "login/oauth"; + private static final GitHubOAuth RETROFIT_GITHUB_OAUTH = new Retrofit.Builder() + .baseUrl(SCHEME + "://" + AUTHORITY + "/" + OAUTH + "/") + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(GitHubOAuth.class); + private static final GitHubApi RETROFIT_GITHUB = new Retrofit.Builder() + .baseUrl(SCHEME + "://api." + AUTHORITY + "/") + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(GitHubApi.class); + + private static final String KEY_ACCESS_TOKEN = "access_token"; + private static final String EMAIL = "user:email"; + + private List mPermissions; + + public GitHubSignInHandler(Application application) { + super(application); + } + + private static IdpResponse createIdpResponse( + @NonNull String token, + @Nullable String email, + @Nullable String name, + @Nullable Uri photoUri) { + return new IdpResponse.Builder( + new User.Builder(GithubAuthProvider.PROVIDER_ID, email) + .setName(name) + .setPhotoUri(photoUri) + .build()) + .setToken(token) + .build(); + } + + @Override + protected void onCreate() { + List permissions = new ArrayList<>(getArguments().getParams() + .getStringArrayList(ExtraConstants.EXTRA_GITHUB_PERMISSIONS)); + if (!permissions.contains(EMAIL)) { permissions.add(EMAIL); } + mPermissions = permissions; + } + + @Override + public void startSignIn(@NonNull HelperActivityBase activity) { + activity.startActivityForResult(GitHubLoginActivity.createIntent(activity, + new Uri.Builder().scheme(SCHEME) + .authority(AUTHORITY) + .path(OAUTH + "/authorize") + .appendQueryParameter("client_id", + getApplication().getString(R.string.github_client_id)) + .appendQueryParameter("scope", TextUtils.join(",", mPermissions)) + .build()), + RequestCodes.GITHUB_PROVIDER); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + if (requestCode != RequestCodes.GITHUB_PROVIDER) { return; } + + if (data == null) { + setResult(Resource.forFailure(new UserCancellationException())); + return; + } + + if (data.hasExtra(KEY_GITHUB_CODE)) { + setResult(Resource.forLoading()); + RETROFIT_GITHUB_OAUTH.getAuthToken( + "application/json", + getApplication().getString(R.string.github_client_id), + getApplication().getString(R.string.github_client_secret), + data.getStringExtra(KEY_GITHUB_CODE) + ).enqueue(this); + } else { + setResult(Resource.forFailure(new FirebaseUiException(ErrorCodes.PROVIDER_ERROR))); + } + } + + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful()) { + final String token = response.body().get(KEY_ACCESS_TOKEN).getAsString(); + RETROFIT_GITHUB.getUser("token " + token).enqueue(new ProfileRequest(token)); + } else { + setResult(Resource.forFailure(new FirebaseUiException( + ErrorCodes.PROVIDER_ERROR, response.message()))); + } + } + + @Override + public void onFailure(Call call, Throwable throwable) { + setResult(Resource.forFailure(new FirebaseUiException( + ErrorCodes.PROVIDER_ERROR, throwable))); + } + + private interface GitHubOAuth { + @POST(KEY_ACCESS_TOKEN) + Call getAuthToken(@Header("Accept") String header, + @Query("client_id") String id, + @Query("client_secret") String secret, + @Query("code") String code); + } + + private interface GitHubApi { + @GET("user") + Call getUser(@Header("Authorization") String token); + } + + private class ProfileRequest implements Callback { + private final String mToken; + + public ProfileRequest(String token) { + mToken = token; + } + + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful()) { + JsonObject body = response.body(); + + String email = null; + if (body.get("email") != null) { + email = body.get("email").getAsString(); + } + String name = null; + if (body.get("name") != null) { + name = body.get("name").getAsString(); + } + Uri profileUri = null; + if (body.get("avatar_url") != null) { + profileUri = Uri.parse(body.get("avatar_url").getAsString()); + } + + setResult(Resource.forSuccess(createIdpResponse(mToken, email, name, profileUri))); + } else { + setResult(Resource.forFailure(new FirebaseUiException( + ErrorCodes.PROVIDER_ERROR, response.message()))); + } + } + + @Override + public void onFailure(Call call, Throwable throwable) { + GitHubSignInHandler.this.onFailure(call, throwable); + } + } +} diff --git a/auth/src/main/java/com/firebase/ui/auth/data/remote/SignInKickstarter.java b/auth/src/main/java/com/firebase/ui/auth/data/remote/SignInKickstarter.java index e35eece61..7d8553670 100644 --- a/auth/src/main/java/com/firebase/ui/auth/data/remote/SignInKickstarter.java +++ b/auth/src/main/java/com/firebase/ui/auth/data/remote/SignInKickstarter.java @@ -37,6 +37,7 @@ import com.google.firebase.auth.FacebookAuthProvider; import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException; import com.google.firebase.auth.FirebaseAuthInvalidUserException; +import com.google.firebase.auth.GithubAuthProvider; import com.google.firebase.auth.GoogleAuthProvider; import com.google.firebase.auth.PhoneAuthProvider; import com.google.firebase.auth.TwitterAuthProvider; @@ -130,6 +131,7 @@ private void redirectSignIn(String provider, String email) { case GoogleAuthProvider.PROVIDER_ID: case FacebookAuthProvider.PROVIDER_ID: case TwitterAuthProvider.PROVIDER_ID: + case GithubAuthProvider.PROVIDER_ID: setResult(Resource.forUsableFailure(new IntentRequiredException( SingleSignInActivity.createIntent( getApplication(), diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/HelperActivityBase.java b/auth/src/main/java/com/firebase/ui/auth/ui/HelperActivityBase.java index 9bd618ea3..3d89ee32a 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/HelperActivityBase.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/HelperActivityBase.java @@ -45,8 +45,8 @@ public static Intent createBaseIntent( } @Override - protected void onCreate(Bundle savedInstance) { - super.onCreate(savedInstance); + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); mAuthHelper = new AuthHelper(getFlowParams()); mProgressDialogHolder = new ProgressDialogHolder(this); } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java index 640866921..39945f9a7 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java @@ -39,6 +39,7 @@ import com.firebase.ui.auth.data.model.UserCancellationException; import com.firebase.ui.auth.data.remote.EmailSignInHandler; import com.firebase.ui.auth.data.remote.FacebookSignInHandler; +import com.firebase.ui.auth.data.remote.GitHubSignInHandler; import com.firebase.ui.auth.data.remote.GoogleSignInHandler; import com.firebase.ui.auth.data.remote.PhoneSignInHandler; import com.firebase.ui.auth.data.remote.TwitterSignInHandler; @@ -49,6 +50,7 @@ import com.firebase.ui.auth.viewmodel.idp.SocialProviderResponseHandler; import com.google.firebase.auth.EmailAuthProvider; import com.google.firebase.auth.FacebookAuthProvider; +import com.google.firebase.auth.GithubAuthProvider; import com.google.firebase.auth.GoogleAuthProvider; import com.google.firebase.auth.PhoneAuthProvider; import com.google.firebase.auth.TwitterAuthProvider; @@ -148,6 +150,13 @@ private void populateIdpList(List providerConfigs, buttonLayout = R.layout.fui_idp_button_twitter; break; + case GithubAuthProvider.PROVIDER_ID: + GitHubSignInHandler github = supplier.get(GitHubSignInHandler.class); + github.init(idpConfig); + provider = github; + + buttonLayout = R.layout.fui_idp_button_github; + break; case EmailAuthProvider.PROVIDER_ID: EmailSignInHandler email = supplier.get(EmailSignInHandler.class); email.init(null); diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/idp/SingleSignInActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/idp/SingleSignInActivity.java index 8e1c0c7fa..2254ffc2c 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/idp/SingleSignInActivity.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/idp/SingleSignInActivity.java @@ -17,6 +17,7 @@ import com.firebase.ui.auth.data.model.State; import com.firebase.ui.auth.data.model.User; import com.firebase.ui.auth.data.remote.FacebookSignInHandler; +import com.firebase.ui.auth.data.remote.GitHubSignInHandler; import com.firebase.ui.auth.data.remote.GoogleSignInHandler; import com.firebase.ui.auth.data.remote.TwitterSignInHandler; import com.firebase.ui.auth.ui.HelperActivityBase; @@ -26,6 +27,7 @@ import com.firebase.ui.auth.viewmodel.idp.ProviderSignInBase; import com.firebase.ui.auth.viewmodel.idp.SocialProviderResponseHandler; import com.google.firebase.auth.FacebookAuthProvider; +import com.google.firebase.auth.GithubAuthProvider; import com.google.firebase.auth.GoogleAuthProvider; import com.google.firebase.auth.TwitterAuthProvider; @@ -74,6 +76,11 @@ protected void onCreate(Bundle savedInstanceState) { twitter.init(null); mProvider = twitter; break; + case GithubAuthProvider.PROVIDER_ID: + GitHubSignInHandler github = supplier.get(GitHubSignInHandler.class); + github.init(providerConfig); + mProvider = github; + break; default: throw new IllegalStateException("Invalid provider id: " + provider); } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/idp/WelcomeBackIdpPrompt.java b/auth/src/main/java/com/firebase/ui/auth/ui/idp/WelcomeBackIdpPrompt.java index 9113d97a8..71a29fd9b 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/idp/WelcomeBackIdpPrompt.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/idp/WelcomeBackIdpPrompt.java @@ -37,6 +37,7 @@ import com.firebase.ui.auth.data.model.State; import com.firebase.ui.auth.data.model.User; import com.firebase.ui.auth.data.remote.FacebookSignInHandler; +import com.firebase.ui.auth.data.remote.GitHubSignInHandler; import com.firebase.ui.auth.data.remote.GoogleSignInHandler; import com.firebase.ui.auth.data.remote.TwitterSignInHandler; import com.firebase.ui.auth.ui.AppCompatBase; @@ -47,6 +48,7 @@ import com.firebase.ui.auth.viewmodel.idp.LinkingSocialProviderResponseHandler; import com.firebase.ui.auth.viewmodel.idp.ProviderSignInBase; import com.google.firebase.auth.FacebookAuthProvider; +import com.google.firebase.auth.GithubAuthProvider; import com.google.firebase.auth.GoogleAuthProvider; import com.google.firebase.auth.TwitterAuthProvider; @@ -121,6 +123,13 @@ protected void onCreate(Bundle savedInstanceState) { providerName = R.string.fui_idp_name_twitter; break; + case GithubAuthProvider.PROVIDER_ID: + GitHubSignInHandler github = supplier.get(GitHubSignInHandler.class); + github.init(config); + mProvider = github; + + providerName = R.string.fui_idp_name_github; + break; default: throw new IllegalStateException("Invalid provider id: " + providerId); } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/provider/GitHubLoginActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/provider/GitHubLoginActivity.java new file mode 100644 index 000000000..8edd1c23d --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/ui/provider/GitHubLoginActivity.java @@ -0,0 +1,81 @@ +package com.firebase.ui.auth.ui.provider; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.RestrictTo; +import android.support.customtabs.CustomTabsIntent; +import android.support.v4.content.ContextCompat; + +import com.firebase.ui.auth.R; +import com.firebase.ui.auth.data.remote.GitHubSignInHandler; +import com.firebase.ui.auth.ui.HelperActivityBase; +import com.firebase.ui.auth.util.ExtraConstants; + +@SuppressLint("GoogleAppIndexingApiWarning") +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +public class GitHubLoginActivity extends HelperActivityBase { + private static final String REFRESH_ACTION = "refresh_action"; + + private boolean mShouldCloseCustomTab; + + @NonNull + public static Intent createIntent(Context context, Uri starter) { + return new Intent(context, GitHubLoginActivity.class) + .putExtra(ExtraConstants.EXTRA_PARAMS, starter); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState == null) { + new CustomTabsIntent.Builder() + .setShowTitle(true) + .enableUrlBarHiding() + .setToolbarColor(ContextCompat.getColor(this, R.color.colorPrimary)) + .build() + .launchUrl(this, + (Uri) getIntent().getParcelableExtra(ExtraConstants.EXTRA_PARAMS)); + mShouldCloseCustomTab = false; + } + } + + @Override + protected void onResume() { + super.onResume(); + if (mShouldCloseCustomTab) { // User pressed back + finish(RESULT_CANCELED, null); + } + mShouldCloseCustomTab = true; + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + mShouldCloseCustomTab = false; + + if (REFRESH_ACTION.equals(intent.getAction())) { + finish(RESULT_OK, (Intent) intent.getParcelableExtra(ExtraConstants.EXTRA_PARAMS)); + return; + } + + Intent result = new Intent().setAction(GitHubSignInHandler.REDIRECT_ACTION); + + String code = intent.getData().getQueryParameter("code"); + if (code == null) { + result.putExtra(GitHubSignInHandler.RESULT_CODE, RESULT_CANCELED); + } else { + result.putExtra(GitHubSignInHandler.RESULT_CODE, RESULT_OK) + .putExtra(GitHubSignInHandler.KEY_GITHUB_CODE, code); + } + + // Force a recursive launch to clear the Custom Tabs activity + startActivity(new Intent(this, GitHubLoginActivity.class) + .putExtra(ExtraConstants.EXTRA_PARAMS, result) + .setAction(REFRESH_ACTION) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP)); + } +} diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ExtraConstants.java b/auth/src/main/java/com/firebase/ui/auth/util/ExtraConstants.java index 06a9147e0..a0c9f1b0c 100644 --- a/auth/src/main/java/com/firebase/ui/auth/util/ExtraConstants.java +++ b/auth/src/main/java/com/firebase/ui/auth/util/ExtraConstants.java @@ -31,6 +31,7 @@ public final class ExtraConstants { public static final String EXTRA_REQUIRE_NAME = "extra_require_name"; public static final String EXTRA_GOOGLE_SIGN_IN_OPTIONS = "extra_google_sign_in_options"; public static final String EXTRA_FACEBOOK_PERMISSIONS = "extra_facebook_permissions"; + public static final String EXTRA_GITHUB_PERMISSIONS = "extra_github_permissions"; public static final String EXTRA_PARAMS = "extra_params"; public static final String EXTRA_PHONE = "extra_phone_number"; diff --git a/auth/src/main/java/com/firebase/ui/auth/util/data/ProviderUtils.java b/auth/src/main/java/com/firebase/ui/auth/util/data/ProviderUtils.java index c1664fa21..477dbe3f9 100644 --- a/auth/src/main/java/com/firebase/ui/auth/util/data/ProviderUtils.java +++ b/auth/src/main/java/com/firebase/ui/auth/util/data/ProviderUtils.java @@ -29,6 +29,7 @@ import com.google.firebase.auth.EmailAuthProvider; import com.google.firebase.auth.FacebookAuthProvider; import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.GithubAuthProvider; import com.google.firebase.auth.GoogleAuthProvider; import com.google.firebase.auth.PhoneAuthProvider; import com.google.firebase.auth.ProviderQueryResult; @@ -38,6 +39,7 @@ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public final class ProviderUtils { + private static final String GITHUB_IDENTITY = "https://github.com"; private static final String PHONE_IDENTITY = "https://phone.firebase"; private ProviderUtils() { @@ -54,6 +56,8 @@ public static AuthCredential getAuthCredential(IdpResponse response) { case TwitterAuthProvider.PROVIDER_ID: return TwitterAuthProvider.getCredential(response.getIdpToken(), response.getIdpSecret()); + case GithubAuthProvider.PROVIDER_ID: + return GithubAuthProvider.getCredential(response.getIdpToken()); default: return null; } @@ -80,6 +84,8 @@ public static String providerIdToAccountType(@AuthUI.SupportedProvider @NonNull return IdentityProviders.FACEBOOK; case TwitterAuthProvider.PROVIDER_ID: return IdentityProviders.TWITTER; + case GithubAuthProvider.PROVIDER_ID: + return GITHUB_IDENTITY; case PhoneAuthProvider.PROVIDER_ID: return PHONE_IDENTITY; // The account type for email/password creds is null @@ -98,6 +104,8 @@ public static String accountTypeToProviderId(@NonNull String accountType) { return FacebookAuthProvider.PROVIDER_ID; case IdentityProviders.TWITTER: return TwitterAuthProvider.PROVIDER_ID; + case GITHUB_IDENTITY: + return GithubAuthProvider.PROVIDER_ID; case PHONE_IDENTITY: return PhoneAuthProvider.PROVIDER_ID; default: diff --git a/auth/src/main/java/com/firebase/ui/auth/viewmodel/RequestCodes.java b/auth/src/main/java/com/firebase/ui/auth/viewmodel/RequestCodes.java index dff77d44b..b23f18f84 100644 --- a/auth/src/main/java/com/firebase/ui/auth/viewmodel/RequestCodes.java +++ b/auth/src/main/java/com/firebase/ui/auth/viewmodel/RequestCodes.java @@ -37,8 +37,11 @@ public final class RequestCodes { /** Request code for retrieving a Google credential. */ public static final int GOOGLE_PROVIDER = 110; + /** Request code for retrieving a GitHub credential. */ + public static final int GITHUB_PROVIDER = 111; + /** Request code for checking if a valid version of Play Services exists. */ - public static final int PLAY_SERVICES_CHECK = 111; + public static final int PLAY_SERVICES_CHECK = 112; private RequestCodes() { throw new AssertionError("No instance for you!"); diff --git a/auth/src/main/res/drawable/fui_ic_github_white_24dp.xml b/auth/src/main/res/drawable/fui_ic_github_white_24dp.xml new file mode 100644 index 000000000..fe64ff712 --- /dev/null +++ b/auth/src/main/res/drawable/fui_ic_github_white_24dp.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/auth/src/main/res/drawable/fui_idp_button_background_github.xml b/auth/src/main/res/drawable/fui_idp_button_background_github.xml new file mode 100644 index 000000000..6d973cb0f --- /dev/null +++ b/auth/src/main/res/drawable/fui_idp_button_background_github.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/auth/src/main/res/layout/fui_idp_button_github.xml b/auth/src/main/res/layout/fui_idp_button_github.xml new file mode 100644 index 000000000..d3ebcf734 --- /dev/null +++ b/auth/src/main/res/layout/fui_idp_button_github.xml @@ -0,0 +1,7 @@ +