Documentation Index
Fetch the complete documentation index at: https://docs.synheart.ai/llms.txt
Use this file to discover all available pages before exploring further.
The mobile auth SDK runs an explicit state machine per (app_id, device). Transitions are validated; invalid transitions throw InvalidStateTransition. The state is persisted alongside device_id so the SDK can resume across app restarts.
States
| Dart enum | Wire string | Meaning |
|---|
unregistered | "unregistered" | No device identity exists for this app_id. |
challengeReceived | "challengeReceived" | Challenge fetched from server, awaiting key generation. |
keyReady | "keyReady" | Hardware keypair generated, awaiting attestation. |
registering | "registering" | Attestation proof generated, register call in flight. |
registered | "registered" | Device identity established, ready to sign. |
keyInvalid | "keyInvalid" | OS-detected key invalidation; requires re-registration. |
Allowed transitions
unregistered → challengeReceived
challengeReceived → keyReady
keyReady → registering
registering → registered
registering → unregistered (failure → reset → retry)
registered → registering (key rotation)
registered → keyInvalid (OS detected key gone)
keyInvalid → unregistered (wipe and start fresh)
Any other transition throws InvalidStateTransition.
┌─────────────┐
│ unregistered│ ◄─────────────┐
└─────┬───────┘ │
│ fetch challenge │ wipe
▼ │
┌──────────────────┐ │
│ challengeReceived│ │
└─────┬────────────┘ │
│ generate key │
▼ │
┌──────────┐ │
│ keyReady │ │
└────┬─────┘ │
│ attest + register │
▼ │
┌──────────────────┐ │
│ registering │ ────fail──────────►│
└─────┬────────────┘ │
│ success │
▼ │
┌────────────┐ rotate ┌─────────────┐ │
│ registered │────────►│ registering │──┘
└─────┬──────┘ └─────────────┘
│ OS invalidation
▼
┌────────────┐ wipe ┌─────────────┐
│ keyInvalid │────────►│unregistered │
└────────────┘ └─────────────┘
Mapping to public API
| API call | Triggered transition |
|---|
configure(baseUrl) | (no state change) |
isRegistered(appId) | (no state change) |
registerDevice(appId) step 1 | unregistered → challengeReceived |
| key generation | challengeReceived → keyReady |
| attestation + register call | keyReady → registering |
| register success | registering → registered |
| register failure | registering → unregistered |
rotateKey(appId) | registered → registering → registered |
| OS detected key invalidation | registered → keyInvalid |
resetDeviceIdentity(appId) | any state → unregistered |
Persistence
The current state is stored alongside device_id, key alias, platform, registered_at, key_rotated_at, and clock_offset_ms.
| Platform | Storage |
|---|
| iOS | Keychain (state + metadata) + Secure Enclave (key reference). |
| Android | EncryptedSharedPreferences (state + metadata) + Keystore (key alias). |
Private key bytes are never persisted in app-managed storage — they live only in hardware.
Key invalidation detection (RFC §11.1)
The SDK transitions registered → keyInvalid automatically when the OS reports the key is no longer usable.
| Platform | Detection |
|---|
| Android | KeyPermanentlyInvalidatedException on any Keystore operation; UnrecoverableKeyException when alias no longer resolves. |
| iOS | errSecItemNotFound (key deleted from Secure Enclave); errSecAuthFailed (biometric context changed); LAError.biometryLockout for biometry-bound keys. |
On detection:
- SDK emits
KeyInvalidated error.
- State transitions to
keyInvalid.
- The next
registerDevice(appId) call wipes local state (keyInvalid → unregistered) and starts fresh.
The SDK never silently retries with the invalid key.
State after resetDeviceIdentity
resetDeviceIdentity(appId) is the one-step path from any state back to unregistered. RFC §9.3 restricts when it’s allowed:
- Server explicitly instructs identity reset (e.g.
DEVICE_REVOKED).
- User explicitly requests it (“sign out of all devices”).
- Key invalidation is detected and rotation is impossible.
It MUST NOT be called as a convenience retry. After it returns:
- The hardware key is deleted (best-effort; on hardware failures the alias is abandoned).
device_id and metadata are cleared.
- Next API call requires a full re-registration.
- Registration — happy-path transitions.
- Errors —
KeyInvalidated, InvalidStateTransition, recovery paths.
- Signing — only
registered enables this.