Kotlin Android App using HAAPI

Kotlin Android App using HAAPI

This tutorial shows how to run a code example that implements mobile OpenID Connect in a Kotlin App using the Hypermedia Authentication API (HAAPI). See also the API Driven Demo Client tutorial for an introductory guide that runs a simple web client.

Overview

The code sample provides a basic Android implementation to demonstrate the approach for handling hypermedia responses, and initially presents a view to start the authentication process:

Android Initial View

The authentication workflow is then managed as a series of fast API requests that return JSON responses. The mobile app processes each API response to perform various actions including rendering of forms:

Android Login Form

Once authentication completes, the app presents its Authenticated View, to simulate a mobile app that works with access tokens to call APIs. The app can then easily perform other OAuth operations such as refreshing tokens and ending the authenticated session:

Android Authenticated View

From this point onwards the app will operate like a traditional mobile OAuth client and call APIs.

Get the Code

First, ensure that you are running an up-to-date version of Android Studio, then clone the GitHub repository and open its folder to view the Kotlin classes used.

Quick Start

The easiest way to run the code example is to point it to a deployed and preconfigured instance of the Curity Identity Server, running in Docker. This is done via a script included with the example that is explained in the Mobile Setup how-to:

./start-idsvr.sh

The result is a working instance of the Curity Identity Server, listening on the Android emulator's default host IP (https://10.0.2.2:8443), ready for the mobile app to connect to. The Admin UI is also available and can be accessed using the following details:

PropertyValue
URLhttps://localhost:6749/admin
Useradmin
PasswordPassword1

The deployed system also includes a user account of demouser / Password1 that can be used to sign in.

Deploying the Curity Identity Server with a Remote URL

It will sometimes be necessary to access the instance of the Curity Identity Server remotely. E.g., when you want to test the mobile app from a physical device, without a debug connection with Android Studio. You can use the ngrok tool to quickly expose local applications on the Internet. You can edit the start-idsvr.sh and stop-idsvr.sh files to use ngrok (set USE_NGROK=true). This will result in exposing the containerized instance of the Curity Identity Server under a random domain similar to https://87ce095409f9.ngrok.io. Should you use this option you will have to update the apps' configuration (see Updating the App Configuration).

If you want more control over exposing the Curity Identity Server with ngrok, then have a look at the Exposing with ngrok tutorial.

Configuration

The code example requires a client with the following settings, and the HAAPI specific settings are explained in the Android HAAPI SDK article. This includes configuration of Client Attestation settings for development so that the app must prove its digital identity before it is allowed to attempt to authenticate end-users. Note that client attestation for emulated devices does not work so validation is usually therefore disabled initially.

<client>
    <id>haapi-android-client</id>
    <client-name>haapi-android-client</client-name>
    <no-authentication>true</no-authentication>
    <redirect-uris>app://haapi</redirect-uris>
    <proof-key>
        <require-proof-key>false</require-proof-key>
    </proof-key>
    <refresh-token-ttl>3600</refresh-token-ttl>
    <scope>openid</scope>
    <scope>profile</scope>
    <user-authentication>
        <allowed-authenticators>Username-Password</allowed-authenticators>
    </user-authentication>
    <capabilities>
        <code>
        </code>
        <haapi/>
    </capabilities>
    <validate-port-on-loopback-interfaces>true</validate-port-on-loopback-interfaces>
    <attestation>
        <disable-attestation-validation>true</disable-attestation-validation>
        <android>
        <package-name>com.example.haapidemo</package-name>
        <signature-digest>LVY9TfP6bKjNxCqyqYC2ayItIUtZk9rBkCTmyc219WY=</signature-digest>
        <android-policy>haapi-android-client</android-policy>
        </android>
    </attestation>
</client>

Updating the App Configuration

In Android Studio, the demo app's configuration settings are specified in the Configuration class. If using the quick start, you can just use the default values.

If you deployed the Curity Identity Server with ngrok, or have your own configuration, then you need to provide the details to the app configuration. This can be achieved in two ways.

  1. Update the newInstance method in the Configuration class to return proper defaults:
fun newInstance(name: String = "haapi-android-client"): Configuration =
    if (CURITY_DEV_MODE) newDevInstance(name) else Configuration(
        name = name,
        clientId = "haapi-android-client",
        baseURLString = "https://idsvr.example.com",
        tokenEndpointURI = "https://idsvr.example.com/token",
        authorizationEndpointURI = "https://idsvr.example.com/authorize",
        userInfoEndpointURI = "https://idsvr.example.com/userinfo",
        metaDataBaseURLString = "https://idsvr.example.com/issuer",
        redirectURI = "app://haapi",
        followRedirect = true,
        isSSLTrustVerificationEnabled = false,
        selectedScopes = listOf("openid", "profile")
    )
  1. In the app itself, you can use the Profile editor to change current settings and create profiles to quickly jump between different environments. To open the settings editor, click on the settings icon while on the app's home page. You can edit an existing profile, or create a new one, then set it as active. As a convenience, you can provide the issuer URI and tap "Fetch the latest configuration". The app will query the server's metadata endpoint to fetch relevant configuration.

Run the App

Once the above configuration is complete you are ready to run the app and use whichever authenticators and users you have configured in the Curity Identity Server. In the following sections we will explain the key technical behavior implemented in the code example.

HAAPI Integration

Integrating the Hypermedia Authentication API is done in three layers summarized below. Curity will be providing libraries for the view layer in the near future so that integration requires very little code:

LayerImplemented ByDescription
DriverCurity LibraryProcessing of low-level messages used for client attestation to prove the app's identity.
ControllerCurity LibraryProcessing HAAPI responses, including form submitting, following redirects, and polling the server.
ViewYour AppOverriding the defaults and customizing presentation to take full control over the login user experience.

Client Attestation Overview

When authentication is first triggered, the app sends a number of low level messages to prove it is the genuine app, based on the digital identity with which the app is signed and delivered to the Play Store.

This must be done before the app is allowed to attempt to authenticate the user. The main request to prove the client's identity is a client assertion request with the following parameters:

Form ParameterExample Value
client_idhaapi-public-client
scopeurn:se:curity:scopes:haapi
grant_typeclient_credentials
client_assertion_typeurn:se:curity:attestation:client
client_assertioneyJraWQi...paTubjbwReZ0A

The server verifies these details and returns a JWT access token in a JSON response that must be sent with subsequent requests in the authentication workflow, along with a separate proof of possession token. See the HAAPI Whitepaper for further details on security.

Response FieldExample Value
access_tokeneyJraWQi...JUHpZbxbstkK41RJhA7vw
scopeurn:se:curity:scopes:haapi
token_typeDPoP

HAAPI Messages

Once the client is attested it can start authenticating the user, which is done using standard OpenID Connect parameters, but with a custom accept header to indicate that the client wants to receive hypermedia responses. The HAAPI models Android SDK exposes a HaapiManager class which is used to communicate with the Curity Identity Server. The manager exposes these relevant methods:

  • start
  • submit
  • followLink

Each returns a HaapiResponse model object, which is a Kotlin sealed interface, and which might represent:

  • Forms to be rendered
  • Redirects to be followed
  • Instructions to open the system browser or external app
  • Polling responses
  • Error Responses

Response models can be processed in a generic way within the mobile app to deal with finer-grained work such as rendering the set of fields within a form model. Have a look at the io.curity.haapidemo.ui.haapiflow package of the sample app to check how such rendering could be implemented.

Eventually, the manager will return an OAuthAuthorizationResponseStep, which signals that the authentication has finished, and the app can now obtain access, refresh, and ID tokens from the Curity Identity Server. This can be easily done using the OAuthTokenManager object. The token manager exposes two methods: fetchAccessToken and refreshAccessToken.

HAAPI Extensible Authentication

HAAPI supports many authentication methods in addition to passwords, so you are encouraged to try additional authentication options such as these:

Authentication FlowCharacteristics
Google Sign InWith this type of personal password, HAAPI will open the system browser to sign the user in
CIBA FlowWith this authentication flow, HAAPI polls while a client waits for a remote user to sign in
WebAuthnThis authenticator demonstrates passwordless and user friendly logins

For further detail about the requests and responses used by the Hypermedia Authentication API, see the HAAPI Data Model, which provides full details on schemas for all areas, including authentication, consent, polling and error responses.

Using and Refreshing Access Tokens

Once the client obtains an access token it will use it to call APIs in the standard way, by sending them as bearer tokens in the HTTP Authorization header. When the access token expires, the refreshAccessToken method of the OAuth token manager is used to get a new access token.

Logout

Logout can be implemented in a simple manner, by just removing tokens from the app and redirecting back to the Unauthenticated View. Subsequent sign in behavior can then be controlled via the following OpenID Connect fields. This can also be useful when testing, in order to sign in as multiple users on the same device:

OpenID Connect Request ParameterUsage
promptSet prompt=login to force the user to re-authenticate immediately
max-ageSet max-age=N to specify the maximum elapsed time in seconds before which the user must re-authenticate

Non-Compliant Devices

Some older Android devices may not support the recommended method for client attestation. This involves using hardware support for reading public certificate details for the Google issued key used to sign the production app. In these cases you can use a fallback method of proving the client identity before authentication. For further details see the following links:

Conclusion

The Hypermedia Authentication API provides a number of benefits to mobile apps, in terms of both financial grade security and login usability:

  • The app gets an improved login user experience
  • The Identity Server continues to dictate security
  • The authentication workflow can be updated without needing to redeploy the app
  • Only genuine mobile clients can attempt user authentication
  • Man In the Browser (MITB) risks are eliminated for most authentication methods