Hey Medplum team, looking for guidance on auth and auditability.
We’re self-hosting Medplum and have our own frontend and backend. The frontend never talks to Medplum directly, everything is proxied through our backend. Until now we’ve used a single Medplum client ID and secret, which works functionally but means History and Blame always show the same actor, so we can’t see which patient or practitioner actually created or edited a resource.
We first tried the OnBehalfOf header, but that only sets metadata on the resource. It doesn’t show up as a real user in History or Blame, and it gets overwritten on each edit so we lose historical attribution.
We’ve now implemented OAuth token exchange in the backend. For each request, we exchange the user’s IdP token for a Medplum token and act as that user. This gives us correct History and Blame, which is exactly what we want. The blocker is that this only works if the user has a ProjectMembership.
From the docs it looks like ProjectMembership can’t be created programmatically and the supported flow is via the invite endpoint. That’s where we’re unsure how to proceed.
We never want patients or practitioners to have direct access to Medplum. No UI access, no password reset, no ability to log in even if they find the Medplum URL. We only want Medplum as a FHIR store with proper audit attribution, with all access enforced by our backend.
So the questions are:
• Is using the invite endpoint the right way to create ProjectMembership for users who will never log in directly?
• Is there a recommended “headless user” pattern for token exchange and audit attribution?
• What’s the best way to prevent direct Medplum login in this setup?
Feels like we’re very close, just want to make sure we’re doing this the intended way.