Authentication
The server-side rules are documented once in Business logic → Authentication flow. This page describes how the mobile client drives them.
Login response shapes
POST /auth/login returns one of several shapes; useLogin.handleLogin
branches on them, in order:
response.token→ signed in. The token is stored and the app routes to the home tabs (or a required-action screen).response.requiresTotp/requiresTotpSetup→ the admin account (role_id 1) must complete authenticator TOTP. The app shows the TOTP screen and callsPOST /auth/login/verify-totp.response.two_fa === 1orenable2fa === true && status === true→ the member has 2FA enabled (or a passcode was just sent); show the OTP (SMS/email passcode) screen.- anything else → generic “Invalid username or password”.
[!NOTE] Because branch 4 is a catch-all, any login response shape the app doesn’t recognise surfaces as “Invalid username or password” — which is why a server change that introduces a new required step must ship alongside a matching app build.
Two-factor authentication
- Members — OTP. A 6-digit passcode sent by SMS or email; the member is challenged at most once per 8-hour window (enforced server-side).
- Admin — TOTP.
role_id 1always uses an authenticator app and never falls through to OTP. - Mandatory staff 2FA. Instructors/trainers/examiners holding instructor
currency, plus the boss (
role_id 11) and tunnel managers (role_id 12), are required to enable 2FA. On mobile this surfaces as theTwoFaRequiredScreen(driven bymember.alerts.requireTwoFa), which routes the member to the web portal to switch 2FA on — enabling it in-app isn’t supported, mirroring the web’s soft-gate redirect. See the business-logic rule.
Biometric login
biometricService stores the member’s credentials in the Keychain/Keystore
under ACCESS_CONTROL.BIOMETRY_ANY, keyed to the device. On the login screen,
handleBiometricLogin retrieves them (prompting Face/Touch ID / fingerprint)
and re-runs the normal login.
Failure handling is deliberately classified so it doesn’t create Sentry noise or strand the user:
- Cancelled prompt → silently ignored.
- Key invalidated (
CryptoFailedException: Authentication tag verification failed— e.g. after a biometric re-enrolment or OS change) → the stored entry is cleared and the member falls back to password, then can re-enrol. - Other auth failures → logged as a warning, entry kept for retry.
Sessions
- The JWT is persisted via
AsyncDataand refreshed from thetokenresponse header on each API call. - A
401from any endpoint triggers a full logout via theApiServicesinterceptor (explicit: false, so biometric credentials are preserved for re-auth). - An in-app inactivity timer (default 30 min) also logs out. See Architecture → Auth context.