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.
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:
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:
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:
From this point onwards the app will operate like a traditional mobile OAuth client and call APIs.
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.
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:
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:
The deployed system also includes a user account of
demouser / Password1 that can be used to sign in.
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
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.
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>
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.
- Update the
newInstancemethod in the
Configurationclass 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") )
- 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.
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.
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:
|Driver||Curity Library||Processing of low-level messages used for client attestation to prove the app's identity.|
|Controller||Curity Library||Processing HAAPI responses, including form submitting, following redirects, and polling the server.|
|View||Your App||Overriding the defaults and customizing presentation to take full control over the login user experience.|
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 Parameter||Example Value|
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 Field||Example Value|
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:
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:
HAAPI supports many authentication methods in addition to passwords, so you are encouraged to try additional authentication options such as these:
|Google Sign In||With this type of personal password, HAAPI will open the system browser to sign the user in|
|CIBA Flow||With this authentication flow, HAAPI polls while a client waits for a remote user to sign in|
|WebAuthn||This 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.
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 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 Parameter||Usage|
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:
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