Skip to content

Adopt external OIDC provider for MedTracker web and mobile authentication #1097

@damacus

Description

@damacus

Summary

MedTracker should move toward an external identity provider model for end-user authentication instead of continuing to expand app-local password-to-token flows.

A provider such as Zitadel should own:

  • primary user authentication
  • MFA / passkeys / account recovery
  • OAuth / OIDC authorization flows for clients

MedTracker should become:

  • an OIDC client for the web app
  • an OIDC-integrated mobile backend for the first-party mobile app
  • a resource server for MedTracker API access

This aligns better with a public deployment than using POST /api/v1/auth/login as a password-to-token endpoint for mobile clients.

Context

Today:

  • MedTracker already supports external OIDC login for the web app via Rodauth OmniAuth
  • local testing docs already exist for Zitadel
  • the API still exposes POST /api/v1/auth/login, which directly exchanges email/password for internal API bearer and refresh tokens

That API login path is acceptable as a temporary internal mechanism, but it is not the right public auth model for a first-party mobile app.

Goal

Make an external OIDC provider the primary authentication authority for MedTracker.

Desired end state:

  • users authenticate with Zitadel (or another supported OIDC provider)
  • web login uses the provider through the existing OIDC integration
  • the mobile app authenticates using Authorization Code + PKCE against the external provider
  • MedTracker API accepts identity established through the external provider rather than collecting passwords directly from the mobile app

Non-goals

This issue does not cover:

  • third-party developer API programs
  • public API keys for mobile clients
  • multi-household isolation redesign
  • removing all local account support immediately

Proposed architecture

1. External IdP owns authentication

Use Zitadel or another OIDC provider for:

  • password / passwordless sign-in
  • passkeys / MFA
  • recovery and account security
  • client authorization flows

MedTracker should stop being responsible for first-party mobile credential collection.

2. MedTracker remains responsible for app identity and authorization

MedTracker still owns:

  • Account, Person, and User domain records
  • role mapping (administrator, doctor, nurse, carer, parent, minor)
  • household/domain authorization via Pundit
  • app-local session and API session lifecycle if retained

External identity proves who the user is. MedTracker still decides what they can do.

3. Mobile uses OIDC Authorization Code + PKCE

The first-party mobile app should:

  • open the system browser
  • authenticate with the external provider
  • complete the provider-managed OAuth/OIDC flow with PKCE
  • return to the app with a code/callback

4. MedTracker API integration options

We need to choose one of two server-side models:

Option A: MedTracker validates provider-issued tokens directly

  • Mobile sends provider-issued access token to MedTracker API
  • MedTracker validates issuer, audience, signature, expiry, etc.
  • API authorization proceeds from linked internal account/user records

Option B: MedTracker exchanges external identity for internal API sessions

  • Mobile completes provider auth
  • MedTracker validates the external identity result
  • MedTracker mints internal API access/refresh tokens (likely backed by ApiSession)
  • Existing /api/v1/* auth model remains mostly intact

Recommendation

Start with Option B unless there is a strong reason to make the API directly consume external JWTs.

Reasoning:

  • it fits the existing ApiSession model
  • it preserves current revocation and rotation patterns
  • it keeps MedTracker API token behavior under application control
  • it limits the amount of authorization code that must be rewritten immediately

Implementation outline

Phase 1: architecture / ADR

  • choose Zitadel (or confirm provider abstraction)
  • document whether MedTracker API will validate external tokens directly or exchange them for internal sessions
  • define client inventory: web app, iOS app, Android app
  • define redirect URI strategy (custom scheme vs universal links/app links)

Phase 2: web alignment

  • ensure the existing web OIDC login path is production-ready for the chosen provider
  • document required claims and account-linking behavior
  • define how new/existing MedTracker accounts map to external identities

Phase 3: mobile auth flow

  • register first-party mobile app clients in the IdP
  • implement mobile Authorization Code + PKCE flow
  • add server-side support for identity-to-session bootstrap if using internal API sessions
  • add tests for successful login, invalid issuer/audience, expired tokens, replayed codes, and revoked sessions

Phase 4: deprecate password API login

  • deprecate POST /api/v1/auth/login
  • optionally restrict it earlier to non-production or migration-only use
  • remove it after the mobile app no longer depends on it

Acceptance criteria

  • MedTracker has a documented target architecture where an external OIDC provider is the primary authentication authority
  • Web login is aligned with that provider model
  • The first-party mobile app has a defined PKCE-based auth flow
  • MedTracker API has a clear integration strategy for externally authenticated users
  • The existing password-to-token API login path has a deprecation plan
  • A follow-up ADR/design doc is created before implementation starts

Related code and docs

  • app/misc/rodauth_main.rb
  • app/models/account_identity.rb
  • app/models/api_session.rb
  • app/controllers/api/v1/auth/sessions_controller.rb
  • docs/oidc-setup.md
  • docs/zitadel-local-testing.md
  • docs/adrs/0002-authentication-and-authorization-strategy.md
  • doc/adr/0003-use-rodauth-for-authentication-and-pundit-for-authorization.md

Follow-up tasks likely needed

  • ADR: external identity architecture for MedTracker
  • mobile client registration and redirect URI conventions
  • account linking / provisioning rules
  • API token strategy decision (direct external token validation vs internal session exchange)
  • migration/removal plan for password-based API login

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions