---
title: Authentication
slug: 6-ex-authentication
docTags: 
createdAt: 2024-04-12T10:35:53.295Z
---

# General

The Digitail API uses the [OAuth 2.0](https://www.oauth.com/) Standard for authentication paired up with the [Authorization Code Grant](https://www.oauth.com/oauth2-servers/access-tokens/authorization-code-request/) with [PKCE](https://www.oauth.com/oauth2-servers/pkce/).

OAuth 2.0 Authorization Code Grant with PKCE offers robust security features suitable for protecting sensitive clinic information:

- **Enhanced Security:** PKCE mitigates code interception and replay attacks by adding a verification step during the authentication process.
- **User Consent:** OAuth 2.0 standards ensure that users explicitly grant permission for applications to access their data, maintaining privacy and compliance with regulations.
- **Simplified Integration:** OAuth 2.0 is widely supported across programming languages and platforms, enabling developers to integrate with ease.

# Set-Up

Integrating with the Digitail API involves the following steps:

1. **Register Your Application:** Obtain API credentials by completing the [API access request form](https://digitail.com/api-access/).
2. **Implement OAuth 2.0 Flow:** Follow the OAuth 2.0 Authorization Code Grant with PKCE flow to authenticate your application and obtain access tokens.
3. **Access Data:** Utilize the access token to make authorized requests to Digitail API endpoints and retrieve clinic data.

:::hint{type="info"}
You can add the retrieved access token directly in the documentation page to try out the endpoints easier
:::

## Implementing OAuth 2.0 Flow

:::::WorkflowBlock
:::WorkflowBlockItem
Generate `code_verifier`, `code_challenge` and `state` variables. These variables are used to ensure the integrity and security of generating & retrieving the `access_token` that follows. `code_verifier` should be a cryptographically random generated string with lowercase & uppercase letters, digits and the punctuation characters -.\_\~ between 43 and 128 characters long. `code_challenge` is the base64 url encoded version of the sha256 hashing of the `code_verifier`. `state` is a randomly generated uri encoded string used by your app to protect yourself against CSRF attacks that is used to verify the integrity of the response.

Store the `code_verifier` and `state` variables locally.

More information [here](https://www.oauth.com/oauth2-servers/pkce/authorization-request/).
:::

::::WorkflowBlockItem
From your software, add a button **"Connect with Digitail"&#x20;**&#x74;hat will redirect the user to the following url:

:::CodeblockTabs
Curl

```curl
curl -L https://vet.digitail.io/oauth/authorize
  ?response_type=code
  &client_id={client_id}
  &redirect_uri=https://your-redirect-url.com/callback
  &state={state}
  &code_challenge={code_challenge}
  &code_challenge_method=S256
```
:::
::::

:::WorkflowBlockItem
Digitail will prompt the user for authentication&#x20;
:::

:::WorkflowBlockItem
The user authenticated and gives consent to give access to data specified.
:::

:::WorkflowBlockItem
Digitail will redirect back to the callback URL with the auth code & state params in the URL

The `code` is available for only 10 minutes and should be used immediately to request an `access_token`
:::

::::WorkflowBlockItem
Request an `access_token` by sending the following POST request:

:::CodeblockTabs
Curl

```curl
curl -X POST
  -d grant_type=authorization_code
  -d client_id={client_id}
  -d client_secret={client_secret}
  -d redirect_uri=https://your-redirect-url.com/callback
  -d code_verifier={code_verifier}
  -d code={code}
  https://vet.digitail.io/oauth/token
```
:::

Send this request from your server. The `client_secret` is only ever sent to the token endpoint, never in the authorize URL from step 2.
::::

::::WorkflowBlockItem
Digitail will respond with the payload:

:::CodeblockTabs
JSON

```json
  {                                                                                                                                                                                                                                                                                                                                                                                                                   
    "token_type": "Bearer",                                                                                                                                                                                                                                                                                                                                                                                           
    "expires_in": 31536000,                                                                                                                                                                                                                                                                                                                                                                                           
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUz...",                                                                                                                                                                                                                                                                                                                                                              
    "refresh_token": "def50200a1b2c3..."                                                                                                                                                                                                                                                                                                                                                                              
  } 
```
:::
::::

:::WorkflowBlockItem
Using the access token

Send the access token on every API request in the `Authorization` header:

```javascript
GET https://vet.digitail.io/api/v1/appointments                                                                                                                                                                                                                                                                                                                                                                     
Authorization: Bearer {access_token}                                                                                                                                                                                                                                                                                                                                                                                
Accept: application/json                                                                                                                                                                                                                                                                                                                                                                                            
```

Requests run in the context of the clinic that belongs to the user who authorized
your application. If that user belongs to more than one clinic, select one by
sending its id in the `X-ClinicId` header. Without the header, the user's default
clinic is used.
:::

:::WorkflowBlockItem
Token lifetimes

| Token              | Valid for  | Notes                                           |
| ------------------ | ---------- | ----------------------------------------------- |
| Authorization code | 10 minutes | Exchange it for tokens immediately.             |
| Access token       | 1 year     | `expires_in` in the token response, in seconds. |
| Refresh token      | 1 year     | Counted from the moment it is issued.           |

Store both tokens securely and treat them like passwords. Only the access token is ever sent to the API. The refresh token is only ever sent to the token endpoint.
:::

:::WorkflowBlockItem
Refreshing the access token

Nothing refreshes automatically. When the access token expires, or ahead of time if you prefer, request a new one from the token endpoint using the`refresh_token` grant. PKCE parameters are not needed for this call.

```javascript
curl -X POST https://vet.digitail.io/oauth/token \                                                                                                                                                                                                                                                                                                                                                                  
  -d grant_type=refresh_token \                                                                                                                                                                                                                                                                                                                                                                                     
  -d refresh_token={refresh_token} \                                                                                                                                                                                                                                                                                                                                                                                
  -d client_id={client_id} \                                                                                                                                                                                                                                                                                                                                                                                        
  -d client_secret={client_secret}                                                                                                                                                                                                                                                                                                                                                                                  
```

```json
{                                                                                                                                                                                                                                                                                                                                                                                                                   
  "token_type": "Bearer",                                                                                                                                                                                                                                                                                                                                                                                           
  "expires_in": 31536000,                                                                                                                                                                                                                                                                                                                                                                                           
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUz...",                                                                                                                                                                                                                                                                                                                                                              
  "refresh_token": "def50200a1b2c3..."                                                                                                                                                                                                                                                                                                                                                                              
}                                                                                                                                                                                                                                                                                                                                                                                                                   
```

Every refresh returns a **new access token and a new refresh token**, and revokes
both of the old ones in the same call. Refresh tokens are single use:

- Replace the stored refresh token with the new one as soon as the response arrives.
- Do not run two refreshes in parallel for the same user. The second one fails
  because the first already revoked the token it is presenting.
- Refreshing at least once a year keeps the session alive indefinitely, because each
  refresh token is valid for one year from issuance.

The token endpoint is rate limited separately from the API, at 60 requests per
minute per IP address.
:::

:::WorkflowBlockItem
Handling expired or revoked tokens

| What you see                                                                                                                | Cause                                                                         | What to do                                          |
| --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------- |
| `401 Unauthorized` from an API endpoint                                                                                     | Access token expired or revoked                                               | Refresh, then retry the request.                    |
| `401` from `/oauth/token` with `"error": "invalid_request"` and a `hint` of `Token has expired` or `Token has been revoked` | Refresh token is dead                                                         | Send the user through the authorization flow again. |
| `401` from `/oauth/token` with `"error": "invalid_client"`                                                                  | Wrong `client_id` or `client_secret`                                          | Check your credentials.                             |
| `400` from `/oauth/token` with `"error": "invalid_grant"`                                                                   | Authorization code expired, reused, or wrong `code_verifier` / `redirect_uri` | Restart the authorization flow.                     |
:::
:::::

## OAuth 2.0 Flow Diagram

![](https://api.archbee.com/api/optimize/nCLer3iAno-0qSCz6BN4S/UqXP8NLQ9kcnWRlrqtNxi_digitailoauth.png "OAuth 2.0 Flow")

![](https://api.archbee.com/api/optimize/nCLer3iAno-0qSCz6BN4S/UqXP8NLQ9kcnWRlrqtNxi_digitailoauth.png "OAuth 2.0 Flow")

