Authentication

Refresh access token

  • Sandbox: available
  • Staging: available
  • Production: unknown
POST
/auth/refresh-token

Exchanges a refresh token for a new access token and a new refresh token. Call it before the access token expires to keep a session alive.

Auth: none. The refresh token in the body is the credential.

Behaviour

  • Ovok forwards the token to Medplum's oauth2/token endpoint (grant_type=refresh_token, scope openid offline_access).
  • The request body takes the token as refresh_token.
  • The response holds accessToken, refreshToken and expiresIn (seconds), the same names the login routes use. Store the new refreshToken: use it for the next refresh.
  • The response also carries the same three values as access_token, refresh_token and expires_in. Those names are deprecated and will be removed in a later release: read the camelCase ones.
  • A refresh token Medplum refuses (spent, expired, revoked, or not a refresh token at all) gives a 401 with Medplum's error description. A refresh token is single-use, so retrying it after a successful refresh also gives a 401: sign in again.
  • A body without refresh_token gives a 400.
  • A session of a Patient whose project turned PATIENT_LOGIN_ENABLED off, or of a Practitioner whose project turned PRACTITIONER_LOGIN_ENABLED off, gives a 403 Login is not enabled for this project. That ends the session: the refresh token is used up, and signing in again is refused while login is off. A Practitioner who is a project admin is not refused, so an admin who is signed in can still turn login back on (they cannot sign in again while it is off). The access token already issued lasts until it expires (60 minutes).
  • POST /oauth2/token applies the same check to every token it grants (refresh and code exchange), answering 403 { "error": "access_denied", "error_description": "Login is not enabled for this project." }.

Example

curl -X POST 'https://api.sandbox.ovok.com/auth/refresh-token' \  -H 'Content-Type: application/json' \  -d '{    "refresh_token": "example.jwt.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NjcyNjg4MDAsImV4cCI6MTc2NzI3MjQwMH0.Ql3B_ztDvctlyZ2z0vp4sl3ExXRQGkzmxdmTAqxNzZk"  }'

Request Body

application/json
  1. body
refresh_token*string

Refresh token.

Length1 <= length

Response Body

Access token, refresh token and expiration time.

application/json
  1. response
accessToken*string

Bearer access token. Expires in 1 hour.

refreshToken*string

Refresh token. Use it for the next refresh.

expiresIn*integer

Seconds until the access token expires.

access_token*stringDeprecated

Deprecated: the same value as accessToken.

refresh_token?stringDeprecated

Deprecated: the same value as refreshToken.

expires_in*integerDeprecated

Deprecated: the same value as expiresIn.

Example Request
curl -X POST 'https://api.sandbox.ovok.com/auth/refresh-token' \  -H 'Content-Type: application/json' \  -d '{  "refresh_token": "string"}'
Example Response
{  "accessToken": "string",  "refreshToken": "string",  "expiresIn": 0,  "access_token": "string",  "refresh_token": "string",  "expires_in": 0}

Change user password PATCH

Changes the password of the signed-in user. Use it from an account settings screen where the user knows their current password. **Auth:** bearer token. **Scope:** the User of the token. ## Behaviour - `oldPassword` must match the current password. A wrong `oldPassword`, or an account with no password set, gives 401. - The new password may equal the old one. - The password must have at least 12 characters, not counting spaces at either end, and must not be on Ovok's list of common and breached passwords. Any character is allowed and none is required. A refused password answers 422 with the error on `newPassword`. - The route does not end existing sessions. - The email address is not marked as verified by this route. - The response is the updated `User` without its password hash. ## Example ```bash curl -X PATCH 'https://api.sandbox.ovok.com/v2/auth/change-password' \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NjcyNjg4MDAsImV4cCI6MTc2NzI3MjQwMH0.Ql3B_ztDvctlyZ2z0vp4sl3ExXRQGkzmxdmTAqxNzZk' \ -H 'Content-Type: application/json' \ -d '{ "oldPassword": "Ex4mple-Passw0rd", "newPassword": "a-new-password" }' ```

Log out (server-side token revocation) POST

Signs the caller out on the server: the access token in the `Authorization` header stops working for every authenticated route, although it has not expired. **Auth:** bearer token. A missing or invalid token gives 401. ## Behaviour - Ovok adds the token to a denylist. The entry lasts for the rest of the token's lifetime (its `exp` claim), so the list never outgrows the set of live tokens. - Only this access token is revoked. The refresh token is not touched here; to end other sessions use `DELETE /auth/session/{option}`. - The call is idempotent: repeating it with the same token returns 204 with no further effect. A token without an `exp` claim, or one that has already expired, is ignored. - If the denylist store is unavailable the call still returns 204, and the token stays valid until it expires. Clear the local session as well. - The response has no body (204 No Content). - The route is available at both `/auth/logout` and `/v1/auth/logout`. ## Example ```bash curl -X POST 'https://api.sandbox.ovok.com/v1/auth/logout' \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NjcyNjg4MDAsImV4cCI6MTc2NzI3MjQwMH0.Ql3B_ztDvctlyZ2z0vp4sl3ExXRQGkzmxdmTAqxNzZk' ```