Build an Android Application with Authentication

With mobile apps becoming ever-present in users' lives, following best security practices has become essential in protecting your users and your apps. Implementing security alone and from the ground up can be costly for your development team, create a less-than-ideal user experience, and be susceptible to design/implementation errors. The easiest path to strong security, and a positive user experience, is to turn to the experts.

The Okta OIDC SDK can help with this in many ways: - Our security experts take the development effort of creating a secure authentication method off your plate - OAuth 2.0 and OpenID Connect allows your users to easily authenticate on your app with social login, using industry-standard secure protocols

This post will show you a simple example of how you can use Okta OIDC SDK to create a (Kotlin) Android app with authentication.

(TLDR) Download the Finished App with Android Authentication

If you want to follow along using a completed version of the app, clone this repository from GitHub using the simplified branch:

git clone https://github.com/oktadeveloper/okta-oidc-android-example.git -b simplified
cd okta-oidc-android-example
The finished app in the master branch from the repository includes everything described in this blog, along with some additional UI and functionality, testing Okta OIDC easier, and providing you with a production-level skeleton to work with when creating your own app.

Create Your First Android App

First, you’ll need to download and install Android Studio. Next, launch the app and navigate to FileNew…​New Project…​. Then, create an "Empty Activity" for "Phone and Tablet". You should now see a screen similar to this:

Create new Android Project

Choose a "Package name" related to a domain you own (I own akaita.com, therefore I used com.akaita.myapplication in the example), a Save location you can remember, "Kotlin", and a "Minimum API level" of 23. Finally, click Finish.

Get Your Okta OIDC Configuration Settings

Before adding anything else to your app, you’ll need to quickly obtain the configuration parameters required to configure Okta OIDC authentication:

  1. Log in to your Okta Developer account or sign up for a free developer account

  2. Once you’ve logged on, you’ll see the Admin Dashboard. Take note of your "Org URL" (at the top right of the screen). In my case, I see: https://dev-123456.okta.com

  3. Navigate to ApplicationsAdd Application

  4. Choose Native as the platform

  5. Add a Login redirect URIs similar to com.okta.dev-123456:/callback (using your own dev-XXXX)

  6. Add a Logout redirect URIs similar to com.okta.dev-123456:/logout (using your own dev-XXXX)

  7. Click on Done

Create a new Okta application

Next, take note of the Client ID that’s found in the General tab of your new Okta application. It’s a long string of around 20 characters.

Add a Sign-in Button to Your Android App

Now that you’ve finished up in Okta’s Admin Panel, head back to Android Studio and add a button to your res/layout/activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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:fitsSystemWindows="true"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="vertical">

    <Button
        android:id="@+id/signIn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Sign in" />

    </LinearLayout>

</ScrollView>

This button will be used to authenticate using OAuth 2.0 + OpenID Connect, thanks to the Okta OIDC SDK.

Give Your Android App Permission to Use the Internet

In your app/src/main/AndroidManifest.xml add the following XML between the <manifest></manifest> tags:

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

Add Okta OIDC SDK to Your Android App

  1. Add a manifestPlaceholder for appAuthRedirectScheme in app/build.gradle. Make sure it is consistent with your Redirect URIs. For instance, my redirect URIs look like com.okta.dev-123456:/callback, therefore my appAuthRedirectScheme is com.okta.dev-123456

  2. Add the required Okta dependencies in app/build.gradle, and declare a Java 1.8 target in the compileOptions clause (this is required by the OIDC library)

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29

    defaultConfig {
        applicationId "com.akaita.myapplication"  (1)
        minSdkVersion 23
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        manifestPlaceholders = [
            "appAuthRedirectScheme": "com.okta.dev-123456" (2)
        ]

        compileOptions {  (3)
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

    // Dependencies required for Okta OIDC
    implementation 'com.okta.authn.sdk:okta-authn-sdk-api:1.0.0'  (4)
    implementation('com.okta.authn.sdk:okta-authn-sdk-impl:1.0.0') {
        exclude group: 'com.okta.sdk', module: 'okta-sdk-httpclient'
    }
    implementation 'com.okta.android:oidc-androidx:1.0.11'

    // Dependency required for Biomatric-Authentication (which we will detail how to implement later on in this same article)
    implementation 'androidx.biometric:biometric:1.0.1'

    testImplementation 'junit:junit:4.13'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
1 Keep you applicationId here.
2 The redirect URI for the application you created in your Okta Developer Console.
3 Okta OIDC libraries require Java 1.8 compatibility.
4 Add the dependencies required for the Okta OIDC library.

Configure Okta OIDC in Your Android App

In your MainActivity class, add a couple of class properties and a couple of new methods, calling them from onCreate():

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.okta.oidc.*
import com.okta.oidc.clients.sessions.SessionClient
import com.okta.oidc.clients.web.WebAuthClient
import com.okta.oidc.net.response.UserInfo
import com.okta.oidc.storage.security.DefaultEncryptionManager
import com.okta.oidc.storage.security.EncryptionManager
import com.okta.oidc.storage.security.GuardedEncryptionManager
import com.okta.oidc.util.AuthorizationException
import kotlinx.android.synthetic.main.activity_main.*

/**
* Authorization client using chrome custom tab as a user agent.
*/
private lateinit var webAuth: WebAuthClient (1)

/**
* The authorized client to interact with Okta's endpoints.
*/
private lateinit var sessionClient: SessionClient (2)

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

    setupWebAuth()
    setupWebAuthCallback(webAuth)
}

private fun setupWebAuth() {
    val oidcConfig = OIDCConfig.Builder()
        .clientId("20-character-long Client ID")
        .redirectUri("com.okta.dev-123456:/callback")
        .endSessionRedirectUri("com.okta.dev-123456:/logout")
        .scopes("openid", "profile", "offline_access")
        .discoveryUri("https://dev-123456.okta.com")
        .create()

    webAuth = WebAuthBuilder()
        .withConfig(oidcConfig)
        .withContext(applicationContext)
        .withCallbackExecutor(null)
        .withEncryptionManager(DefaultEncryptionManager(this))
        .setRequireHardwareBackedKeyStore(true) (3)
        .create()
    sessionClient = webAuth.sessionClient
}

private fun setupWebAuthCallback(webAuth: WebAuthClient) { (4)
    val callback: ResultCallback<AuthorizationStatus, AuthorizationException> =
        object : ResultCallback<AuthorizationStatus, AuthorizationException> {
            override fun onSuccess(status: AuthorizationStatus) {
                if (status == AuthorizationStatus.AUTHORIZED) {
                    Log.d("MainActivity", "AUTHORIZED")
                    Toast.makeText(this@MainActivity, "Authorized", Toast.LENGTH_SHORT).show()
                } else if (status == AuthorizationStatus.SIGNED_OUT) {
                    Log.d("MainActivity", "SIGNED_OUT")
                    Toast.makeText(this@MainActivity, "Signed out", Toast.LENGTH_SHORT).show()
                }
            }

            override fun onCancel() {
                Log.d("MainActivity", "CANCELED")
                Toast.makeText(this@MainActivity, "Cancelled", Toast.LENGTH_SHORT).show()
            }

            override fun onError(msg: String?, error: AuthorizationException?) {
                Log.d("MainActivity", "${error?.error} onError", error)
                Toast.makeText(this@MainActivity, error?.toJsonString(), Toast.LENGTH_SHORT).show()
            }
        }
    webAuth.registerCallback(callback, this)
}
1 private lateinit var webAuth: WebAuthClient is a reference to the web client you will invoke to log in
2 private lateinit var sessionClient: SessionClient is a reference to the session you can use to conduct multiple operations after logging in, such as getting the user’s profile, revoking the authentication token, refreshing the authentication token, etc…​
3 setRequireHardwareBackedKeyStore(true) forces the app to require a device with encryption capabilities. This is the default configuration for Okta OIDC and it’s considered the best practice. If you want to run this code in a emulator, though, you can temporarily set it to false.
4 private fun setupWebAuthCallback() is the place where you can define the action to take when authentication succeeds, fails or is canceled…​
You can create a utility method to automatically detect if your application is running in an emulator.

Make sure to use the values you wrote down during the Get your Okta OIDC configuration settings step to create val oidcConfig in private fun setupWebAuth().

Now that you have a minimal configuration of Okta OIDC, it’s time to add a listener for the button we added previously.

Log in Using Okta OIDC

Create a payload using AuthenticationPayload and call webAuth.signIn() in MainActivity.kt.

import kotlinx.android.synthetic.main.main_activity.*

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

    setupWebAuth()
    setupWebAuthCallback(webAuth)

    signIn.setOnClickListener {
        val payload = AuthenticationPayload.Builder()
            .build()
        webAuth.signIn(this, payload)
    }
}

This will instruct Okta OIDC SDK to launch a web browser in which your users can authenticate using their Okta credentials.

Once they successfully authenticate, you will be able to use sessionClient to do things like check their authentication status:

if (sessionClient.isAuthenticated) {
    // Do something specific to authenticated users
} else {
    // Do something to non-authenticated users
}

You can even download their profile:

private fun downloadProfile() {
    sessionClient.getUserProfile(object : RequestCallback<UserInfo, AuthorizationException> {
        override fun onSuccess(result: UserInfo) {
            Log.d("Profile", result.toString())
        }

        override fun onError(error: String?, exception: AuthorizationException?) {
            Log.d("Profile", error, exception.cause)
        }
    })
}

That’s it! You now have an Android app with robust OIDC authentication!

When you launch your app and click on the SIGN IN button, you will be greeted with Okta’s authentication portal, which you can use to authenticate users into your app:

Android browser login prompt

(Optional) Add Social login

It should be easy for users to authenticate into your app. Okta OIDC SDK helps you accomplish this by allowing users to use their social accounts to validate their identity..

With Okta, you can add an external Identity Provider—such as Google, Facebook, LinkedIn or Microsoft. To achieve this, connect to the external identity providers and ask them to trust Okta for your application. This is done in three simple steps:

Lastly, use the identity providers in your app:

signIn.setOnClickListener {
    val payload = AuthenticationPayload.Builder()
        .setIdp("{IdP-id}") // From your "Okta" admin console
        .setIdpScope("clientScope1", "clientScope2", "clientScope3") // Optional, in case the IDP requires it
        .build()
    webAuth.signIn(this, payload)
}

Now, when a user clicks your SIGN IN button, they’ll be greeted with the login page of the IDP of your choice, which will be used to authenticate users into your app.

(Optional) Biometric Login

Additionally, biometrics can be used to access sessions created by Okta OIDC. The BiometricPrompt recently published by the Android team makes it a very feasible option, taking a lot of the complexity off your hands and offering a unified familiar & native experience for users on all variants of Android (Google devices, Samsung devices, …​).

BiometricPrompt uses the available resources in each device to offer whichever options are possible. Those include Iris authentication, fingerprint authentication, PIN authentication, Pattern authentication, etc.

In essence, there are two components to be taken into account:

  1. BiometricPrompt can be used to only allow biometrically authenticated users into your app, or into specific sections of your app

  2. You can (and should) instruct Okta OIDC SDK to store all data in a biometrically protected encryption system

I created an easy-to-launch Kotlin wrapper of BiometricPrompt for you:

import androidx.biometric.BiometricConstants.ERROR_NEGATIVE_BUTTON
import androidx.biometric.BiometricPrompt
import androidx.biometric.BiometricPrompt.PromptInfo
import androidx.fragment.app.FragmentActivity
import java.util.concurrent.Executors

class Biometric(
    fragmentActivity: FragmentActivity,
    onSuccessListener: () -> Unit,
    onCancelListener: () -> Unit,
    onErrorListener: (Int, String) -> Unit) {

    private val mCallback: BiometricPrompt.AuthenticationCallback =
        object : BiometricPrompt.AuthenticationCallback() {
            override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
                if (errorCode == ERROR_NEGATIVE_BUTTON) {
                    onCancelListener()
                } else {
                    onErrorListener(errorCode, errString.toString())
                }
                prompt.cancelAuthentication()
            }

            override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                onSuccessListener()
            }
         }

    private val prompt: BiometricPrompt
    private val promptInfo: PromptInfo

    init {
        prompt = BiometricPrompt(fragmentActivity, Executors.newSingleThreadExecutor(), mCallback)
        promptInfo = PromptInfo.Builder()
            .setTitle("Biometric authentication succeeded")
            .setDeviceCredentialAllowed(true)
            .setConfirmationRequired(true)
            .build()
    }

    fun show() {
        prompt.authenticate(promptInfo)
    }
}

Simply trigger this so that when a user who is not biometrically authenticated tries to launch MainActivity, they are only allowed through if they biometrically authenticate in their device. Also, remember to use GuardedEncryptionManager to store Okta OIDC data:

import com.okta.oidc.storage.security.DefaultEncryptionManager
import com.okta.oidc.storage.security.EncryptionManager
import com.okta.oidc.storage.security.GuardedEncryptionManager

private var currentEncryptionManager: EncryptionManager? = null
private var keyguardEncryptionManager: GuardedEncryptionManager? = null

override fun onResume() {
    super.onResume()

    // Check whether the user has already authenticated using the device's authentication method. If it hasn't, ask them to do it
    if (currentEncryptionManager?.isUserAuthenticatedOnDevice?.not() == true) {
        showKeyguard()
    }
}

private fun showKeyguard() {
    Biometric(
        fragmentActivity = this,
        onSuccessListener = {
            Log.d("MainActivity", "Biometric authentication succeeded")
            Toast.makeText(this, "Biometric authentication succeeded", Toast.LENGTH_SHORT).show()
        },
        onCancelListener = {
            runOnUiThread {
                Log.d("MainActivity", "Biometric authentication cancelled")
                Toast.makeText(this, "Biometric authentication cancelled", Toast.LENGTH_SHORT).show()
                finish()
            }
        },
        onErrorListener = { code, message ->
            runOnUiThread {
                Log.d("MainActivity", "Biometric authentication failed")
                Toast.makeText(this, "Biometric authentication failed", Toast.LENGTH_SHORT).show()
                finish()
            }
        }).show()
}

private fun setupWebAuth() {
    keyguardEncryptionManager = GuardedEncryptionManager(this, Int.MAX_VALUE)

    webAuth = WebAuthBuilder()
        .withConfig(oidcConfig)
        .withContext(applicationContext)
        .withCallbackExecutor(null)
        .withEncryptionManager(keyguardEncryptionManager)
        .create()
    sessionClient = webAuth.sessionClient
}

Now, whenever a user opens a new instance of your app, they will be able to access it using their biometric information. In the below example, a Google Pixel phone, the authentication is a fingerprint:

Android fingerprint prompt

Learn More About Android Auth and OIDC

It’s now easier than ever to implement OAuth 2.0 and OpenID authorization thanks to Okta OIDC SDK. A high effort, high maintenance chore just became a very straightforward task.

By taking advantage of BiometricPrompt and Kotlin, we can make our apps even safer, while still creating a seamless user experience that feels native to each manufacturer’s UI style.

Although the example we’ve created here does enough to satisfy the needs of the majority of apps, Okta OIDC SDK doesn’t stop there. The Okta OIDC Android repository contains a variety of ideas and suggestions to improve user experience like:

  • Using your own OkHttp client

  • Using a custom UI to log in

  • Having fine-grained control over session tokens' expiration, refresh, etc…​

  • Settings to handle preference of browser client for the authentication process (Chrome, Samsung browser, Firefox, etc…​)

This post has given you the foundations to set up a successful OIDC client. If you want to deepen your knowledge around modern authentication systems, check these additional resources on Android, OAuth 2.0 and OpenID Connect:

If you enjoyed this blog post and want to see more like it, follow @oktadev on Twitter, subscribe to our YouTube channel, or follow us on LinkedIn. As always, please leave your questions and comments below—we love to hear from you!