Mobile Authenticator & Recovery
Authenticator enrollment, recovery posture, and the human workflow around stronger sign-in assurance.
Scope
Strong authentication only works when enrollment, recovery, and support workflows are clear to the people who administer them. This guide keeps the operator-facing identity assurance model and removes the low-level implementation detail used by the internal product manual.
Current Scope
Portal now ships two first-party native app projects:
ios/PortalAuthenticatorandroid/PortalAuthenticator
Both apps now truthfully ship the same narrow mobile slice:
- local manual TOTP entry, edit, delete, and QR import
- local rolling-code display and on-device vault persistence
- optional local biometric or device-auth gate over already-stored app state
- fixed Cadres development Portal web and API endpoints
- generate and keep a device-bound Ed25519 key on the device
- complete the browser-issued linked-device challenge
- inventory linked devices
- revoke linked devices
- load a narrow governance approvals inbox and detail
- approve or deny governance access requests through the linked-device action-challenge flow
- report public-vault readiness and blocker state as groundwork only
Portal’s backend now also ships the next mobile-safe workflow contract:
- governance requester catalog, submit, history, and detail facades
- governance approval and access-review read facades
- support-access read facades
- device-authenticated action challenges for governance, access-review, and support-access decisions
The current native apps consume only the governance approval subset of those workflow routes. Governance requester UI, access-review UI, and support-access UI remain non-shipped mobile scope.
What this wave still does not ship:
- push approvals
- public-vault account bootstrap or sync
- backup or recovery
- governance requester UI, access-review UI, or support-access UI
- mobile session review or revoke
Pair A Device
The current branch still does not ship a browser pairing issuer UI. Challenge issuance still happens through the authenticated contract from a normal Portal browser session.
Workflow:
- Open either native app:
- iOS: run
ios/PortalAuthenticatorfrom Xcode on an iPhone or simulator - Android: open
android/PortalAuthenticatorin Android Studio and run it on an Android 13+ device or emulator, or use the checked-in Gradle wrapper from the repo root - Use the app’s fixed Cadres development environment configuration.
- From an authenticated Portal browser session in a normal membership context, issue a link challenge.
- Paste the returned
challenge_tokeninto the native app. - Review or edit the device label and complete
Link Device.
Expected result:
- the native app signs the canonical pairing payload with its local Ed25519 key
- Portal records the linked device and writes audit action
auth.mobile_device_linked
Sign In To Manage Linked Devices
The app now uses Portal’s real first-party native auth path:
- open the
Devicestab - review the fixed Cadres development Portal web and API endpoints shown in the app
- tap
Sign In With Portal - complete the browser-based Portal login, MFA, tenant selection, or consent flow inside the native browser handoff:
- iOS uses
ASWebAuthenticationSession - Android uses Custom Tabs plus the app’s deep-link callback
- Portal redirects back to the app’s registered callback URL
After sign-in, the Devices tab can:
- list the authenticated user’s linked devices for the selected tenant context
Operational Notes
- The iOS app stores the Ed25519 private key in Keychain with device-only accessibility. It does not claim Secure Enclave backing in this tranche.
- The Android app stores a software-generated Ed25519 private key encrypted at rest with an Android Keystore wrapping key. It does not claim hardware-backed or StrongBox-backed Ed25519 in this tranche.
- The native apps use Portal’s real browser-based sign-in, not embedded fake login forms.
- The backend accepts the native token path only on the explicit linked-device-management and mobile-workflow facade routes. Other browser-session-sensitive self-service routes still rely on the existing
portal_sessioncontract. - High-value mobile workflow decisions now require both the canonical Portal identity checks and a one-time linked-device action challenge signed by the exact active device key.
- Both apps now ship local biometric or device-auth gating only over already-stored app state. That local unlock is not a second auth model and not a permission bypass.
- Neither app ships push, camera, notification, public-vault account bootstrap, backup, recovery, requester workflow UI, access-review UI, support-access UI, or native session review in this wave.