Skip to main content

Practitioner sign-in

A practitioner has one account across Ovok, with one email and one password, and a membership in each project they belong to. Sign-in therefore has a step that patients do not have: the practitioner signs in first, then chooses a tenant.

StepMethodPathTenant needed?
1POST/auth/tenant/Practitioner/login/startNo
2, only with MFAPOST/auth/tenant/Practitioner/login/mfaNo
3POST/auth/tenant/Practitioner/login/tokenYes: tenantCode

None of the three needs a bearer token.

Before you start​

You needWhere it comes from
A practitioner account with a membership in the projectPractitioner registration or a practitioner invitation.
Practitioner sign-in switched onPRACTITIONER_LOGIN_ENABLED must not be false for the chosen project. It is on when unset.

Create the code verifier and challenge exactly as in patient sign-in, step 0.

Step 1: Start sign-in​

Checks the email and password. No tenant yet.

curl --request POST \
--url 'https://api.sandbox.ovok.com/auth/tenant/Practitioner/login/start' \
--header 'Content-Type: application/json' \
--data '{
"email": "practitioner@example.com",
"password": "your-password",
"codeChallenge": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
}'
Body fieldDescription
emailThe practitioner's email. Ovok lowercases it.
passwordThe practitioner's password.
codeChallengeThe hash of your code verifier.

Without MFA, the answer carries the session code and every tenant the practitioner can enter:

Response fieldExampleMeaning
nextSteptoken or mfaTells the app whether to continue to token exchange or verify the one-time code.
sessionCodeTemporary codePresent when nextStep is token; send it with the verifier in step 3.
loginIdUUIDPresent when nextStep is mfa; send it with the one-time code in step 2.
profiles[]One entry per tenantMemberships the practitioner can choose from.

With MFA enrolled, nextStep is mfa and the response includes loginId instead of sessionCode and profiles.

Building the tenant chooser​

profiles fieldMeaning
tenantCodeSend this in step 3.
projectid, resourceType and name of the project, for display.
profileThe practitioner's profile in that project.
isMainProjecttrue when the project has no parent. null only when the hierarchy could not be read.
parentProjectIdThe parent project, so you can group sub-tenants beneath it. Set only when the practitioner is also a member of that parent, otherwise null.

Use isMainProject, not parentProjectId, to decide whether a project is a main account. If isMainProject is null, show a flat list.

StatusMessageCause
400Username or password is incorrect.A wrong email, an unknown account, an account without a password, or a wrong password. All give the same answer on purpose.
422A list of path and messageA body field is missing or invalid.
429More than 10 requests in a minute.

Step 2: Verify the one-time code (only with MFA)​

curl --request POST \
--url 'https://api.sandbox.ovok.com/auth/tenant/Practitioner/login/mfa' \
--header 'Content-Type: application/json' \
--data '{"loginId":"8c1e5a52-6f0b-4d9e-9a41-0b2f7c3d9e10","mfaToken":"123456"}'
Body fieldDescription
loginIdThe loginId from step 1. A UUID.
mfaTokenThe current one-time code, at least 6 characters.

On success the answer is { "nextStep": "token", "sessionCode": "…", "profiles": [ … ] }, with the same profiles list as step 1.

StatusMessageCause
400Invalid MFA token or user.The code is wrong or expired, or the loginId is unknown.
422A list of path and messageloginId is not a UUID, or mfaToken is shorter than 6 characters.
429More than 5 requests in a minute.

Step 3: Choose a tenant and get tokens​

curl --request POST \
--url 'https://api.sandbox.ovok.com/auth/tenant/Practitioner/login/token' \
--header 'Content-Type: application/json' \
--data '{
"sessionCode": "<sessionCode from step 1 or 2>",
"codeVerifier": "<your code verifier>",
"tenantCode": "big-health-company"
}'

Ovok binds the session to the practitioner's membership in that tenant before it issues tokens, so the tokens are scoped to that one project.

Response fieldExampleMeaning
accessTokenToken stringBearer token for API calls.
refreshTokenToken stringToken used to renew the session.
expiresIn3600Access-token lifetime in seconds.
project.reference, project.displayProject/<id>, Big Health CompanyProject the tokens are for.
profile.reference, profile.displayPractitioner/<id>, Example PractitionerPractitioner profile that signed in.
StatusMessageCause
404Tenant not found.The tenant code is wrong.
403Login is not enabled for this project.PRACTITIONER_LOGIN_ENABLED is false for that project. Checked before the session code is used.
404Login not found.The session code is unknown.
404Account not found.The practitioner has no membership in the chosen tenant.
400Code verification failed.The codeVerifier does not match the challenge.
429More than 30 requests in a minute.

To switch tenant later, sign in again and choose another tenantCode.

Gotchas​

  • The setting is checked at step 3, not step 1. A practitioner can pass the password and MFA steps and still be refused with 403 at the end. Your sign-in screen must handle a 403 at step 3, and the profiles list can include tenants where sign-in is off.
  • Project admins are not exempt. If you turn PRACTITIONER_LOGIN_ENABLED off and your own session ends, you cannot sign back in to turn it on again. Keep a signed-in admin session open until you have confirmed the change.
  • Turning the switch off does not end sessions. Signed-in practitioners keep working until their token expires, up to an hour.
  • One account, many projects. A practitioner's email and password are shared across every project they belong to. Registration is refused for an email that already has an account; add that person to your project with an invitation instead.
  • A failed read of the project looks like "off". If the project cannot be read for a moment, callers see the same 403.
  • Every error takes at least a second.