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
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:
MedTracker should become:
This aligns better with a public deployment than using
POST /api/v1/auth/loginas a password-to-token endpoint for mobile clients.Context
Today:
POST /api/v1/auth/login, which directly exchanges email/password for internal API bearer and refresh tokensThat 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:
Non-goals
This issue does not cover:
Proposed architecture
1. External IdP owns authentication
Use Zitadel or another OIDC provider for:
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, andUserdomain recordsadministrator,doctor,nurse,carer,parent,minor)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:
4. MedTracker API integration options
We need to choose one of two server-side models:
Option A: MedTracker validates provider-issued tokens directly
Option B: MedTracker exchanges external identity for internal API sessions
ApiSession)/api/v1/*auth model remains mostly intactRecommendation
Start with Option B unless there is a strong reason to make the API directly consume external JWTs.
Reasoning:
ApiSessionmodelImplementation outline
Phase 1: architecture / ADR
Phase 2: web alignment
Phase 3: mobile auth flow
Phase 4: deprecate password API login
POST /api/v1/auth/loginAcceptance criteria
Related code and docs
app/misc/rodauth_main.rbapp/models/account_identity.rbapp/models/api_session.rbapp/controllers/api/v1/auth/sessions_controller.rbdocs/oidc-setup.mddocs/zitadel-local-testing.mddocs/adrs/0002-authentication-and-authorization-strategy.mddoc/adr/0003-use-rodauth-for-authentication-and-pundit-for-authorization.mdFollow-up tasks likely needed