ODK Central customised to have app login users with username, passwords, short term tokens and discouraging concurrent logins

Dear All

Rationale: We need to have a login and pin protection in ODK Collect for our use case. To support those logins, we need to be able to set passwords for AppUsers all the while ensuring weak passwords are not used. While we are at it, why not also capture optionally their phone numbers. Currently it is possible to revoke app users but not restore them… So add restore as well. And while we are at it, let us also ensure good practice that each data collector has a unique username and password and to discourage sharing of passwords, we disable concurrent logins by deactivating older sessions. Of course all this has to be audit logged. Hmm also why not make the session TTL and max number of active sessions configurable.

So better part of today was spent heavily customising ODK Central backend and Vue UI using Codex 5.2 Medium. The fork of ODK Central Server is available at. https://github.com/drguptavivek/central which has two git submodules - server and client typical of ODK central

Short-Lived Token issued for App Users at login

"App User Auth" system, introduces username/password authentication and short-lived session tokens for App Users (Field Keys), replacing the long-lived token model. Ultimate idea is forcing login every few days and PIN every time app relaunches, which may be major deal breakers for ODK usage if sensitive data is being collected.

1. Overview

Legacy ODK Central App Users rely on a long-lived API token (valid for 10+ years) embedded in the QR code. This poses a security risk if the QR code is compromised.

The implementation introduces:

  • Username/Password Authentication: App Users must log in to obtain a session token.
  • Short-Lived Tokens: Session tokens have a configurable expiration (default 3 days).
  • Enhanced Management: Support for phone numbers, password resets, and explicit revocation/restoration.

2. Database Schema

The system extends the field_keys and actors tables using sidecar tables.

vg_field_key_auth

Stores authentication credentials and status for App Users.

CREATE TABLE vg_field_key_auth (
"actorId" integer PRIMARY KEY REFERENCES actors(id),
vg_username text NOT NULL UNIQUE,
vg_password_hash text NOT NULL,
vg_phone text,
vg_active boolean DEFAULT true
);

vg_settings

Stores system-wide configuration for App User sessions.

CREATE TABLE vg_settings (
vg_key_name text PRIMARY KEY,
vg_key_value text
);

Default Values:

  • vg_app_user_session_ttl_days: "3"
  • vg_app_user_session_cap: "2"

vg_app_user_login_attempts

Tracks login attempts for rate limiting and security auditing.

CREATE TABLE vg_app_user_login_attempts (
id serial PRIMARY KEY,
username text NOT NULL,
ip text NOT NULL,
succeeded boolean NOT NULL,
"createdAt" timestamptz DEFAULT now()
);

3. Backend Implementation

Domain Logic (server/lib/domain/vg-app-user-auth.js)

  • Login: Verifies password hash, checks vg_active status, and creates a session using standard Sessions.create.
  • Revocation: Sets vg_active = false and deletes all active sessions for the user.
  • Restoration: Sets vg_active = true.
  • Password Reset: Updates vg_password_hash and terminates existing sessions.

Queries (server/lib/model/query/vg-app-user-auth.js)

  • getSessionTtlDays: Fetches TTL from vg_settings (default 3).
  • getSessionCap: Fetches session cap from vg_settings (default 2).
  • upsertSetting: Inserts or updates configuration keys.

4. Frontend Implementation

Routes (client/src/routes.js)

  • /system/settings: Maps to VgSettings component. Requires config.read and config.set permissions.

Components

client/src/components/system/vg-settings.vue

  • Purpose: Admin interface for configuring session settings.
  • Features:
  • Fetches current TTL and Cap settings.
  • Validates inputs (min 1).
  • Updates settings via PUT /v1/system/settings.
  • Displays success/error alerts.

client/src/components/user/vg-list.vue

  • Purpose: Enhanced list view for App Users.
  • Features:
  • Columns: Display Name, Username, Phone, Created Date.
  • Actions: Revoke Access, Restore Access, Reset Password.

client/src/components/user/vg-new.vue

  • Purpose: Modal for creating new App Users.
  • Features:
  • Auto-generates strong passwords.
  • Validates phone number format (+xx) xxxxxxxxxx.
  • Displays credentials for manual recording.

client/src/components/user/vg-qr-panel.vue

  • Purpose: Displays connection information.
  • Features:
  • QR Code: Contains only Server URL and Project Name (no credentials).
  • Credentials: Displays Username and Password in plaintext for manual entry.

Resources (client/src/request-data/resources.js)

The following endpoints handle App User authentication and management:

Method Path Description Access
POST /v1/projects/:id/app-users/login Authenticate and get session token. Anonymous
POST /v1/projects/:id/app-users/:id/password/change Change own password. Self
POST /v1/projects/:id/app-users/:id/password/reset Reset user password. Admin
POST /v1/projects/:id/app-users/:id/revoke Revoke own sessions. Self
POST /v1/projects/:id/app-users/:id/revoke-admin Revoke user access (deactivate). Admin
POST /v1/projects/:id/app-users/:id/active Restore or deactivate user. Admin
GET /v1/system/settings Get session configuration. Admin
PUT /v1/system/settings Update session configuration. Admin
  • systemSettings: App-wide resource for fetching/updating session config.

5. Security Enhancements

Secure QR Code

The configuration QR code no longer contains credentials.

  • Legacy: Encoded full credentials (token or username/password).
  • New: Encodes only Server URL and Project Name.
  • Flow: User scans QR -> ODK Collect prompts for Username/Password -> User enters credentials manually.

Password Policy

  • Requirements: At least 1 uppercase, 1 lowercase, 1 digit, 1 symbol.
  • Generator: Auto-generates strong passwords (format: Word-Word-123-Word) during creation and reset.

NEXT STEPS

Collect will have a wrapper Login and password system asked by PIN
Part work done for another custom API - https://github.com/drguptavivek/collect

Cheers

Dr Vivek Gupta

1 Like

I now also have working version of the collect app for it.
Essentially the use case is when you want to restablish and revalidate trust between device and server.
In doing so, we break away from the multi project capability of ODK collect. App users in Central are specific to one project. Multi project means multiple user ids. So since we are revalidating app user tokens every couple of days, the app user needs to relogin. Further there is a PIN specific to the app user that gets set. So the app virtually becomes like linked to that app user and thereby the project of the app user.
In case of repeated incorrect PINs, the session token gets deleted from the app and relogin is forced.
There’s a possibility that user may also log out.
In case of logout, all blank forms are cleared out but noth the filled forms. After relogin, forms need to be re-downloaded.
Any filled older forms get uploaded successfully so long as current used has server grants to that form. Non allowed forms with data fail uploads and stay in device.
Server will store max two sessions for an app user , configurable.
I still haven’t tried QR based server setup in app.
Password is never saved in app. Its just used for token generation.
I am mulling following features

  1. A project manager should be able to validate a device on which app is installed by scanning a device QR
  2. Show number of sessions and devices active for a app user in Central
  3. Show token validity timer in app with notifications requesting user to revalidate their session.

Ideas and suggestions welcome

Best Wishes
Dr Vivek Gupta

2 Likes

The MEDRES ODK collect fork has a RC. Full details and screenshots are at the repo.

The application is hardened against common exploit vectors:

  • PIN Bypass Protection: Mandatory re-auth on cold starts or background resume.

  • Shared-Device Isolation: Automatic PIN clearance on user change.

  • System Resilience: Fail-safe logout fallbacks for storage corruption or network races.

  • API Guard: Sync for concurrent operations and rate limiting for auth calls.

For a full catalog of protected scenarios, see Security Scenarios & Resilience.

Technical Architecture

The architecture is built on a clean separation between the AIIMS Auth Module and the ODK Core.

Component Responsibility
AIIMS Auth Module Handles login, token management, and security lifecycle.
ODK Core Manages form rendering, logic, and data storage.

Fork of ODK Central

This is a fork of the upstream ODK Central meta repo (client/ + server/submodules):

  • Upstream: getodk/central

  • This fork focuses on operational security for App Users (Field Keys), and enabling an ODK Collect workflow with login + PIN protection.

What’s included in this fork

  • Central backend (server/): App User Auth (username/password + short-lived sessions), session caps/TTL, lockouts, audit logging, telemetry capture + admin listing, and web-user /v1/sessions hardening.

  • Central frontend (client/): Admin UX for app users (username/phone, create/reset/revoke/restore), System + Project settings UI (TTL/cap/admin_pw), and session/device visibility.

Why this fork exists

Upstream ODK Central Mobile App Users rely on a long-lived API token that can be shared via QR code. For some deployments, that model is too risky operationally (tokens are effectively permanent if leaked), and it does not support the “login + PIN” style protection we need in ODK Collect.

Rationale for this fork:

  • We need login and PIN protection in ODK Collect for our use case (revalidate trust between device and server).

  • To support login, Central needs app-user passwords with strong password policy(reject weak passwords).

  • We also want optional phone numbers for app users.

  • Upstream supports revocation; we also need restore/reactivate App users to facilitate their movement / field operations.

  • We want to discourage credential sharing: enforce unique username per collector and limit concurrent sessions (revoke older sessions on login).

  • All of this must be audit logged.

  • Session TTL and maximum active sessions must be configurable.

This fork replaces the long-lived token model with an explicit login/session model for app users: username/password → short-lived bearer token, with server-enforced Collect app session validity (TTL) and concurrent app session caps.

Operational differences (high level)

  • App user auth model: Username/password login issues a short-lived bearer token; tokens are not returned from create/list endpoints.

  • Token lifecycle: Fixed expiry (no sliding refresh) of App user issued lokens based on vg_app_user_session_ttl_days (default 3days).

  • Session caps: Server enforces vg_app_user_session_cap (default 3); older sessions are revoked on login when the cap is exceeded.

  • Admin controls: Reset/change password, revoke sessions, deactivate/reactivate, session listing with metadata.

  • Security UX: QR codes are for configuration (not embedded credentials); managed QR can include admin_pw for Collect settings lock.

  • Web user Login hardening: Additional protections for /v1/sessions (lockouts, auditing, response behavior).

  • Telemetry: App-user telemetry capture + admin listing.

Benefits for survey data managers / investigators

This model is designed to improve operational control and accountability in the field, especially for sensitive studies and shared-device deployments:

  • Re-establish trust periodically: short-lived tokens force revalidation every few days (configurable), reducing the blast radius of leaked credentials.

  • Stronger identity and accountability: unique usernames per collector, auditable login/session activity, and clearer linkage between submissions and an authenticated field worker.

  • Rapid response to risk: deactivate/reactivate users, revoke sessions, clear lockouts, and rotate passwords without having to rotate long-lived tokens.

  • Reduced credential sharing incentives: session caps and older-session revocation discourage concurrent sharing of a single account.

  • Operational visibility: session/device metadata (and optional telemetry/map views) help supervisors detect suspicious patterns, device churn, and support field issues faster.

  • Configurable policy: TTL and max active sessions can be tuned per deployment (and per project where supported) instead of being “one size fits all”.

  • Device-side safety controls (Collect): PIN lock on app relaunch/backgrounding and brute-force protection (session wipe after repeated PIN failures) reduce risk on lost/shared devices.

  • Server-side brute-force protection: app-user login attempts are tracked and can trigger username+IP lockouts after repeated failures within a configurable window, limiting password-guessing attacks.

2 Likes