# Authentication

Canonical page: https://docs.ovok.com/authentication

Ovok signs people in **per tenant**. Every project on Ovok is a tenant, and a short **tenant code** names it. A patient signs in to the tenant they belong to; a practitioner signs in once and then chooses which of their tenants to enter. Tokens are issued for one project, so what a user can reach is decided by the project they signed in to.

This section covers tenant authentication: the eight routes under `/auth/tenant/`. Use them for every new app.

## How sign-in works [#how-sign-in-works]

Sign-in has two steps so that a code stolen in transit cannot be turned into tokens. Your app creates a random **code verifier**, sends its hash (the **code challenge**) when it starts, and sends the verifier itself when it finishes. This is PKCE with the `S256` method.

```mermaid
sequenceDiagram
  participant App as Your app
  participant Ovok
  App->>Ovok: login/start (email, password, code challenge)
  alt Multi-factor sign-in is enrolled
    Ovok-->>App: nextStep mfa, loginId
    App->>Ovok: login/mfa (loginId, one-time code)
  end
  Ovok-->>App: nextStep token, sessionCode
  App->>Ovok: login/token (sessionCode, code verifier)
  Ovok-->>App: accessToken, refreshToken, project, profile
```

Registration returns tokens straight away, so a newly registered user is signed in without a second step.

## The eight routes [#the-eight-routes]

Each audience has the same four routes under its own path.

| Step | Patient | Practitioner |
| --- | --- | --- |
| Start sign-in | [`POST /auth/tenant/Patient/login/start`](/authentication/patient-login#step-1-start-sign-in) | [`POST /auth/tenant/Practitioner/login/start`](/authentication/practitioner-login#step-1-start-sign-in) |
| Verify the second factor | [`POST /auth/tenant/Patient/login/mfa`](/authentication/patient-login#step-2-verify-the-one-time-code-only-with-mfa) | [`POST /auth/tenant/Practitioner/login/mfa`](/authentication/practitioner-login#step-2-verify-the-one-time-code-only-with-mfa) |
| Finish sign-in | [`POST /auth/tenant/Patient/login/token`](/authentication/patient-login#step-3-exchange-the-session-code-for-tokens) | [`POST /auth/tenant/Practitioner/login/token`](/authentication/practitioner-login#step-3-choose-a-tenant-and-get-tokens) |
| Register | [`POST /auth/tenant/Patient/register`](/authentication/patient-registration) | [`POST /auth/tenant/Practitioner/register`](/authentication/practitioner-registration) |

None of them needs a bearer token. The tenant is named by `tenantCode` in the body, except for the practitioner `login/start` and `login/mfa` steps, which come before a tenant is chosen.

[Carers](/authentication/carers) (FHIR `RelatedPerson`) sign in the same way as patients, under `/auth/tenant/RelatedPerson/login/...`. They have no register route: a carer account is made only by accepting a patient's invite.

## Before you build: what the project needs [#before-you-build-what-the-project-needs]

Sign-in and registration are controlled by project settings. Most default to **on** when unset, but registration needs more than a switch, and practitioner registration is **off** until you turn it on. Set each value deliberately; the [project setup](/authentication/project-setup) page walks through it.

| Flow | Settings it depends on | When unset |
| --- | --- | --- |
| Patient sign-in | [`PATIENT_LOGIN_ENABLED`](/settings-and-features/settings/patient-login-enabled) | On |
| Patient registration | [`PATIENT_REGISTRATION_ENABLED`](/settings-and-features/settings/patient-registration-enabled), plus a default patient AccessPolicy on the project | On, but refused until the AccessPolicy exists |
| Business-email sign-up (optional) | [`CLINICIAN_INVITE_ON_BUSINESS_EMAIL`](/settings-and-features/settings/clinician-invite-on-business-email), [`DEFAULT_PRACTITIONER_ACCESS_POLICY`](/settings-and-features/settings/default-practitioner-access-policy), [`PRACTITIONER_APP_URL`](/settings-and-features/settings/practitioner-app-url), [`PATIENT_APP_URL`](/settings-and-features/settings/patient-app-url) | Off |
| Practitioner sign-in | [`PRACTITIONER_LOGIN_ENABLED`](/settings-and-features/settings/practitioner-login-enabled) | On |
| Practitioner registration | [`PRACTITIONER_REGISTRATION_ENABLED`](/settings-and-features/settings/practitioner-registration-enabled) set to `true`, plus [`DEFAULT_PRACTITIONER_ACCESS_POLICY`](/settings-and-features/settings/default-practitioner-access-policy) | **Off** |
| Carer sign-in | [`RELATED_PERSON_LOGIN_ENABLED`](/settings-and-features/settings/related-person-login-enabled) | On |

`GET /v1/project/settings` reports `false` for a boolean that was never set, even where the flow behaves as on. See [unset settings](/settings-and-features#defaults-and-dependencies) before you read that response as a switch.

Some flows also send an email: a welcome after patient registration, and an invitation for business-email sign-ups. Each is sent only when its template is [mapped](/email-templates). Settings decide whether a flow can run, not what a signed-in user may do. That is the AccessPolicy's job; see [settings are not permissions](/settings-and-features#settings-are-not-permissions).

## Find your tenant code [#find-your-tenant-code]

A tenant code is a short name, such as `big-health-company`, that your apps send in `tenantCode`.

- A signed-in user can read the code of their current project with `GET /organizations/code`.
- A practitioner's `login/start` response lists the `tenantCode` of every project they belong to.
- A patient who has forgotten their code can be emailed all of theirs with `POST /organizations/code/email`. The route always answers `202`, so it does not reveal whether the address has an account.

## What you get back [#what-you-get-back]

A finished sign-in or a registration returns:

| Field | Meaning |
| --- | --- |
| `accessToken` | Bearer token for API calls. Valid for one hour. |
| `refreshToken` | Token for renewing the session. |
| `expiresIn` | Lifetime of the access token, in seconds. |
| `project` | `reference` and `display` of the project the tokens are for. |
| `profile` | `reference` and `display` of the Patient or Practitioner who signed in. |

Send the access token as `Authorization: Bearer <accessToken>`. Renewing a session and signing out are covered by the [account routes](#account-routes) below.

## Account routes [#account-routes]

The eight tenant routes sign people in. Once a user holds tokens, a second group of routes manages the account: who the token belongs to, sessions, passwords, deletion and consents. They are account-level routes. They have no `/auth/tenant/` variants, and they work with the tokens that a tenant sign-in or registration returns. The two password reset routes and the token refresh need no bearer token.

**Identity and sessions**

- [`GET /auth/me` — Get account information](/authentication/account/get-account-information)
- [`GET /auth/session` — List sessions](/authentication/account/list-sessions)
- [`DELETE /auth/session/:option` — Revoke sessions](/authentication/account/revoke-sessions)
- [`POST /auth/refresh-token` — Refresh an access token](/authentication/account/refresh-token)
- [`POST /v1/auth/logout` — Log out](/authentication/account/log-out)

**Passwords and deletion**

- [`PATCH /v2/auth/change-password` — Change password](/authentication/account/change-password)
- [`POST /v2/auth/reset-password` — Send a password reset email](/authentication/account/reset-password)
- [`POST /v2/auth/reset-password/process` — Set a new password from a reset email](/authentication/account/process-password-reset)
- [`DELETE /auth/delete` — Schedule account deletion](/authentication/account/delete-account)

**Consents of the signed-in practitioner**

- [`GET /v1/me/consent/medical` — Get medical consent](/authentication/account/get-medical-consent)
- [`POST /v1/me/consent/medical` — Give or withdraw the medical consent](/authentication/account/update-medical-consent)
- [`GET /v1/me/consent/legal` — Get legal consent](/authentication/account/get-legal-consent)
- [`POST /v1/me/consent/legal` — Accept or withdraw the terms](/authentication/account/update-legal-consent)

## Limits you will meet [#limits-you-will-meet]

Every route here is rate limited per route and per client IP address. Over the limit, the answer is `429` with `Retry-After` in seconds; the JSON body has the same value in `retryAfterSec`. See [Slim API errors](/slim-apis#errors-on-every-route) for the full rate-limit header list.

| Route | Requests per minute |
| --- | --- |
| `login/start` | 10 |
| `login/token` | 30 |
| `login/mfa` | 5 |
| `register` | 5 |

Every error response from these routes is held back to take at least one second. This slows down guessing; it also means a slow failure is not an outage. Errors share one JSON shape, described in [errors and troubleshooting](/authentication/troubleshooting).

## Where to go next [#where-to-go-next]

1. [Set up your project](/authentication/project-setup): tenant code, settings and AccessPolicies.
2. [Patient sign-in](/authentication/patient-login) and [practitioner sign-in](/authentication/practitioner-login).
3. [Patient registration](/authentication/patient-registration) and [practitioner registration](/authentication/practitioner-registration).
4. [Errors and troubleshooting](/authentication/troubleshooting).
5. [Account routes](#account-routes): sessions, passwords, deletion and consents once a user is signed in.
