Client Authentication (SDK Layer)#

When attestation cannot be used — older devices, simulators, or any Dynamic Client Registration fallback path — the SDK needs a client authentication method. The available methods (Secret, MTLS, Signed JWT) are the same as at the Driver Layer; the SDK Layer wires them onto HaapiAccessorBuilder (iOS) or HaapiAccessorFactory (Android). For the concept and trade-offs, see Client Authentication .

When to Use Client Authentication#

Configure a client authentication method whenever the SDK cannot rely on attestation alone. In practice this means:

  • DCR fallback is configured — the SDK authenticates the main HAAPI client when calling the registration endpoint to spawn a per-device dynamic client. Without an authentication method, the registration call fails before reaching the server. See DCR .
  • Attestation is disabled or unavailable on the deployment — for example, simulators, App Extensions that can’t access attestation APIs, or development builds where useAttestation is false.
  • The main HAAPI client on the Curity Identity Server uses a non-no-authentication method — the server-side method and the SDK-side method must match.

When the device passes attestation and the deployment is attestation-only, the SDK doesn’t use client authentication on the main HAAPI flow, and configuring it is optional. Configuring it anyway is safe — the SDK uses it only when needed — and is the recommended posture because it also covers the DCR-fallback path on devices that fail attestation.

Secret#

A static shared secret. Easy to set up; appropriate only for development or controlled environments because the secret is identical for every install.

let clientAuth = ClientAuthenticationMethodSecret("foo")

let haapiAccessor = HaapiAccessorBuilder(haapiConfiguration)
    .setClientAuthenticationMethod(method: clientAuth)
    .buildForHaapi()

Mutual TLS#

Mutual-TLS authentication: the client presents a certificate during the TLS handshake. The server validates the certificate against a configured trust store, or against pinned public-key hashes.

// Using a bundled PKCS12 keystore + server PEM:
let clientAuth = try ClientAuthenticationMethodMTLS(
    pkcs12Filename: "client.p12",
    pkcs12Passphrase: "passphrase",
    serverPEMFilename: "server.pem",
    isValidatingHostname: true,
    bundle: Bundle.main
)

// Or with pinned server public-key hash:
let clientAuthPinned = try ClientAuthenticationMethodMTLS(
    pkcs12Filename: "client.p12",
    pkcs12Passphrase: "passphrase",
    serverKeyPinnings: [
        KeyPinning(
            hostname: "idsvr.example.com",
            publicKeyHash: "Kjuy4mT3fbeDozRNP6rTjWRYmbs79Begb5Roq+DUu7s="
        )
    ],
    bundle: Bundle.main
)

let haapiAccessor = HaapiAccessorBuilder(haapiConfiguration)
    .setClientAuthenticationMethod(method: clientAuth)
    .buildForHaapi()

Signed JWT#

Build a JWT signed with an asymmetric or symmetric key and present it as client_assertion. The server is configured with the matching public key (asymmetric) or shared secret (symmetric) to verify the assertion. Asymmetric is recommended for production — the private key never leaves the device.

// Asymmetric (recommended for production):
let asymConfig = try ClientAuthenticationMethodJWTAsymmetric(
    pemFilename: "rsa-private-key.pem",
    signatureAlgorithm: .rs256,
    bundle: Bundle.main
)

// Symmetric (HMAC):
let symConfig = ClientAuthenticationMethodJWTSymmetric(
    signatureAlgorithm: .hs256,
    secretKey: "shared-secret-key"
)

let haapiAccessor = HaapiAccessorBuilder(haapiConfiguration)
    .setClientAuthenticationMethod(method: asymConfig)
    .buildForHaapi()

When configuring DCR fallback, the client authentication method must be set to one of Secret, MTLS, or Signed JWT — not the default None. DCR bootstraps a per-device dynamic client; doing so requires the SDK to authenticate the main client when calling the registration endpoint, and that authentication is what this method provides. Without it, the registration call fails before reaching the server. See DCR for the matching server-side prerequisites.

Was this helpful?