如何在Android Firebase UI中实现GitHub登录授权
Great question! Let's break down exactly how to add a GitHub login button to your Android app that integrates with Firebase—you've already checked off the big prerequisites (GitHub app created, Firebase Console configured with Client ID/Secret), so we'll focus on the UI, click handling, and auth flow.
1. Add the GitHub Login Button to Your XML Layout
First, add a polished GitHub login button to your login screen layout. Use a MaterialButton with GitHub's branding for consistency:
<!-- Inside your login layout file --> <com.google.android.material.button.MaterialButton android:id="@+id/btn_github_login" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Sign in with GitHub" android:drawableStart="@drawable/ic_github" <!-- Add GitHub's vector icon here --> android:drawablePadding="8dp" android:layout_marginHorizontal="16dp" android:layout_marginTop="8dp" android:backgroundTint="#24292F" <!-- GitHub's dark gray brand color --> android:textColor="@android:color/white"/>
You can grab GitHub's official vector icon from Material Icons or GitHub's branding resources if you don't already have it.
2. Handle Button Click & Launch GitHub Authorization Flow
When the user taps the button, we'll redirect them to GitHub's OAuth 2.0 authorization page using Chrome Custom Tabs (for a smoother in-app experience vs. opening a separate browser).
First, add the Custom Tabs dependency to your app-level build.gradle:
implementation("androidx.browser:browser:1.6.0")
Then, set up the button click listener in your LoginActivity/Fragment:
class LoginActivity : AppCompatActivity() { private val GITHUB_CLIENT_ID = "your_github_client_id_here" private val GITHUB_REDIRECT_URI = "your_app_scheme://github-auth" // Match what you set in GitHub app settings private val AUTH_PREFS = "AuthPrefs" private val KEY_GITHUB_STATE = "github_state" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_login) val btnGithubLogin = findViewById<MaterialButton>(R.id.btn_github_login) btnGithubLogin.setOnClickListener { // Generate a random state string to prevent CSRF attacks val authState = UUID.randomUUID().toString() // Save the state to validate later getSharedPreferences(AUTH_PREFS, MODE_PRIVATE) .edit() .putString(KEY_GITHUB_STATE, authState) .apply() // Build GitHub's authorization URL val githubAuthUrl = Uri.parse("https://github.com/login/oauth/authorize") .buildUpon() .appendQueryParameter("client_id", GITHUB_CLIENT_ID) .appendQueryParameter("redirect_uri", GITHUB_REDIRECT_URI) .appendQueryParameter("scope", "user:email") // Request user email (optional but useful) .appendQueryParameter("state", authState) .build() // Launch the authorization page with Custom Tabs val customTabsIntent = CustomTabsIntent.Builder().build() customTabsIntent.launchUrl(this, githubAuthUrl) } } }
3. Set Up a Callback Activity to Handle GitHub's Response
GitHub will redirect back to your app using the redirect_uri you specified. Create a new activity (GithubAuthCallbackActivity) to capture this response, then register it in your AndroidManifest.xml:
Manifest Entry
<activity android:name=".GithubAuthCallbackActivity" android:exported="true" android:noHistory="true"> <!-- Prevents this activity from staying in the back stack --> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <!-- Match the scheme from your redirect URI --> <data android:scheme="your_app_scheme" /> </intent-filter> </activity>
Callback Activity Code
This activity will validate the state, exchange the authorization code for a GitHub access token, then use that token to sign in to Firebase. First, add OkHttp (for network requests) to your build.gradle:
implementation("com.squareup.okhttp3:okhttp:4.11.0")
Then, the activity code:
class GithubAuthCallbackActivity : AppCompatActivity() { private val GITHUB_CLIENT_ID = "your_github_client_id_here" private val GITHUB_CLIENT_SECRET = "your_github_client_secret_here" private val GITHUB_REDIRECT_URI = "your_app_scheme://github-auth" private val AUTH_PREFS = "AuthPrefs" private val KEY_GITHUB_STATE = "github_state" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_github_auth_callback) // Can be a blank layout intent.data?.let { uri -> val authCode = uri.getQueryParameter("code") val returnedState = uri.getQueryParameter("state") val savedState = getSharedPreferences(AUTH_PREFS, MODE_PRIVATE).getString(KEY_GITHUB_STATE, null) // Validate state to prevent CSRF attacks if (returnedState == savedState && authCode != null) { exchangeCodeForAccessToken(authCode) } else { Toast.makeText(this, "Authentication failed: Invalid state", Toast.LENGTH_SHORT).show() finish() } } ?: run { Toast.makeText(this, "No authentication data received", Toast.LENGTH_SHORT).show() finish() } } private fun exchangeCodeForAccessToken(code: String) { val okHttpClient = OkHttpClient() val requestBody = FormBody.Builder() .add("client_id", GITHUB_CLIENT_ID) .add("client_secret", GITHUB_CLIENT_SECRET) .add("code", code) .add("redirect_uri", GITHUB_REDIRECT_URI) .build() val request = Request.Builder() .url("https://github.com/login/oauth/access_token") .post(requestBody) .header("Accept", "application/json") // Get JSON response for easier parsing .build() okHttpClient.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { runOnUiThread { Toast.makeText(this@GithubAuthCallbackActivity, "Failed to connect to GitHub", Toast.LENGTH_SHORT).show() finish() } } override fun onResponse(call: Call, response: Response) { response.body?.string()?.let { responseString -> try { val jsonObject = JSONObject(responseString) val accessToken = jsonObject.getString("access_token") signInToFirebase(accessToken) } catch (e: JSONException) { runOnUiThread { Toast.makeText(this@GithubAuthCallbackActivity, "Invalid response from GitHub", Toast.LENGTH_SHORT).show() finish() } } } ?: run { runOnUiThread { Toast.makeText(this@GithubAuthCallbackActivity, "Empty response from GitHub", Toast.LENGTH_SHORT).show() finish() } } } }) } private fun signInToFirebase(accessToken: String) { val credential = GithubAuthProvider.getCredential(accessToken) FirebaseAuth.getInstance().signInWithCredential(credential) .addOnCompleteListener(this) { task -> if (task.isSuccessful) { // Login successful! Redirect to main activity val intent = Intent(this, MainActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK startActivity(intent) } else { runOnUiThread { Toast.makeText(this, "Firebase login failed: ${task.exception?.message}", Toast.LENGTH_SHORT).show() } } finish() } } }
4. Key Notes to Avoid Issues
- Redirect URI Match: Ensure the
redirect_uriin your code exactly matches what you set in your GitHub app's "Authorization callback URL" field. - State Validation: Never skip validating the
stateparameter—it's critical for preventing CSRF attacks. - Dependencies: Use the latest versions of Firebase Auth, OkHttp, and Custom Tabs in your
build.gradle. - Internet Permission: Add
<uses-permission android:name="android.permission.INTERNET" />to yourAndroidManifest.xml(required for network requests).
内容的提问来源于stack exchange,提问作者Jean-Pascal MEWENEMESSE

