Initialization
Open the SenseCrypt Authenticator SDK, configure the portal and tenant, and load the on-device inference engine.
Initialization has two independent steps, deliberately separated:
open()— wires platform key storage, biometrics, and attestation into the Rust core and returns yourSenseCryptAuthenticatorhandle. Cheap.initializeInferenceEngine()— parses the license and loads the face detection, matching, and liveness models. Costs 200–500 ms of model-load wall time.
They are separate because the authentication flows that don't touch a camera — key rotation, revocation, listing keys — need only step 1. Run step 2 during your splash screen so the cost is paid before the user taps anything.
Opening the SDK
import SenseCryptAuthenticatorSDK
let authn = try SenseCryptAuthn.open(
config: AuthenticatorConfig(
portalUrl: "https://your-tenant.sensecrypt.com",
appLabel: "Acme",
tenantId: nil,
enableLogging: false
)
)The entry-point enum is SenseCryptAuthn, not SenseCryptAuthenticatorSDK. The Swift module is already named SenseCryptAuthenticatorSDK, and an enum sharing that name would shadow the module identifier — so qualifying a bindgen type as SenseCryptAuthenticatorSDK.Key would resolve to the enum instead of the module. The shorter name keeps both reachable.
import ai.seventhsense.sensecrypt.authenticator.SenseCryptAuthenticatorSDK
import ai.seventhsense.sensecrypt.authenticator_ffi.AuthenticatorConfig
val authn = SenseCryptAuthenticatorSDK.open(
context = context.applicationContext,
config = AuthenticatorConfig(
portalUrl = "https://your-tenant.sensecrypt.com",
appLabel = "Acme",
tenantId = null,
enableLogging = BuildConfig.DEBUG,
),
playIntegrityCloudProjectNumber = BuildConfig.PLAY_INTEGRITY_CLOUD_PROJECT_NUMBER,
)Android's open() takes two extra parameters beyond context and config, both with defaults:
| Parameter | Type | Purpose |
|---|---|---|
scanTheme | Int | Theme resource applied to the bundled scan surfaces. |
playIntegrityCloudProjectNumber | Long | Google Cloud project number used for Play Integrity device attestation. |
Pass context.applicationContext, not an Activity — the handle outlives any single Activity.
Open once, keep it for the process lifetime. The handle owns the Rust core, the inference engine cache, and the signing key access path. Hold it in your application object or a singleton — do not open per screen.
AuthenticatorConfig
| Field | Type | Meaning |
|---|---|---|
portalUrl | String | Base URL of the SenseCrypt portal, no trailing slash. Every flow's HTTP call is relative to this origin. |
appLabel | String | Human-readable product label. Appears in audit-log entries and in bundled interstitial UI. |
tenantId | String? | Tenant the portal should route flows to. null means the default tenant. |
enableLogging | Bool | Opt-in SDK log output, from both the Rust core and the platform wrapper. Defaults to false. |
enableLogging defaults to off so a host app that never sets it — which is every release build — emits nothing to logcat or the Xcode console. Pass your debug flag (BuildConfig.DEBUG, #if DEBUG). Panic reporting stays on regardless of this setting.
Failures from open()
open() throws AuthnInitError:
| Case | Meaning |
|---|---|
InvalidPortalUrl(url, details) | portalUrl could not be parsed. Check for a trailing slash or a missing scheme. |
SecureHardwareUnavailable | The device has no usable secure key store. The SDK cannot operate; surface a terminal message. |
BiometricNotEnrolled | No device biometric is enrolled. Prompt the user to set one up in system settings, then retry. |
Internal(details) | Unexpected core failure. |
Loading the inference engine
do {
try await authn.initializeInferenceEngine()
} catch let error as SenseCryptInitError {
// See the table below.
}try {
authn.initializeInferenceEngine(context)
} catch (e: SenseCryptInitException) {
// See the table below.
}initializeInferenceEngine is a suspend function and takes a Context on Android; the iOS equivalent takes no argument.
This call stages the SDK's bundled model assets and your mobile.lic into app-private storage, then constructs the capture engine — parsing the license and loading the detection, recognition, and liveness pipelines.
It is idempotent: a second call after success is a no-op. Construction runs off the main thread, so it will not block your UI dispatcher during the 200–500 ms model load. On a first run the asset staging step adds noticeably more — expect several seconds on a slow device or an emulator, since the models are copied out of the package before anything loads.
Failures from initializeInferenceEngine()
Throws SenseCryptInitError (Swift) / SenseCryptInitException (Kotlin):
| Case | Meaning | What to show the user |
|---|---|---|
LicenseNotFound(path) | No mobile.lic in the app bundle or assets. | Nothing — this is a build/packaging bug. Fix the project. |
LicenseInvalid(reason) | Present, but parse or signature verification failed. | A terminal error. The file is corrupt or not yours. |
LicenseExpired | Parsed correctly but past its expiry date. | "Update your app" — not a support dead end. A current release ships a fresh license, so an app update is the fix. |
ModelLoad(reason) | A bundled model is missing or corrupt. | Terminal; indicates a damaged artifact. |
LivenessInit(reason) | The liveness pipeline's data is missing or corrupt. | Terminal; indicates a damaged artifact. |
Treat LicenseExpired distinctly from the other four. The others are your bugs; this one is expected end-of-life behaviour on an old install, and the correct UX is an update prompt.
Reading device state
Once open, the handle exposes what this device already holds — useful for deciding whether to show enrollment or sign-in:
let keys: [Key] = authn.keys
let current: Key? = authn.currentKey
let branding: Branding? = authn.brandingFor(keyId: someKeyId)
let entries: [AuditEntry] = authn.auditLog(limit: 50)val keys: List<Key> = authn.keys
val current: Key? = authn.currentKey
val branding: Branding? = authn.brandingFor(keyId)
val entries: List<AuditEntry> = authn.auditLog(limit = 50)keys is filtered to keys that are actually usable: records whose local hardware key was wiped are dropped, and keys surviving a reinstall are cleared by open() as a clean-slate measure. An empty list means this device has no enrollment and you should start a registration flow.
See Reference for the fields on Key, Branding, and AuditEntry.
Related
Install
Add the SenseCrypt Authenticator SDK to an iOS or Android project — artifact placement, the development license, required dependencies, and camera permissions.
Login flow
Approve a sign-in with the SenseCrypt Mobile SDK — scan the QR, handle an optional password step, capture a face, and post the result.