Using the Android SDK to Consume the Authentication API

Using the Android SDK to Consume the Authentication API

tutorials

The authentication API is currently in Beta

General Availability (GA) of the Curity Authentication API will come soon. This information will help you prepare.

What Is the Hypermedia Authentication API?

The Hypermedia Authentication API is a feature of the Curity Identity Server. It enables clients to leverage OAuth and OpenID Connect and Curity’s powerful authentication engine straight from client applications — without the need of a browser. If you want to learn more about the Hypermedia Authentication API, check out the overview here.

It’s All About Security

The browser typically plays a vital role in OAuth and OpenID flows — it makes them more secure. Browser redirects ensure the Authorization Server delivers data to the proper client, and not to an impersonator. Thus, removing the browser from OAuth and OpenID flows may at first seem a bit reckless. As we now use an API to fulfill a vulnerable authentication and authorization flow, we require another method to make sure that the client calling the API is indeed allowed to do so.

We’ve added two security features to the Curity Identity Server to ensure the Authentication API process is just as secure as browser flows. These new security features are:

  • Client attestation.
  • Usage of proof of possession tokens.

Client Attestation

Client attestation is done by verifying the running application’s package ID and the signing credentials. This is processed by the Android Operation System and bound to a cryptographic key pair. After a positive attestation, the client (the mobile app) is issued a Client Attestation Token (CAT) in the form of a JWT. The concrete implementation of the attestation is out of scope for this article, but what is important to note here is that client attestation asserts a few things about the client that calls the Authentication API:

  1. The app was correctly signed with the credentials configured in the Curity Identity Server for the associated OAuth client. This means the app has not been tampered with. For example, it hasn’t been decompiled, had its code changed, and recompiled into a malicious client. What is essential when using these features is that decompilation cannot provide any information that could help create an impersonated client.
  2. The app runs in a safe environment, that is, on a system that has not been rooted. Otherwise, it wouldn’t be possible to perform the verification described above securely.

Demonstration of Proof-of-Possession

Proof-of-Possession tokens are another way to use access tokens. The most common ones used in APIs are Bearer tokens. Bearer tokens work a bit like cash — any client who posses a Bearer access token can successfully call an API. Proof-of-Possession tokens behave more like credit cards — only legitimate owners of these tokens can use them to access the API. This further increases the security of the communication. Even if an access token is stolen, it can’t be used by another party.

The Hypermedia Authentication API uses a standard for Demonstration of Proof-of-Possession. By adopting this standard, access tokens used to access the Authentication API are bound to a private key that only the legitimate client possesses.

The Android SDK for Hypermedia Authentication API

Obtaining CATs and using DPoP to secure requests to the API is a security-sensitive process and quite complicated to implement. The method of obtaining a CAT requires a few different steps, and the DPoP standard requires the client to generate proper DPoP headers for every request. To facilitate the usage of these crucial security components, Curity has released a simple SDK for Android to help you leverage the Hypermedia API.

Prerequisites for using the API

To call the API from an Android app, make sure of the following:

  1. You must use at least version 5.3 of the Curity Identity Server.

  2. The Authentication API is still in beta and is disabled by default. To enable support for the API, run the server with the system property se.curity.haapi.enable set to true. Follow these steps to set this property using the configuration CLI:

    • Start the command shell by running the idsh command from the bin directory of your Curity Identity Server installation folder.
    • Enter configuration mode by running the configure command.
    • Enter the following command. Use the appropriate service role instead of default if your server runs in a different role. Remember that this overwrites the configuration setting — so if you ever changed the JVM options, adjust the value accordingly.
set environments environment services service-role default jvm-options "-XX:+UseG1GC -Dse.curity.haapi.enable=true"
  • Enter the commit command and exit the shell.
  • As we’ve changed the JVM settings, you need to restart the Curity Identity Server.

If you prefer to use the Admin UI or RESTCONF to change the JVM settings, have a look at the documentation.

  1. You will require a client with the API capability to use the Android attestation policy. These settings cannot currently be set using the web UI, but you can use the following XML file to merge it with your server’s configuration. Make sure to adjust values in the XML below to conform with your environment:

You can use this version of configuration to ease development and allow emulator usage. It has some relaxed requirements for validation. Remember that these options shouldn’t be used in production unless necessary to support special devices.

  • In line 17, change the Token Service’s name, if you don’t use the default.
  • In line 24, set the proper client name.
  • In line 27, set the package name your app uses.
  • In line 28, enter the digest of the credentials used to sign your app. (Read below to learn how to get the digest if you use Android Studio.)
<config xmlns="http://tail-f.com/ns/config/1.0">
    <facilities xmlns="https://curity.se/ns/conf/base">
        <client-attestation>
            <android-policy xmlns="https://curity.se/ns/conf/client-attestation">
                <id>emulator-policy</id>
                <verify-boot-state>false</verify-boot-state>
                <minimum-security-level>software</minimum-security-level>
                <override-certificate-chain-validation>
                    <do-not-validate-certificate-chain/>
                </override-certificate-chain-validation>
            </android-policy>
        </client-attestation>
    </facilities>
    <profiles xmlns="https://curity.se/ns/conf/base">
        <profile>
            <id>token-service</id>            <type xmlns:as="https://curity.se/ns/conf/profile/oauth">as:oauth-service</type>
            <settings>
                <authorization-server xmlns="https://curity.se/ns/conf/profile/oauth">
                    <client-store>
                        <config-backed>
                            <client>
                                <id>client-name</id>                                <attestation>
                                    <android>
                                        <package-name>com.example.yourpackage</package-name>                                        <signature-digest>A3...4=</signature-digest>                                        <android-policy>emulator-policy</android-policy>
                                    </android>
                                </attestation>
                            </client>
                        </config-backed>
                    </client-store>
                </authorization-server>
            </settings>
        </profile>
    </profiles>
</config>

Use the following configuration for production environments. It uses default settings for the attestation policy.

  - In line 4, change the Token Service's name, if you don't use the default.
  • In line 11, set the proper client name.
  • In line 15, set the package name your app uses.
  • In line 16, enter the digest of the credentials used to sign your app. (Read below to learn how to get the digest if you use Android Studio.)
<config xmlns="http://tail-f.com/ns/config/1.0">
    <profiles xmlns="https://curity.se/ns/conf/base">
        <profile>
            <id>token-service</id>            <type xmlns:as="https://curity.se/ns/conf/profile/oauth">as:oauth-service</type>
            <settings>
                <authorization-server xmlns="https://curity.se/ns/conf/profile/oauth">
                    <client-store>
                        <config-backed>
                            <client>
                                <id>client-name</id>                                <attestation>
                                    <android>
                                        <package-name>com.example.yourpackage</package-name>                                        <signature-digest>A3...4=</signature-digest>                                    </android>
                                </attestation>
                            </client>
                        </config-backed>
                    </client-store>
                </authorization-server>
            </settings>
        </profile>
    </profiles>
</config>

Remember, you can also use the configuration CLI or RESTCONF API to set these settings.

Obtaining a Signing Key Digest

If you use Android Studio, you can use the gradle task signingReport to obtain hashes of your signing keys. You then must base64-encode the SHA-256 hash. Here’s how to do it from the terminal:

$ ./gradlew signingReport
...
SHA-256: 67:60:CA:11:93:...:4A:7E:A0:A4
...
$ echo "67:60:CA:11:93:...:4A:7E:A0:A4" | xxd -r -p | base64
Z2DKE...9oKQ=

You now have the Server and a client configured to use the authentication API from an Android app.

Using the SDK in an Android app

You can obtain the SDK from the Curity Nexus. It is publicly available, so you don’t need any credentials to download the SDK. Add the Curity Nexus repository to your project dependency repositories.

Add the following to `build.gradle`:
  repositories {
    ...
    maven {
        url 'https://nexus.curity.se/nexus/content/repositories/customer-release-repo'
    }
}
Add the following to your `pom.xml`:
<repositories>
    <repository>
        <id>customer-release-repo</id>
        <url>https://nexus.curity.se/nexus/content/repositories/customer-release-repo</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

Then, you can add the SDK dependency to your project.

dependencies {
    ...
    implementation 'se.curity.identityserver:identityserver.haapi.android.sdk:1.0.0-beta2'
}
<dependency>
  <groupId>se.curity.identityserver</groupId>
  <artifactId>identityserver.haapi.android.sdk</artifactId>
  <version>1.0.0-beta2</version>
  <scope>provided</scope>
</dependency>

The SDK is available for Kotlin and Java, depending on the chosen language of your project.

The SDK exposes an object HaapiTokenManager and a utility function for OkHttp HTTP client, which registers an interceptor. The token manager accepts some configuration options and is responsible for obtaining a Client Attestation token and creating proper DPoP headers. The interceptor is used to enrich requests with the necessary tokens and headers.

Below is an example of calling the API with the SDK. Note that no additional workaround Client Attestation or DPoP is needed. Thanks to the SDK, these are still used in the communication with the API.

val haapiTokenManager = HaapiTokenManager(
    URI("https://curity-instance.example.com/oauth/v2/oauth-token"),
    "client-name"
)

val httpClient = OkHttpClient.Builder()
    .addHaapiInterceptor(haapiTokenManager)
    .build()

val authorizeUrl = Uri.Builder()
        .scheme("https")
        .authority("curity-instance.example.com")
        .appendPath("oauth")
        .appendPath("v2")
        .appendPath("oauth-authorize")
        .appendQueryParameter("client_id", "client-name")
        .appendQueryParameter("scope", "openid")
        .appendQueryParameter("response_type", "code")
        .build()
        .toString()

val request = Request.Builder()
    .get()
    .url(authorizeUrl)
    .build()

val response = httpClient.newCall(request).execute()
val responseBody = haapiResponse.body?.string() ?: "{}"

val responseObject = JSONObject(responseBody)

println("Type of the response from API: ${responseObject["type"]}")
HaapiTokenManager haapiTokenManager = new HaapiTokenManager.Builder(new URI("https://curity-instance.example.com/oauth/v2/oauth-token"), "client-name").build();

OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();

OkHttpUtils.addHaapiInterceptor(httpClientBuilder, haapiTokenManager);
OkHttpClient httpClient = httpClientBuilder.build();

String authorizeUrl = new Uri.Builder()
    .scheme("https")
    .authority("curity-instance.example.com")
    .appendPath("oauth")
    .appendPath("v2")
    .appendPath("oauth-authorize")
    .appendQueryParameter("client_id", "client-name")
    .appendQueryParameter("scope", "openid")
    .appendQueryParameter("response_type", "code")
    .build()
    .toString();

Request request = new Request.Builder()
    .get()
    .url(authorizeUrl)
    .build();

Response response = httpClient.newCall(request).execute();

String responseBodyString;

try {
    ResponseBody responseBody = response.body();
    if (responseBody != null) {
        responseBodyString = responseBody.string();
    } else {
        responseBodyString = "{}";
    }
} catch (IOException e) {
    responseBodyString = "{}";
}

JSONObject responseObject = new JSONObject(responseBodyString);

System.out.println("Type of the response from API: " + responseObject.getString("type"));

Conclusion

With the Android SDK, you can start using the Authentication API in minutes without delving into the specific details of the API’s security enhancements. Thanks to this SDK, you can quickly focus on creating user interfaces.

To learn more about implementing clients that consume this API, have a look at the Authentication API documentation.

If you need help setting up the API or SDK, or have any other questions or comments, please do not hesitate to contact us.

Let’s Stay in Touch!

Get the latest on identity management, API Security and authentication straight to your inbox.

Keep up with our latest articles and how-tos using RSS feeds