Skip to content

Mobile App Authentication

WalletHero’s staff mobile app uses workspace-scoped mobile sessions. The separate mobile_app_identities identity layer was removed; mobile users now authenticate either with an existing Directus account or with a workspace pairing code.

Overview

Every active mobile session belongs to one mobile_app_user, one workspace_id, and one mobile_app_sessions row. The app stores one local profile per workspace/API URL and sends the profile’s access token in X-Mobile-Token.

Password login authenticates a directus_user, finds the workspaces that user belongs to, and provisions or updates one linked mobile_app_user per workspace. Pairing code login remains available for staff devices that are created from the mobile users admin screen.

Environment

MOBILE_APP_JWT_SECRET is required in directus-api/extensions/wallethero-api. It signs both mobile access tokens and refresh tokens. Access tokens expire after 15 minutes; refresh/session expiry is 30 days.

The removed identity/SSO flow no longer uses GOOGLE_CLIENT_ID or APPLE_CLIENT_ID in the mobile app auth path.

API Reference

All endpoints are mounted under /wallethero-api.

Public credential-issuance endpoints:

  • POST /mobile-app/auth/login with { email, password, device_id?, device_name?, device_os?, push_token? }. Returns { data: ClaimedProfile[] }, one profile per workspace membership.
  • POST /mobile-app/auth/pair with { code, device_id?, device_name?, device_os?, push_token? }. Returns tokens for the paired workspace user.
  • POST /mobile-app/auth/refresh with { refresh_token }. Returns a new access/refresh token pair for the same session.

Mobile-token endpoints:

  • GET /mobile-app/me
  • POST /mobile-app/auth/logout
  • GET /workspace/:workspaceId/mobile-app
  • GET /workspace/:workspaceId/mobile-app/passes/search
  • GET /workspace/:workspaceId/mobile-app/passes/lookup
  • GET /workspace/:workspaceId/mobile-app/passes/:passId/history
  • GET /workspace/:workspaceId/mobile-app/passes/:passId/loyalty
  • GET|POST /workspace/:workspaceId/mobile-app/passes/:passId/transactions
  • GET /workspace/:workspaceId/mobile-app/passes/:passId/ledger
  • GET /workspace/:workspaceId/mobile-app/passes/:passId/rewards
  • GET /workspace/:workspaceId/mobile-app/passes/:passId/redemptions
  • POST /workspace/:workspaceId/mobile-app/passes/:passId/redemptions/:redemptionId/use
  • POST /mobile-app-actions/:id/execute

Access Rules

mobileAppAccountabilityMiddleware validates the JWT, checks the DB session is not revoked or expired, rejects URL workspace mismatches, and sets req.mobileAppUser. If the mobile user is linked to a Directus user, the request gets that user’s accountability; otherwise it gets mobile-session accountability only.

Runtime mobile endpoints require an enabled mobile_app_configs row for the workspace. project_ids limits pass search, lookup, action execution, history, loyalty, transactions, ledger, rewards, and redemptions. hidden_fields filters client custom fields in pass search and lookup results.

allowed_role_ids on configs or actions requires a linked Directus workspace role. Unlinked paired users can use unrestricted configs/actions, but they cannot bypass role-restricted ones.

Data Model

Current mobile auth tables:

  • mobile_app_configs: one enabled/disabled config per workspace, plus project scope, role scope, hidden fields, and app config JSON.
  • mobile_app_actions: child rows for configured staff actions.
  • mobile_app_users: per-workspace staff mobile user, optionally linked through linked_user_id to directus_users.
  • mobile_app_pairing_codes: six-digit pairing codes, always scoped to a mobile_app_user and purpose pair.
  • mobile_app_sessions: SHA-256 hash of the current refresh token (refresh_token_hash — the plaintext is never stored), device metadata, activity, expiry, and revocation state.

Removed by directus-api/migrations/20260605A-drop-mobile-app-identities.mjs:

  • mobile_app_identities
  • mobile_app_users.identity_id
  • mobile_app_pairing_codes.identity_id
  • verify_email and reset_password mobile pairing-code purposes

Mobile App Flow

LoginScreen
  ├── Email + password → /mobile-app/auth/login → one profile per workspace
  └── Pairing code → /mobile-app/auth/pair → one paired workspace profile

Active profile
  ├── /mobile-app/me → current mobile user and workspace
  ├── /workspace/:workspaceId/mobile-app → enabled config/actions
  └── pass/action endpoints → staff workflows

There is no mobile self-registration, email verification, password reset, SSO, identity token, or profile-claim flow in the current architecture.

Troubleshooting

"Mobile app JWT secret not configured" Set MOBILE_APP_JWT_SECRET and restart Directus.

No profiles appear after password login Verify the Directus user is active and belongs to at least one workspace. Suspended existing mobile_app_users are skipped.

A paired user cannot see the app Verify the workspace has an enabled mobile app config. If the config is role-restricted, the mobile user must be linked to a Directus workspace user with an allowed role.

Pass search or actions return "Mobile app config not found or disabled" Enable the workspace mobile app config, or create it from the frontend Mobile App settings page.

WalletHero Documentation