Hypermedia Authentication API

Introduction

This document describes the Hypermedia Authentication API (HAAPI) that allows OAuth clients to request tokens from the Curity Identity Server in an API-driven way, i.e., without requiring a browser to perform the user authentication and consent interactions.

Curity Identity Server supports a wide range of authentication methods, with highly dynamic flows that depend on previous user actions as well on contextual information. Due to this characteristic, HAAPI adopts a hypermedia-driven style, where the client application is guided through the flow based on hypermedia controls such as forms and links, present in the HAAPI responses. The client application should use these responses and controls to drive the UI presented to the user and obtain the information required to produce the next request. For this matter, HAAPI uses a JSON-based custom media type with the application/vnd.auth+json media type identifier. The client application must always send an Accept header including this media type identifier, to inform Curity Identity Server that the request is an HAAPI request. This is required because HAAPI shares most of the URIs that already exist for the traditional HTML-based interactions.

Tip

For a broader overview of the API, checkout the recorded Webinar and the getting started articles in the resource library available on our Web site.

Access control

All requests to HAAPI require a HAAPI access token. In order to obtain it, a client application must perform an OAuth 2.0 token request to the token endpoint, using the client credentials grant.

The HAAPI access token is a DPoP-bound token and not a bearer token. Due to this, the token request also needs a DPoP header with a proof token (see OAuth 2.0 Demonstration of Proof-of-Possession draft specification which may change as it is finalized.)

Tip

For an introduction to DPoP, checkout the Demonstration of Proof-of-Possession overview in the resource library available on our Web site.

The DPoP processing depends on the use-legacy-dpop client setting, located inside the Haapi client capability. When set to false, Curity Identity Server will use an improved DPoP processing algorithm, providing better security and handling of clock skew issues. However, using this new non-legacy behavior requires the use of compatible mobile (Android or iOS) HAAPI SDK versions. See Curity SDKs for further information. Any HAAPI Web SDK version will correctly support the new DPoP behavior.

The following example request illustrates the HAAPI access token request, using a client secret as the client authentication method:

POST /dev/oauth/token HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
DPoP: eyJqd2siOnsiYWxnIjoiUFMyNTYiLCJlIjoiQVFBQiIsImV4dCI6dHJ1ZSwia2V5X29wcyI6WyJ2ZXJpZnkiXSwia3R5IjoiUlNBIiwibiI6InhYRTB3Um55MVUtbmNSZnd4RUt4WlhJMzJmOTR5NDRlSGpTUnBwSjFmdDFwUEtwanZydlIwWXc5dGo2Zm9GMVJJeDJKdVJmamFLQURNSlpPTUd3Mks2eERpUkFXMzh5Y19faldLMUMxMFY2X3NxZkNSeU5BRFVtNUlrTzcxOXNYRW5iZTU1dVd6aTlCX3VyZ0tZQ1VpcTI1MXdRRC1UbXdRbUpfWEpabmhDdVlmbm4tRzcxWmJhNFMxdGJPS1F5dzlmOHVtRFFOQXVwTlptUU1oVW9xbTcxbm1qWkVGSWtvWlNtaWRtdHctd0pEZVN4Z3AtU2pVTXFPYk1XT2IwOWNLUF9kUmZyTlhaYi12UjloNVBUUm92aC1nX1lOODl4SU5rUld6cld0akxLdGtVLS02R2xkXzhoUW1uajlMVDlRMFFJdmFtVWI0VFlGVFNXcWNvZkFBUSJ9LCJhbGciOiJQUzI1NiIsInR5cCI6ImRwb3Arand0In0.eyJqdGkiOiJjZjdkNmZmYy1iYWY5LTQ5NTgtOWViYy1jYmY1NTQ4OGNlZTciLCJodG0iOiJQT1NUIiwiaHR1IjoiaHR0cHM6Ly9sb2NhbGhvc3Q6ODQ0My9kZXYvb2F1dGgvdG9rZW4iLCJpYXQiOjE1OTI1ODE0NDN9.vquk9ES8KDU-kY2dd_t5XQM3NfhOiXVbB_qr8iNzdFhHf7C0EMa4-TVs6LlYvVmfujWeoyLkcjqNi2qXg3CY9uZSqdS3qcdjKjt6antieZLVrGNNYkBFtikCzz2nNWjh9JoAtTgo7E3Hs20FhFtAMLWMc8XL2T1ADDkHwhgDKLTKPA8wbLwInegaICqtcEojFv9LIAapSAwpGyU4-CcoHlihJc6u1tb6rta7jXeLvxq_e8yo3ZH9AYYOT0dh_cf6WEEhTv4IqcA5kyIUWuoQGwJsicl9t2v5vQclVglyQRvEQQIO6YR5yXJbKczqqb5CtZvXRHEGAyO0iMxfuLC6vQ

client_id=client-one&client_secret=client-secret&scope=urn:se:curity:scopes:haapi&grant_type=client_credentials

Notice:

  • The request DPoP header, containing the DPoP proof token.
  • The request payload with grant_type set to client_credentials and using the client_secret field.
  • The scope set to urn:se:curity:scopes:haapi, signalling that the requested token is for HAAPI.

In order for the token to be issued, the client must also have the haapi capability enabled in the server configuration.

The HAAPI access token must be used on all HAAPI requests, by adding it to the Authorization header and using the DPoP scheme, along side with a per-request DPoP proof token present in the DPoP header, as illustrated in the following request:

GET /dev/oauth/authorize?client_id=client-one&response_type=code&...
Host: example.com
Accept: application/vnd.auth+json
Authorization: DPoP ey...Yw
DPoP: eyJqd2siOnsiYWxnIjoiUFMyNTYiLCJlIjoiQVFBQiIsImV4dCI6dHJ1ZSwia2V5X29wcyI6WyJ2ZXJpZnkiXSwia3R5IjoiUlNBIiwibiI6InhYRTB3Um55MVUtbmNSZnd4RUt4WlhJMzJmOTR5NDRlSGpTUnBwSjFmdDFwUEtwanZydlIwWXc5dGo2Zm9GMVJJeDJKdVJmamFLQURNSlpPTUd3Mks2eERpUkFXMzh5Y19faldLMUMxMFY2X3NxZkNSeU5BRFVtNUlrTzcxOXNYRW5iZTU1dVd6aTlCX3VyZ0tZQ1VpcTI1MXdRRC1UbXdRbUpfWEpabmhDdVlmbm4tRzcxWmJhNFMxdGJPS1F5dzlmOHVtRFFOQXVwTlptUU1oVW9xbTcxbm1qWkVGSWtvWlNtaWRtdHctd0pEZVN4Z3AtU2pVTXFPYk1XT2IwOWNLUF9kUmZyTlhaYi12UjloNVBUUm92aC1nX1lOODl4SU5rUld6cld0akxLdGtVLS02R2xkXzhoUW1uajlMVDlRMFFJdmFtVWI0VFlGVFNXcWNvZkFBUSJ9LCJhbGciOiJQUzI1NiIsInR5cCI6ImRwb3Arand0In0.eyJqdGkiOiIxZWE1ODY2OS01OTIwLTRhMmMtOGU0MS05NTMyMDAyZGQxODAiLCJodG0iOiJHRVQiLCJodHUiOiJodHRwczovL2xvY2FsaG9zdDo4NDQzL2Rldi9vYXV0aC9hdXRob3JpemUiLCJpYXQiOjE1OTI1ODE0NDN9.G0MTv82emHMNE2VLwLj57uBcKMLdIh1IFzQaB_qRgWwzOQL6nmfvUNQQzHKkPUjkP4B8M3FK7PH9w7hyJOBCiQqNnFGBCUbsV67zCGpGG7gO1u74o6-_yfiydQOFrZJZaib0vD-P9471KhLq0NikUT9-rUghDxt1o2OLDDbRKM3am_iLCeiNoeB9uMR6iimyXKwphS1RaYDHO7k_82-8G1soGm-EF40St7_kMZbSUR4T_XKjp0hZKx-U5FuKnGzd_4SUpT7kTrFqMv6COjg9GbGqyNS4glfYawPywHyZcAHwPSeycu6G48PHaX_N1v-mVgDFo80ITSCBgQycm-sfBA

Client attestation

Tokens requests can also use an attestation-based authentication method, in alternative to client secrets. This method is adequate for use by public clients, such as native clients or browser-based clients, which cannot hold static client secrets. The attestation process uses client-side platform specific functionalities to verify the client application identity. Due to that, the recommended way is for client applications to use a Curity provided client SDK (Software Development Kit) to perform attestation and manage API access tokens.

Attestation settings are defined in the client configuration section and vary with the client’s platform, as described in the following sections. Some more advanced configurations can be made via client attestation policies, namely defining the TTL of HAAPI access tokens (haapi-access-token-ttl setting).

Android client attestation configuration

A statically configured OAuth client can use Android based attestation via the attestation client configuration. The following configuration excerpt shows how to use this configuration element.

<authorization-server xmlns="https://curity.se/ns/conf/profile/oauth">
    <client-store>
        <config-backed>
            <client>
                <id>the-client-id</id>
                <attestation>
                  <android>
                      <package-name>com.example.client.app</package-name>
                      <signature-digest>Z2DK...9oKQ=</signature-digest>
                  </android>
                </attestation>
            </client>
        </config-backed>
    </client-store>
</authorization-server>

The package-name property must have the client application Android package name.

The signature-digest property must have the SHA-256 hash of the signing certificate, encoded using Base64. To obtain this value, run the signingReport gradle task on the Android Studio project, look for the SHA-256 line and convert its value to Base64.

Listing 277 Obtain the signing certificate hash and convert it to Base64.
$ ./gradlew signingReport
...
SHA-256: 67:60:CA:11:93:B6:5D:61:56:42:70:29:A1:10:B3:86:A8:48:C7:33:83:7B:B0:54:B0:0A:E3:E1:4A:7D:A0:A4
Valid until: Wednesday, July 5, 2045
$ echo "67:60:CA:11:93:B6:5D:61:56:42:70:29:A1:10:B3:86:A8:48:C7:33:83:7B:B0:54:B0:0A:E3:E1:4A:7D:A0:A4" | xxd -r -p | base64
Z2DKEZO2XWFWQnApoRCzhqhIxzODe7BUsArj4Up9oKQ=

To support client applications running in Android emulators, namely during the development phase, a custom Android attestation policy needs to be defined in facilities

<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>the-emulator-policy-name</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>
</config>

Then, the client attestation configuration needs to explicitly refer this policy

<authorization-server xmlns="https://curity.se/ns/conf/profile/oauth">
    <client-store>
        <config-backed>
            <client>
                <id>the-client-id</id>
                <attestation>
                  <android>
                      <package-name>com.example.client.app</package-name>
                      <signature-digest>Z2DK...9oKQ=</signature-digest>
                      <android-policy>the-emulator-policy-name</android-policy>
                  </android>
                </attestation>
            </client>
        </config-backed>
    </client-store>
</authorization-server>

Warning

Notice that custom policies should only be used for development purposes or to support special devices, since they may reduce the Android attestation security.

On the client side, the attestation protocol is performed automatically by the Curity provided SDK, including the use of native Android attestation APIs.

iOS client attestation configuration

A statically configured iOS client can use iOS attestation (i.e. Apple Device Check services) by configuring the iOS attestation settings of the client. These settings include the app-id of the iOS client app, that is made up of the Apple-assigned teamId together with the app’s bundleId, separated by a .. For example, when the teamId is 01234abcde and the app’s bundleId is app.curity.io, then the AppId to configure is 01234abcde.app.curity.io. This value is needed to identify the attested client app.

Listing 278 example configuration snippet for an iOS OAuth client
<authorization-server xmlns="https://curity.se/ns/conf/profile/oauth">
    <client-store>
        <config-backed>
            <client>
              <id>ios-oauth-client</id>
              ...
              <capabilities>
                ...
                <haapi/>
              </capabilities>
              <attestation>
                <ios>
                  <app-id>01234abcde.app.curity.io</app-id>
                <ios>
              </attestation>
            </client>

Further configuration of iOS related attestation is done by a shared iOS Attestation Policy in Curity’s facilities. Here you can set properties to:

  • Indicate whether attestation is done for production or non-production apps through the mode setting,
  • Configure how the attested credentials are cryptographically validated. By default this uses Apple’s CA to verify attested credential data. It can be overridden such that no verification of the attested credential data is done, or to use an alternative CA certificate.

When a client does not configure a particular iOS Attestation Policy, a default policy is applied that is comparable to a policy with a production-mode and uses Apple’s CA to verify attested credential data.

Listing 279 example configuration snippet for a non-validating iOS Attestation policy
<config xmlns="http://tail-f.com/ns/config/1.0">
  <facilities xmlns="https://curity.se/ns/conf/base">
      <client-attestation>
          <ios-policy xmlns="https://curity.se/ns/conf/client-attestation">
              <id>non-prod-policy</id>
              <mode>non-production</mode>
              <override-certificate-chain-validation>
                  <do-not-validate-certificate-chain/>
              </override-certificate-chain-validation>
          </ios-policy>
      </client-attestation>
  </facilities>
</config>

Note that when custom trust-anchors are used in an iOS Attestation Policy, they must be provided in PEM-format.

When a iOS client is successfully attested, a CAT is issued that the client can exchange for a HAAPI token, similarly to how web- and Android clients do.

Browser (Web) client attestation configuration

A browser-based OAuth client needs to be properly configured in Curity before it can perform a successful attestation and use HAAPI. Currently, this is only possible for statically configured clients and involves two parts.

First, the HTTP origin where the browser-based application is running needs to be added to the allowed-origins list and attestation needs to be set to web. The haapi capability also needs to be enabled, similarly to what happens for the other client types. The following snippet exemplifies these configuration requirements, for a browser-based client application running from https://example.com:1234.

Listing 280 example configuration snippet for a browser-based client.
<authorization-server xmlns="https://curity.se/ns/conf/profile/oauth">
  <client-store>
      <config-backed>
          <client>
            <id>haapi-public-client</id>
            <allowed-origins>https://example.com:1234</allowed-origins>
            ...
            <capabilities>
              ...
              <haapi/>
            </capabilities>
            <attestation>
              <web />
            </attestation>
          </client>

Finally, the client may optionally use a custom attestation policy. For that, a new web-policy needs to be defined under facilities/client-attestation. This policy can then override the default attestation behavior characteristics:

  • disable-origin-verification - Allows a browser-based client to obtain attestation from any origin. This setting should not be set to true in production environments and should be used only for development and testing purposes.

After creating the custom policy, the static client configuration needs to refer it, as shown in the next snippet.

Listing 281 example custom attestation policy snippet for a browser-based client.
<config xmlns="http://tail-f.com/ns/config/1.0">
  <facilities xmlns="https://curity.se/ns/conf/base">
      <client-attestation>
          <web-policy xmlns="https://curity.se/ns/conf/client-attestation">
              <id>test-policy</id>
              <disable-origin-verification>true</disable-origin-verification>
          </web-policy>
      </client-attestation>
  </facilities>
  ...
  <profiles xmlns="https://curity.se/ns/conf/base">
      <profile>
          <id>...</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>haapi-public-client</id>
                              <attestation>
                                  <web>
                                      <web-policy>test-policy</web-policy>
                                  </web>
                              </attestation>
                              ...
                          </client>

Warning

Note that using a custom policy that changes the default behavior may compromise the security characteristics of the system.

Disabling attestation for testing purposes

It is possible to completely disable the attestation validation performed for a specific configured client. This is achieved by setting attestation/disable-attestation-validation under the client’s configuration. The client application should still perform the attestation protocol, using the provided SDKs, however the attestation data will not be validated.

Warning

Disabling attestation validation for a client allows any application to act as that client. Therefore, this should only be used in non-production environments, as a way to enable some testing scenarios (e.g. testing iOS applications running on simulators and not real devices).

Debugging Web CAT problems

It may be difficult to understand when problems happen because, by the nature of client attestation on a browser, a lot of things happen in the background (generation of cryptographic keys, execution of binary code, exchange of messages between Javascript application and the Javascript HAAPI driver) which can go wrong in certain circumstances.

Problems might occur, for example, due to users having older browsers which do not have the most recent Web Crypto API or cannot execute WebAssembly. We have also seen browsers that appear to “freeze” a session while the user likely closes the window, only to restart again much later and attempt to continue an authentication flow when the temporary tokens stored by the browser have long expired.

To make it easier to troubleshoot such issues, we have added extra logging both in the Javascript driver library and in the Curity Identity Server.

To enable extra logging on the client, add the following Javascript snippet to your application:

Listing 282 enabling debug information for the haapi-web-driver.
window['se:curity:web-cat:debug'] = true

Refer to the haapi-web-driver package documentation for more details.

On the server side, the logger called se.curity.identityserver.controllers.cat.RequestModel, in particular, will log information about requests from Web CAT Clients (look for the CAT request ... message).

On successful CAT issuance, se.curity.identityserver.controllers.cat.CatController will log the full attestation data collected on the client (look for the messages containing WebAttestationData and Successfully issued a CAT...).

Make sure to enable DEBUG-level logging for these loggers in non-production environments.

Attestation fallback and dynamically registered clients

The Curity Identity Server may be used in scenarios where attestation support is not fully available, for instance when needing to support older devices or mobile operating system versions. For those situations, it is possible to use an alternative method to obtain HAAPI access tokens, based on the use of dynamically registered clients (DCR), which is commonly called “DCR fallback”. This method is based on the following:

  • The main HAAPI client must be configured with a proper authentication method (i.e. other than no-authentication).
  • A template HAAPI client must be configured with the required capabilities, namely the haapi capability. This template HAAPI client must be configured to use the main HAAPI client for the authenticate-client-by setting.
  • Each mobile application must have access to the main HAAPI client credentials.
  • On each device, the main HAAPI client credentials will be used to obtain a DCR initial token (see Dynamic Clients) using the client credentials grant. Then, this initial token will be used to register a new confidential DCR client, specific to that device. This DCR client should use device specific credentials, typically generated on the device.
  • Finally, every time a new HAAPI access token needs to be obtained by the device, the DCR client will be used to perform a client credentials based HAAPI token request, using the device-specific credentials.

The following resources describe this method in more detail:

The DCR fallback method is supported by both the HAAPI Android and HAAPI iOS SDKs.

Enabling DCR-fallback on existing deployments

The DCR fallback method may need to be added after an initial deploy without it. This means that during a transition period there will be:

  • old versions of the mobile application, where the configuration of the HAAPI client in the application does not have any credentials;
  • and newer versions of the mobile application, where the configuration of the HAAPI client in the application does have any credentials.

In order for both mobile application versions to be able to work during that transition period, the HAAPI client must be configured on the server with:

  • A primary authentication method, different from no-authentication, used when the newer application version requests DCR initial tokens.
  • A secondary no-authentication method, used when the older mobile application version does attestation without any credentials.

When the transition period is over, the secondary method should be removed.

Caution

The use of the DCR fallback method changes the HAAPI security properties. Therefore, it should only be used when needing to accept devices without proper attestation support and when the security trade-offs are acceptable.

Authorization code and refresh token binding

By default, the use of the DPoP protocol for proof-of-possession is limited to the HAAPI Access Token. However, it is also possible to require proof-of-possession for the authorization code and refresh tokens that result from an HAAPI-based authorization request. When this feature is enabled, then:

  • The issued authorization code will be bound to the key used during the HAAPI flow, meaning that its use on a token request must be accompanied by a DPoP proof-of-possession of that key.
  • In addition, the issued refresh token will also be bound to the same key, requiring that uses of that refresh token on future token requests must also be accompanied by a DPoP proof-of-possession of that key. This applies to the first refresh token and all the subsequent refresh tokens that are associated to the initial authorization code grant.

For the moment being, the access tokens are not bound to this key and their usage does not require any proof-of-possession.

Enabling DPoP-based proof-of-possession for both authorization codes and refresh tokens is done via the issue-token-bound-authorization-code client setting, located inside the Haapi client capability. Enabling this property can only be done if the sibling use-legacy-dpop client setting is set to false (the default value).

The DPoP-based proof-of-possession on authorization code and refresh token based token requests is handled by both the Android and iOS SDKs.

Flow state management

HAAPI was designed to be used in cross origin browser-based applications, i.e., applications that have a different origin than the one where HAAPI is located. Due to that, HAAPI uses a custom header and not cookies to communicate the state required through the several steps that constitute an authorization and authentication flow. This state management protocol is based on two headers:

  • The Set-Session-Id response header is used by the server to communicate an opaque identifier to the client application.
  • The Session-Id request header is used by the client to send this opaque identifier on all subsequent requests from the same flow. Note that the server can send new Set-Session-Id headers in the middle of a flow. In that case, the client application must use the newer identifier to complete the flow.

Session identifiers are bound to access token DPoP keys, meaning if those keys change (e.g. due to a new access token being requested), then a new flow needs to be started.

API Driven UI

It is possible to use HAAPI to create an API-driven front-channel user interface, such as a Single Page Application (SPA), as an alternative to the template driven approach documented on chapter Front-End Development. See section API Driven UI for more information.

SDK

Curity provides a number of SDKs that allow the development of tools to extend and integrate with the Curity Identity Server. The following HAAPI SDKs are currently available to help develop client applications that use HAAPI.