fbpx
How to add GitHub Login button to your Android app using Kotlin

How to add GitHub Login button to your Android app using Kotlin

UPDATE [March 4rd, 2020]: AsyncTasks replaced with Coroutines

Today, I’ll show you how to implement the ‘Log in with GitHub’ button into your Android app.

We’re going to use 3-legged Authorization(OAuth) to get the authorization code and exchange it later for the access token. Using the access token, we’ll be able to get user’s information and email address making an HTTP request to https://api.github.com/user

Let’s get started!

First, we need to create the Client ID and Client Secret on the GitHub website.

Go to https://github.com/settings/developers and press the Register a new application button.

Fill the required fields and press Register application.

On the new page, you’ll see the Client ID and Client Secret we going to use later.

Adding the dependencies

In this tutorial, we’re going to use Coroutines, which will help us do HTTP POST requests easier.

In the build.gradle file of your app add the following dependency:

dependencies {
    // ...
   
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5'
    
    // ...
}

Creating the GitHub Login Button

First, add a Button to your XML file. In this example, we have a button in the middle of the activity_main.xml.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorPrimary"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/github_login_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="@drawable/rounded_corners"
        android:drawablePadding="8dp"
        android:drawableStart="@drawable/github_icon"
        android:drawableLeft="@drawable/github_icon"
        android:padding="8dp"
        android:text="Log in with GitHub"
        android:textAllCaps="false"
        android:textColor="@android:color/white" />

</RelativeLayout>

Create a new Kotlin object file by right-clicking on your app’s package name on the left side and go New > Kotlin File/Class.

Give it the name GithubConstants, choose Object and press OK.

Inside the new file, copy and paste the following code and replace the CLIENT_ID, CLIENT_SECRET and REDIRECT_URI with yours.

object GithubConstants {

    val CLIENT_ID = "MY_CLIENT_ID"
    val CLIENT_SECRET = "MY_CLIENT_SECRET"
    val REDIRECT_URI = "MY_REDIRECT_URI"
    val SCOPE = "read:user,user:email"
    val AUTHURL = "https://github.com/login/oauth/authorize"
    val TOKENURL = "https://github.com/login/oauth/access_token"

}

In your AndroidManifest.xml file, add internet permission.

<uses-permission android:name="android.permission.INTERNET" />

In the Kotlin class of your Activity (In this example is the MainActivity), add a listener to your button, and create a Dialog with a WebView inside.

lateinit var githubAuthURLFull: String
    lateinit var githubdialog: Dialog


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val state = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())

        githubAuthURLFull =
            GithubConstants.AUTHURL + "?client_id=" + GithubConstants.CLIENT_ID + "&scope=" + GithubConstants.SCOPE + "&redirect_uri=" + GithubConstants.REDIRECT_URI + "&state=" + state

        github_login_btn.setOnClickListener {
            setupGithubWebviewDialog(githubAuthURLFull)
        }

    }

    // Show Github login page in a dialog
    @SuppressLint("SetJavaScriptEnabled")
    fun setupGithubWebviewDialog(url: String) {
        githubdialog = Dialog(this)
        val webView = WebView(this)
        webView.isVerticalScrollBarEnabled = false
        webView.isHorizontalScrollBarEnabled = false
        webView.webViewClient = GithubWebViewClient()
        webView.settings.javaScriptEnabled = true
        webView.loadUrl(url)
        githubdialog.setContentView(webView)
        githubdialog.show()
    }

This WebView uses a custom WebViewClient, named GithubWebViewClient.

This helps to ‘catch’ the URL with the authorization code after the user has given access to your app.

// A client to know about WebView navigations
    // For API 21 and above
    @Suppress("OverridingDeprecatedMember")
    inner class GithubWebViewClient : WebViewClient() {
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        override fun shouldOverrideUrlLoading(
            view: WebView?,
            request: WebResourceRequest?
        ): Boolean {
            if (request!!.url.toString().startsWith(GithubConstants.REDIRECT_URI)) {
                handleUrl(request.url.toString())

                // Close the dialog after getting the authorization code
                if (request.url.toString().contains("code=")) {
                    githubdialog.dismiss()
                }
                return true
            }
            return false
        }

        // For API 19 and below
        override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
            if (url.startsWith(GithubConstants.REDIRECT_URI)) {
                handleUrl(url)

                // Close the dialog after getting the authorization code
                if (url.contains("?code=")) {
                    githubdialog.dismiss()
                }
                return true
            }
            return false
        }

        // Check webview url for access token code or error
        private fun handleUrl(url: String) {
            val uri = Uri.parse(url)
            if (url.contains("code")) {
                val githubCode = uri.getQueryParameter("code") ?: ""
                requestForAccessToken(githubCode)
            }
        }
    }

After getting the authorization code, you need to exchange it for an access token by making an HTTP POST request to https://github.com/login/oauth/access_token

fun requestForAccessToken(code: String) {
        val grantType = "authorization_code"

        val postParams =
            "grant_type=" + grantType + "&code=" + code + "&redirect_uri=" + GithubConstants.REDIRECT_URI + "&client_id=" + GithubConstants.CLIENT_ID + "&client_secret=" + GithubConstants.CLIENT_SECRET
        GlobalScope.launch(Dispatchers.Default) {
            val url = URL(GithubConstants.TOKENURL)
            val httpsURLConnection =
                withContext(Dispatchers.IO) { url.openConnection() as HttpsURLConnection }
            httpsURLConnection.requestMethod = "POST"
            httpsURLConnection.setRequestProperty(
                "Accept",
                "application/json"
            );
            httpsURLConnection.doInput = true
            httpsURLConnection.doOutput = true
            val outputStreamWriter = OutputStreamWriter(httpsURLConnection.outputStream)
            withContext(Dispatchers.IO) {
                outputStreamWriter.write(postParams)
                outputStreamWriter.flush()
            }
            val response = httpsURLConnection.inputStream.bufferedReader()
                .use { it.readText() }  // defaults to UTF-8
            withContext(Dispatchers.Main) {
                val jsonObject = JSONTokener(response).nextValue() as JSONObject

                val accessToken = jsonObject.getString("access_token") //The access token

                // Get user's id, first name, last name, profile pic url
                fetchGithubUserProfile(accessToken)
            }
        }
    }

After having the access token, you can get user’s Id, Display Name, Profile pic URL and email address with an HTTP GET request.

fun fetchGithubUserProfile(token: String) {
        GlobalScope.launch(Dispatchers.Default) {
            val tokenURLFull =
                "https://api.github.com/user"

            val url = URL(tokenURLFull)
            val httpsURLConnection =
                withContext(Dispatchers.IO) { url.openConnection() as HttpsURLConnection }
            httpsURLConnection.requestMethod = "GET"
            httpsURLConnection.setRequestProperty("Authorization", "Bearer $token")
            httpsURLConnection.doInput = true
            httpsURLConnection.doOutput = false
            val response = httpsURLConnection.inputStream.bufferedReader()
                .use { it.readText() }  // defaults to UTF-8
            val jsonObject = JSONTokener(response).nextValue() as JSONObject
            Log.i("GitHub Access Token: ", token)

            // GitHub Id
            val githubId = jsonObject.getInt("id")
            Log.i("GitHub Id: ", githubId.toString())

            // GitHub Display Name
            val githubDisplayName = jsonObject.getString("login")
            Log.i("GitHub Display Name: ", githubDisplayName)

            // GitHub Email
            val githubEmail = jsonObject.getString("email")
            Log.i("GitHub Email: ", githubEmail)

            // GitHub Profile Avatar URL
            val githubAvatarURL = jsonObject.getString("avatar_url")
            Log.i("Github Profile Avatar URL: ", githubAvatarURL)

        }
    }

Done!

You can find the final project here

If you have any questionsplease feel free to leave a comment below

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments