How to Configure DCR Fallback#
You want users on attestation-incapable devices — older OS versions, non-Google-certified Android hardware, simulators, devices the server rejects — to still complete authentication. Dynamic Client Registration (DCR) is the SDK’s fallback: when attestation is unavailable, the SDK registers a per-device dynamic OAuth client and uses that client for subsequent token requests.
For the concept and trade-offs, see DCR . For the per-layer configuration depth, see DCR (SDK Layer) . This page is the end-to-end task walkthrough.
Prerequisites#
DCR is a coordinated client-and-server feature. The server side must be configured first; the client side is wired afterward. On the Curity Identity Server:
- Main HAAPI client with a real authentication method (Secret, MTLS, or Signed JWT). These are the credentials that ship with your app and the SDK uses to bootstrap the registration request. The main client must not use
no-authentication. - Template HAAPI client carrying the
haapicapability. Defines the shape of the per-device dynamic clients (capabilities, allowed scopes, token settings). The template client must reference the main client via theauthenticate-client-bysetting. - OAuth profile exposes the DCR endpoint (
oauth-registration). Note the URL — you’ll paste it into the client configuration below.
See the Implementing HAAPI Attestation Fallback guide on the Curity resources site for the full server-side walkthrough.
Step-by-Step#
1. Choose a client authentication method#
DCR cannot use no-authentication — the SDK must prove itself as the main HAAPI client when calling the registration endpoint. Pick one:
- Secret — easy, development-only.
- Mutual TLS — production-recommended for sensitive deployments.
- Signed JWT (asymmetric) — production-recommended for app distribution where the private key stays on-device.
Per-method depth lives at Client Authentication (SDK Layer) .
2. Wire DCR + the auth method onto your builder#
Configure both unconditionally — the SDK picks attestation when available and falls back to DCR only when needed.
SDK Layer#
let dcrConfiguration = DCRConfiguration(
templateClientId: "dcr-template-client-haapi-ios",
clientRegistrationEndpointUrl: URL(
string: "/oauth/oauth-registration",
relativeTo: baseURL
)!
)
let haapiAccessor = HaapiAccessorBuilder(haapiConfiguration)
.setDCRConfiguration(dcrConfiguration)
.setClientAuthenticationMethod(method: ClientAuthenticationMethodSecret("foo"))
.buildForHaapi()UI Layer#
HaapiUIKitConfigurationBuilder(...)
.setClientAuthenticationMethod(method: ClientAuthenticationMethodSecret("foo"))
.setDCRConfiguration(
configuration: DCRConfiguration(
templateClientId: "dcr-template-client-haapi-ios",
clientRegistrationEndpointUrl: URL(
string: "/oauth/oauth-registration",
relativeTo: baseURL
)!
)
)
.build() SDK Layer#
val dcrConfiguration = DcrConfiguration(
templateClientId = "dcr-template-client-haapi-android",
clientRegistrationEndpointUri = URI.create("$baseURLString/oauth/oauth-registration"),
context = applicationContext
)
val haapiAccessor = HaapiAccessorFactory(haapiConfiguration)
.setDcrConfiguration(dcrConfiguration)
.setClientAuthenticationMethodConfiguration(
ClientAuthenticationMethodConfiguration.Secret(secret = "foo")
)
.createForHaapi(onCoroutineContext = this.coroutineContext)UI Layer#
WidgetConfiguration.Builder(...)
.setClientAuthenticationMethod(
WidgetConfiguration.ClientAuthenticationMethod.Secret("foo")
)
.setDcr(
WidgetConfiguration.Dcr(
templateClientId = "dcr-template-client-haapi-android",
clientRegistrationEndpointUri = baseUri.resolve("/oauth/oauth-registration"),
context = this
)
)
.build() React Native has only an SDK Layer surface. Attach dcrConfiguration and a non-noAuthentication client authentication method to each platform config record. The native bridge picks attestation vs DCR at runtime — your JS code does not branch.
import {
createIOSConfiguration,
createAndroidConfiguration,
secretAuthentication,
ApplicationContextOption,
} from 'identityserver.haapi.reactnative.sdk'
const iosConfig = createIOSConfiguration({
clientId: 'haapi-ios-client',
appRedirect: 'app://haapi',
clientAuthenticationMethodConfiguration: secretAuthentication('foo'),
dcrConfiguration: {
templateClientId: 'dcr-template-client-haapi-ios',
clientRegistrationEndpointUrl:
'https://idsvr.example.com/oauth/oauth-registration',
},
})
const androidConfig = createAndroidConfiguration({
clientId: 'haapi-android-client',
appRedirect: 'app://haapi',
clientAuthenticationMethodConfig: secretAuthentication('foo'),
dcrConfiguration: {
templateClientId: 'dcr-template-client-haapi-android',
clientRegistrationEndpointUrl:
'https://idsvr.example.com/oauth/oauth-registration',
contextOption: ApplicationContextOption.Default,
},
})For production deployments, use iOSJwtAsymmetricAuthentication / AndroidJwtAsymmetricAuthentication (or MTLS) instead of secretAuthentication. See Client Authentication for the full set of per-platform factories.
Verify It Works#
The SDK selects between attestation and DCR automatically at runtime. To confirm the DCR path runs on an attestation-incapable device:
- Run the app on an iOS Simulator or an Android emulator.
- Watch the logs for
HAAPI_DRIVER_DCRrecords (Android) or DCR-related events inHaapiLogger(iOS). On first launch, the SDK calls the registration endpoint and persists the dynamic client identity to storage. - The HAAPI flow proceeds and the user receives an access token, just as on an attestation-capable device.
Pitfalls#
- Forgetting client authentication. Configuring
setDCRConfiguration/setDcrwithout a non-Noneauthentication method causes the first registration call to fail at the server. The SDK Layer’s DCR warning admonition spells this out. - Template client missing
authenticate-client-by. Without this server setting, the registration request is unauthenticated and the server rejects it. Validate the template-client configuration before testing the client path. - Wrong endpoint URL. The dist endpoint typically lives at
<baseURL>/oauth/oauth-registration, but some deployments differ. Copy the exact URL from the OAuth profile on CIS. - Branch on attestation capability. Don’t. Both
HaapiAccessorBuilder(iOS) andHaapiAccessorFactory(Android) decide the path automatically. Ship one configuration; the SDK ignores the DCR config on attestation-capable devices.