Exchange practitioner session code for tokens
- Sandbox: available
- Staging: available
- Production: unknown
Finishes a practitioner login into one tenant: binds the session to the practitioner's membership in that tenant, then exchanges the session code and your PKCE codeVerifier for an access token and a refresh token.
Auth: none. The session code and code verifier prove the earlier steps.
Scope: the project that owns tenantCode. Pick it from the profiles returned by login/start or login/mfa.
Behaviour
PRACTITIONER_LOGIN_ENABLEDon the chosen project must not befalse(it defaults to on).- The practitioner must have a membership in the chosen tenant. Ovok points the pending login at that membership before the code is redeemed, so the tokens are scoped to that project.
- Ovok redeems the code with Medplum's
oauth2/tokenendpoint (grant_type=authorization_code). - If the account was scheduled for deletion (
DELETE /auth/delete), logging in cancels the deletion. - The response holds
accessToken,refreshToken,expiresIn(seconds) and references to theprojectandprofile. - Error responses are delayed to take at least one second.
Example
curl -X POST 'https://api.sandbox.ovok.com/auth/tenant/Practitioner/login/token' \ -H 'Content-Type: application/json' \ -d '{ "sessionCode": "<sessionCode from login/start>", "codeVerifier": "<your codeVerifier>", "tenantCode": "my-medical-project" }'application/json- body
sessionCode*stringcodeVerifier*stringtenantCode*stringAccess token, refresh token, expiry, project and profile.
application/json- response
accessToken*stringBearer access token. Expires in 1 hour.
refreshToken?stringRefresh token.
expiresIn?integerToken expiration.
3600project*profile*curl -X POST 'https://api.sandbox.ovok.com/auth/tenant/Practitioner/login/token' \ -H 'Content-Type: application/json' \ -d '{ "sessionCode": "string", "codeVerifier": "string", "tenantCode": "string"}'{ "accessToken": "string", "refreshToken": "string", "expiresIn": 3600, "project": { "reference": "string", "display": "string" }, "profile": { "reference": "string", "display": "string" }}Start practitioner login POST
Starts a practitioner login: checks the email and password and returns either a session code plus the list of tenants the practitioner can enter, or, when the user has MFA enrolled, a login ID for the MFA step. **Auth:** none. **Scope:** no tenant yet. A practitioner's User is server-wide; the tenant is chosen in `login/token`. ## Behaviour - This is step 1 of a PKCE login. Generate a random `codeVerifier`, send its S256 hash as `codeChallenge` here, and send the `codeVerifier` itself to `POST /auth/tenant/Practitioner/login/token`. - Response `nextStep`: - `token`: no MFA. The response carries `sessionCode` and `profiles`, one entry per Practitioner membership of the user: its `tenantCode`, `project` (`id`, `resourceType`, `name`), `profile`, `admin` (true when the user is an admin of that project), `isMainProject` and `parentProjectId`. - `mfa`: the user has MFA enrolled. Pass `loginId` and the current TOTP code to `POST /auth/tenant/Practitioner/login/mfa`, which returns the same `profiles` list. - `isMainProject` is `true` when the project has no parent project, and `null` when the hierarchy could not be read. `parentProjectId` is set only when the caller is also a member of that parent; otherwise it is `null`. - `PRACTITIONER_LOGIN_ENABLED` is not checked here; it is checked in `login/token` against the chosen tenant. - A wrong email, an unknown account, an account without a password and a wrong password all give the same 401 `Invalid email or password.` - After 5 failed sign-ins for one email within 15 minutes, that email's sign-ins answer 429 with `Retry-After` for 15 minutes, the right password included, whether or not the email has an account. A successful sign-in resets the count. - Error responses are delayed to take at least one second. ## Example ```bash curl -X POST 'https://api.sandbox.ovok.com/auth/tenant/Practitioner/login/start' \ -H 'Content-Type: application/json' \ -d '{ "email": "max.mustermann@example.com", "password": "your-password", "codeChallenge": "<base64url S256 hash of your codeVerifier>" }' ```
Verify practitioner MFA code POST
Verifies the TOTP code of a practitioner who has MFA enrolled, after `login/start` answered `nextStep: "mfa"`. Returns the session code for `login/token` and the list of tenants the practitioner can enter. **Auth:** none. The `loginId` from `login/start` identifies the login. ## Behaviour - `mfaToken` must equal the current TOTP code generated from the user's MFA secret. - On success the login is marked MFA-verified and the response is `{ "nextStep": "token", "sessionCode": ..., "profiles": [...] }`. `profiles` is the same list `login/start` returns without MFA. - Pass `sessionCode` and one `tenantCode` from `profiles` to `POST /auth/tenant/Practitioner/login/token`. - Error responses are delayed to take at least one second. ## Example ```bash curl -X POST 'https://api.sandbox.ovok.com/auth/tenant/Practitioner/login/mfa' \ -H 'Content-Type: application/json' \ -d '{ "loginId": "c3499c27-2973-0a7f-807e-fb8676a92dcb", "mfaToken": "123456" }' ```