(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
InvalidStateTransition.
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 alongsidedevice_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). |
Key invalidation detection (RFC §11.1)
The SDK transitionsregistered → 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. |
- SDK emits
KeyInvalidatederror. - State transitions to
keyInvalid. - The next
registerDevice(appId)call wipes local state (keyInvalid → unregistered) and starts fresh.
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.
- The hardware key is deleted (best-effort; on hardware failures the alias is abandoned).
device_idand metadata are cleared.- Next API call requires a full re-registration.
Related
- Registration — happy-path transitions.
- Errors —
KeyInvalidated,InvalidStateTransition, recovery paths. - Signing — only
registeredenables this.