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/loginwith{ email, password, device_id?, device_name?, device_os?, push_token? }. Returns{ data: ClaimedProfile[] }, one profile per workspace membership.POST /mobile-app/auth/pairwith{ code, device_id?, device_name?, device_os?, push_token? }. Returns tokens for the paired workspace user.POST /mobile-app/auth/refreshwith{ refresh_token }. Returns a new access/refresh token pair for the same session.
Mobile-token endpoints:
GET /mobile-app/mePOST /mobile-app/auth/logoutGET /workspace/:workspaceId/mobile-appGET /workspace/:workspaceId/mobile-app/passes/searchGET /workspace/:workspaceId/mobile-app/passes/lookupGET /workspace/:workspaceId/mobile-app/passes/:passId/historyGET /workspace/:workspaceId/mobile-app/passes/:passId/loyaltyGET|POST /workspace/:workspaceId/mobile-app/passes/:passId/transactionsGET /workspace/:workspaceId/mobile-app/passes/:passId/ledgerGET /workspace/:workspaceId/mobile-app/passes/:passId/rewardsGET /workspace/:workspaceId/mobile-app/passes/:passId/redemptionsPOST /workspace/:workspaceId/mobile-app/passes/:passId/redemptions/:redemptionId/usePOST /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 throughlinked_user_idtodirectus_users.mobile_app_pairing_codes: six-digit pairing codes, always scoped to amobile_app_userand purposepair.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_identitiesmobile_app_users.identity_idmobile_app_pairing_codes.identity_idverify_emailandreset_passwordmobile 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 workflowsThere 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.