Client Authentication#

For most production mobile apps, client authentication happens transparently: attestation handles client identity, and the OAuth client configured for HAAPI uses no-authentication because hardware-backed attestation already proves the client’s authenticity. When attestation cannot be used — older devices, simulators, or any flow that uses the Dynamic Client Registration fallback — the client must authenticate by another method.

The Three Methods#

Secret. The client sends a static shared secret on the token request. Easy to set up; appropriate only for development or controlled environments because the secret is identical for every install of the app.

val authMethod = ClientAuthenticationMethodConfiguration.Secret(secret = "foo")

Mutual TLS (MTLS). The client presents a client certificate during the TLS handshake. the Curity Identity Server validates the certificate against a configured trust store, or against a pinned public-key hash for the server side. Strong, but requires certificate provisioning and renewal infrastructure.

Signed JWT client assertion. The client builds a JWT (using a symmetric or asymmetric key pair) and presents it as client_assertion. The server is configured with the matching public key (or shared secret) to verify the assertion. Provides cryptographic proof of possession without TLS-level coupling and integrates cleanly with key-rotation processes.

Choosing a Method#

Production deployments that need a fallback typically use Signed JWT (asymmetric) — keys are easy to rotate, the signing material can live in the device Keychain or Keystore, and the server’s configuration is decoupled from the transport. MTLS suits environments where a public-key infrastructure already exists. Secret stays in development and prototyping; do not ship it.

When you configure a DcrConfiguration or set useAttestation to false, the client authentication method must be set to something other than None. Otherwise the flow fails before reaching the server.

Was this helpful?