Implementing Firebase GitHub Authentication in Flutter

Image credit

In this article, I’m going to show you how to implement Firebase GitHub authentication method properly in Flutter. As you might already know, there are some Flutter plugins that are provided officially by Flutter team. And FlutterFire is a subset of them. I encourage you to go and try FlutterFire plugins sometime if you need quick backend solutions for your small-scale project; but for this article we’re going to need only the firebase_auth plugin. So, let me list all the steps that you need to do in order to implement GitHub authentication in your Flutter app:

Step 0. First of all, I assume that you already downloaded google-services.json file for Android and GoogleServices-Info.plist file for iOS from Firebase dashboard of your app and added them to your project properly.

Step 1. Create an OAuth app on GitHub via this link: https://github.com/settings/applications/new. As “authorization callback URL”, you need to set something like appname://auth. You will be redirected to this URL once you successfully log into GitHub on browser (which is the first step of this authorization method from the user point of view). We’re going to utilize this URL in later steps to set app’s deep link configuration, so that you can be redirected back to the app.

Step 2. Enable GitHub authentication method on your app’s Firebase dashboard. As “client ID” and “client secret”, you need to set what GitHub has provided after creating OAuth app on GitHub in Step 1.

Step 3. Add the following dependencies to your project’s pubspec.yaml file:

dependencies:
firebase_auth: ^0.8.2
google_sign_in: ^4.0.1+1
url_launcher: ^5.0.2
uni_links: ^0.2.0
http: ^0.12.0+1
  • google_sign_in is required by the firebase_auth dependency, so we need to add it as well.
  • url_launcher will be used to launch URL on browser.
  • uni_links is to add deep-linking capability to the app.
  • http will be used to send HTTP request.

Why and where we need all these will be much more meaningful in the upcoming steps, so please bear with me.

Step 4. For Android, add the following classpath to project-level build.gradle file located under /android directory:

dependencies {
// ...
classpath 'com.google.gms:google-services:4.2.0'
}

And add the following line to the end of app-level build.gradle file located under /android/app directory:

apply plugin: 'com.google.gms.google-services'

Finally, add the following intent filter to the AndroidManifest.xml file located under /android/app/src/main directory:

<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:host="auth"
android:scheme="appname" />
</intent-filter>

Please be aware that scheme and host properties we set here are from Step 1.

Step 5. For iOS, add the following attributes into Info.plist file located under /ios/Runner directory:

<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string><!-- REVERSED_CLIENT_ID from GoogleServices-Info.plist --></string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>auth</string>
<key>CFBundleURLSchemes</key>
<array>
<string>foreach</string>
</array>
</dict>
</array>

Once again, please be aware that in the first “dict” element of the above array, value of CFBundleURLSchemes attribute is set as the REVERSED_CLIENT_ID which you can find in GoogleServices-Info.plist file. Similarly, values of CFBundleURLName and CFBundleURLSchemes attributes in the second “dict” element are from Step 1.


We’ve completed the configuration steps. Now, we are ready to go with coding…

Step 6. Import url_launcher package and launch the GitHub login page on browser as follows:

import 'package:url_launcher/url_launcher.dart';
//...
void onClickGitHubLoginButton() async {
const String url = "https://github.com/login/oauth/authorize" +
"?client_id=" + GITHUB_CLIENT_ID +
"&scope=public_repo%20read:user%20user:email";

if (await canLaunch(url)) {
await launch(
url,
forceSafariVC: false,
forceWebView: false,
);
} else {
print("CANNOT LAUNCH THIS URL!");
}
}

On this page, you need to log in with your GitHub credentials.

Step 7. After successfully logging into GitHub, you will get redirected to the URL appname://auth?code=AUTH_CODE. This redirection will be caught by your app as we’ve set the deep link configuration in Step 4 & 5, and you will be redirected (or asked to be redirected) back to the app afterwards.

At this point, to be able to handle incoming data, or in other words, to parse the URL given above and get the AUTH_CODE value from it, you need to have the following setup in your login page widget code:

import 'package:flutter/material.dart';
import 'dart:async';
import 'package:uni_links/uni_links.dart';

class LoginPage extends StatefulWidget {
@override
_LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<SplashPage> {
StreamSubscription _subs;

// ...

@override
void initState() {
_initDeepLinkListener()
super.initState();
}

@override
void dispose() {
_disposeDeepLinkListener();
super.dispose();
}

// ...

void _initDeepLinkListener() async {
_subs = getLinksStream().listen((String link) {
_checkDeepLink(link);
}, cancelOnError: true);
}

void _checkDeepLink(String link) {
if (link != null) {
String code = link.substring(link.indexOf(RegExp('code=')) + 5);
loginWithGitHub(code)
.then((firebaseUser) {
print("LOGGED IN AS: " + firebaseUser.displayName);
}).catchError((e) {
print("LOGIN ERROR: " + e.toString());
});
}
}

void _disposeDeepLinkListener() {
if (_subs != null) {
_subs.cancel();
_subs = null;
}
}
}

In this code, basically we are listening for deep links incoming to the app.

As stated, after a successful GitHub login, you have the “code” in your hands. Now, you need to use it to get access token from GitHub, which afterwards will be used to log into Firebase. Remember that this is our eventual goal.

Step 8. Implement the loginWithGitHub(…) method highlighted in the code above as follows:

import 'package:firebase_auth/firebase_auth.dart';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
// ...
Future<FirebaseUser> loginWithGitHub(String code) async {
//ACCESS TOKEN REQUEST
final response = await http.post(
"https://github.com/login/oauth/access_token",
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
},
body: jsonEncode(GitHubLoginRequest(
clientId: GITHUB_CLIENT_ID,
clientSecret: GITHUB_CLIENT_SECRET,
code: code,
)),
);

GitHubLoginResponse loginResponse =
GitHubLoginResponse.fromJson(json.decode(response.body));

//FIREBASE STUFF
final AuthCredential credential = GithubAuthProvider.getCredential(
token: loginResponse.accessToken,
);

final FirebaseUser user = await FirebaseAuth.instance.signInWithCredential(credential);
return user;
}
// ...
//GITHUB REQUEST-RESPONSE MODELS
class GitHubLoginRequest {
String clientId;
String clientSecret;
String code;

GitHubLoginRequest({this.clientId, this.clientSecret, this.code});

dynamic toJson() => {
"client_id": clientId,
"client_secret": clientSecret,
"code": code,
};
}

class GitHubLoginResponse {
String accessToken;
String tokenType;
String scope;

GitHubLoginResponse({this.accessToken, this.tokenType, this.scope});

factory GitHubLoginResponse.fromJson(Map<String, dynamic> json) =>
GitHubLoginResponse(
accessToken: json["access_token"],
tokenType: json["token_type"],
scope: json["scope"],
);
}

So, what we’re doing here is that first we request for an access token by sending an HTTP POST call (to the address shown in the code) whose request body contains the “code” we’ve got in the previous step, together with app’s GitHub credentials (“client ID” and “client secret”). Then, using the access token we get in the response of this call, we sign into Firebase. If everything goes successfully, you should eventually have FirebaseUser object in your hands.


Well done! You’re now logged into Firebase with your GitHub account.

I wanted to share how I implemented Firebase GitHub authentication in Flutter since I couldn’t find any resource on the net while trying to achieve this for my project. I hope that this article helps you and makes things clearer a bit.

Stay safe and with Flutter…