How to Integrate Risk Assessment#
You want to integrate with a risk-scoring authentication service (BankID is the canonical case) that needs device-context fields — OS name, OS version, device model, application identifier — on every HAAPI request. The SDK collects these fields once at startup and attaches them to outgoing requests; the Curity Identity Server forwards them to the assessment service during flow steps that need them.
For the concept and security trade-offs, see Risk Assessment . For per-field detail and platform asymmetry, see Risk Assessment (SDK Layer) . This page is the end-to-end task walkthrough.
Prerequisites#
Risk assessment is server-driven — the client merely supplies device context. Before configuring the client:
- the Curity Identity Server version 9.7.0 or later. Earlier versions do not support the risk-assessment forwarding protocol.
- A configured risk-assessment integration at the Curity profile level. BankID’s risk-assessment functionality, or another vendor’s equivalent. The integration carries the upstream risk-service URL, threshold settings, and auth-step bindings.
- An authentication flow that includes a risk-assessment step. Risk assessment fires only at specific points in the flow (typically before high-value steps); flows without those steps do not invoke it even when the client-side fields are configured.
Refer to the BankID Relying Party Guidelines for version 6 for the upstream service requirements and acceptance criteria.
Step-by-Step#
1. Decide where to construct the configuration#
- iOS —
RiskAssessmentConfigurationaccessesUIDeviceproperties, which are main-actor-isolated. Build it inAppDelegate.application(_:didFinishLaunchingWithOptions:)(UIKit) or the App’sinit(SwiftUI), not from a background thread. - Android — pass
applicationContextfromApplication.onCreate. The framework reads OS name, version, device model, and package name from that Context internally.
2. Wire it onto your builder#
SDK Layer#
let riskAssessmentConfiguration = RiskAssessmentConfiguration(
operatingSystemName: UIDevice.current.systemName,
operatingSystemVersion: UIDevice.current.systemVersion,
deviceModelName: UIDevice.current.deviceModelName,
applicatonIdentifier: Bundle.main.bundleIdentifier!
)
let haapiConfiguration = HaapiConfiguration(
// ...other configuration
riskAssessmentConfiguration: riskAssessmentConfiguration
)UI Layer#
let riskAssessmentConfiguration = try! RiskAssessmentConfiguration()
let configurationBuilder = HaapiUIKitConfigurationBuilder(/* ... */)
.setRiskAssessmentConfiguration(riskAssessmentConfiguration)Note the SDK-Layer parameter is misspelled in the framework (applicatonIdentifier, missing the second i in “application”); type it verbatim or the code won’t compile. The UI Layer’s parameterless RiskAssessmentConfiguration() reads the same four fields from UIDevice and Bundle.main internally.
SDK Layer#
val haapiConfiguration = HaapiConfiguration(
// ...other configuration
applicationContext = applicationContext
)Android’s risk-assessment wiring is just passing applicationContext. The framework reads OS name (always "Android"), OS version (Build.VERSION.RELEASE), device model (Build.MODEL), and package name (applicationContext.packageName) from that Context.
UI Layer#
WidgetConfiguration.Builder(/* ... */)
.setContext(applicationContext)
.build()Despite the generic-sounding name, setContext is the risk-assessment wiring call on Android — it’s what gives the framework access to the device-context fields it forwards to the assessment service.
React Native has only an SDK Layer surface. Toggle risk assessment via a single boolean per platform — useRiskAssessmentConfiguration: true — on createIOSConfiguration / createAndroidConfiguration. The native bridge handles field collection internally using the same UIDevice (iOS) / Build + Context (Android) lookups documented in Risk Assessment (SDK Layer) .
import {
createIOSConfiguration,
createAndroidConfiguration,
noAuthentication,
} from 'identityserver.haapi.reactnative.sdk'
const iosConfig = createIOSConfiguration({
clientId: 'haapi-ios-client',
appRedirect: 'app://haapi',
clientAuthenticationMethodConfiguration: noAuthentication(),
useRiskAssessmentConfiguration: true,
})
const androidConfig = createAndroidConfiguration({
clientId: 'haapi-android-client',
appRedirect: 'app://haapi',
clientAuthenticationMethodConfig: noAuthentication(),
useRiskAssessmentConfiguration: true,
})There is no JS-facing field selection — the four device-context fields (OS name, OS version, device model, application identifier) are read on the native side. The Android allowBackup consideration in Step 3 applies to React Native apps too.
3. (Android) Confirm android:allowBackup="true" in your manifest#
allowBackup defaults to true on modern Android — leave it alone if you haven’t explicitly set it to false. Some risk-assessment integrations rely on persisted device context surviving backup-and-restore for continuity.
<application
android:name=".ClientApplication"
android:allowBackup="true"
...>
Verify It Works#
- Run a HAAPI flow that includes a risk-assessment step (typically a high-value authentication like BankID).
- Inspect the request payloads at the proxy / server log — the four fields (OS name, OS version, device model, application identifier) should be present.
- Trigger the high-risk scenarios your service detects (unfamiliar device, geo anomaly) and confirm the assessment service returns the expected risk score.
Pitfalls#
- Don’t configure if you don’t use a risk service. Risk assessment adds device-context lookups at startup and per-request payload overhead. Most apps don’t need it. Skip it unless your flow includes a risk-assessment step.
- iOS main-actor isolation.
RiskAssessmentConfigurationmust be built on the main thread because it touchesUIDevice. Constructing it from a background queue results in a runtime crash on Swift 6 actors. - iOS parameter typo is permanent.
applicatonIdentifier(missing onei) is preserved for binary compatibility. Type it verbatim. A future major release may correct it. - Android
allowBackup=falsebreaks continuity. Apps that opted out of backup-and-restore (often for security reasons) lose persisted risk-assessment context across factory resets and device migrations. Some integrations cope; others don’t. Test the end-to-end backup-and-restore path before disablingallowBackup. - Server-side prerequisite missing. Without a configured risk-assessment integration on the CIS profile, the device-context fields are forwarded but never consumed — flows run without a risk score, or fail with a flow-level error when the auth step expects one.