Skip to Content
MobileAuthentication

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:

  1. response.token → signed in. The token is stored and the app routes to the home tabs (or a required-action screen).
  2. response.requiresTotp / requiresTotpSetup → the admin account (role_id 1) must complete authenticator TOTP. The app shows the TOTP screen and calls POST /auth/login/verify-totp.
  3. response.two_fa === 1 or enable2fa === true && status === true → the member has 2FA enabled (or a passcode was just sent); show the OTP (SMS/email passcode) screen.
  4. 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 1 always 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 the TwoFaRequiredScreen (driven by member.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 AsyncData and refreshed from the token response header on each API call.
  • A 401 from any endpoint triggers a full logout via the ApiServices interceptor (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.
Last updated on