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.
application/vnd.auth+json
Accept
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.
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.)
DPoP
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.
use-legacy-dpop
false
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:
grant_type
client_credentials
client_secret
scope
urn:se:curity:scopes:haapi
In order for the token to be issued, the client must also have the haapi capability enabled in the server configuration.
haapi
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
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).
haapi-access-token-ttl
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.
attestation
<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.
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.
signature-digest
signingReport
SHA-256
$ ./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
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.
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.
app-id
teamId
bundleId
.
01234abcde
app.curity.io
01234abcde.app.curity.io
<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:
mode
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.
production
<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.
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.
allowed-origins
web
https://example.com:1234
<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:
web-policy
facilities/client-attestation
disable-origin-verification
true
After creating the custom policy, the static client configuration needs to refer it, as shown in the next snippet.
<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>
Note that using a custom policy that changes the default behavior may compromise the security characteristics of the system.
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.
attestation/disable-attestation-validation
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).
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:
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).
se.curity.identityserver.controllers.cat.RequestModel
CAT request ...
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...).
se.curity.identityserver.controllers.cat.CatController
WebAttestationData
Successfully issued a CAT...
Make sure to enable DEBUG-level logging for these loggers in non-production environments.
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:
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).
issue-token-bound-authorization-code
The DPoP-based proof-of-possession on authorization code and refresh token based token requests is handled by both the Android and iOS SDKs.
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:
Set-Session-Id
Session-Id
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.
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.
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.