> ## Documentation Index
> Fetch the complete documentation index at: https://docs.drime.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Authorization Flow

> Send the user to Drime, receive a code, exchange it for tokens

## 1. Prepare PKCE

For every authorization, generate a random `code_verifier` (43 to 128 characters from `A-Z`, `a-z`, `0-9`, `-`, `.`, `_`, `~`) and its S256 challenge:

```bash theme={null}
code_verifier=$(openssl rand 48 | openssl base64 -A | tr '+/' '-_' | tr -d '=')
code_challenge=$(printf '%s' "$code_verifier" \
  | openssl dgst -binary -sha256 \
  | openssl base64 -A | tr '+/' '-_' | tr -d '=')
```

Keep the `-A` flags: without them `openssl base64` breaks long output into lines, and a verifier with a line break in it fails the exchange with `invalid_grant`.

PKCE is required for desktop, mobile and script applications. Server applications may leave it out, but should not: it costs nothing and protects the code. Only `S256` is accepted; `plain` is refused.

## 2. Send the user to Drime

```
https://app.drime.cloud/oauth/authorize
  ?client_id=lumo_8f3a6c21d4e77b90
  &redirect_uri=https%3A%2F%2Fyour-app.com%2Foauth%2Fcallback
  &response_type=code
  &scope=files.read%20files.write
  &state=STATE
  &code_challenge=CODE_CHALLENGE
  &code_challenge_method=S256
```

| Parameter | Value |
| - | - |
| `client_id` | Your application's client ID, from the console |
| `redirect_uri` | One of your registered redirect URLs, character for character |
| `response_type` | Always `code` |
| `scope` | The [scopes](/oauth/scopes) you ask for, separated by spaces. At least one |
| `state` | A random value, new for each request, that you check when the user comes back. It is your protection against CSRF |
| `code_challenge` | The challenge from step 1 |
| `code_challenge_method` | `S256` |

The user signs in to Drime if they are not signed in yet (password, two-factor, passkey or Google: you implement none of it). Then they see the authorization screen: your application's name and what it asks for. They can untick permissions before approving. The screen shows every time, including to someone who already approved your application.

If `client_id` or `redirect_uri` is wrong, Drime shows the error on its own page and sends nobody to the URL: an address you did not register never receives anything.

## 3. Drime sends the user back

```
https://your-app.com/oauth/callback?code=CODE&state=STATE
```

Check that `state` is the value you sent, and refuse the request otherwise.

If the user declines, you get `?error=access_denied`, with `error_description` and your `state`, and no code.

The code is valid for **60 seconds** and works **once**. A failed exchange uses it up as well: start a new authorization.

## 4. Exchange the code for tokens

<CodeGroup>
  ```bash Server application theme={null}
  curl -X POST https://app.drime.cloud/oauth/token \
    -d grant_type=authorization_code \
    -d code="$CODE" \
    -d redirect_uri=https://your-app.com/oauth/callback \
    -d client_id="$CLIENT_ID" \
    -d client_secret="$CLIENT_SECRET" \
    -d code_verifier="$CODE_VERIFIER"
  ```

  ```bash Desktop, mobile or script theme={null}
  curl -X POST https://app.drime.cloud/oauth/token \
    -d grant_type=authorization_code \
    -d code="$CODE" \
    -d redirect_uri=http://127.0.0.1:53682/callback \
    -d client_id="$CLIENT_ID" \
    -d code_verifier="$CODE_VERIFIER"
  ```
</CodeGroup>

* `redirect_uri` must be the one used in step 2.
* A server application may send its credentials with HTTP Basic instead of the body (RFC 6749, section 2.3.1). If it sends both, Basic is used.
* A desktop, mobile or script application sends no secret. Sending one is refused.

```json theme={null}
{
  "access_token": "554|wbB1NbhiyX8hpNdZZakHaKMEeHEkPOsLts3LG3b4VeJEwJBd",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "Jx2kq2aB6JHgGHaPRGNveeELUprYX0IBDYac31IZygRXEEwvY6ISpEqVKNFlrOKFvY4EEhPiha2lJhZ5",
  "scope": "files.read files.write"
}
```

Read `scope` instead of assuming it. It holds what you were actually given:

* **sometimes more** than you asked for, because a `.write` scope always brings its `.read`;
* **sometimes less**, because the user may have unticked a permission;
* **never** anything the user did not approve.

The response does not say who the user is. Call [`GET /api/v1/users/me`](/api-reference/user/get-logged-user) once and store `user.id` with the tokens: [webhooks](/oauth/webhooks) name accounts by this id.

## 5. Call the API

```bash theme={null}
curl https://app.drime.cloud/api/v1/drive/file-entries \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

## 6. Refresh

Access tokens last **one hour**. Refresh tokens last **90 days** and **rotate**: each refresh returns a new refresh token, valid 90 days again, and retires the one you sent. The access token you had stops working at the same time.

<CodeGroup>
  ```bash Server application theme={null}
  curl -X POST https://app.drime.cloud/oauth/token \
    -d grant_type=refresh_token \
    -d refresh_token="$REFRESH_TOKEN" \
    -d client_id="$CLIENT_ID" \
    -d client_secret="$CLIENT_SECRET"
  ```

  ```bash Desktop, mobile or script theme={null}
  curl -X POST https://app.drime.cloud/oauth/token \
    -d grant_type=refresh_token \
    -d refresh_token="$REFRESH_TOKEN" \
    -d client_id="$CLIENT_ID"
  ```
</CodeGroup>

<Warning>
  Store the new refresh token before you use it. A refresh token presented twice is treated as stolen: Drime revokes every token your application holds for that user, and the user has to authorize again. If several processes share the tokens, refresh behind a lock.
</Warning>

You may pass `scope` to narrow the new access token to part of what you were granted. Scopes you were not granted are never added, and a request for nothing but those answers `invalid_scope`.

## 7. Revoke

When a user disconnects Drime from inside your application, revoke the refresh token:

<CodeGroup>
  ```bash Server application theme={null}
  curl -X POST https://app.drime.cloud/oauth/revoke \
    -d token="$REFRESH_TOKEN" \
    -d client_id="$CLIENT_ID" \
    -d client_secret="$CLIENT_SECRET"
  ```

  ```bash Desktop, mobile or script theme={null}
  curl -X POST https://app.drime.cloud/oauth/revoke \
    -d token="$REFRESH_TOKEN" \
    -d client_id="$CLIENT_ID"
  ```
</CodeGroup>

* **A refresh token** ends the authorization: every token your application holds for that user stops working.
* **An access token** ends only that token. The refresh token keeps working.
* A token that is unknown or already revoked answers `200` too, as RFC 7009 requires. Wrong client credentials answer `401` with `invalid_client`.

Users can also disconnect your application themselves, at any time, from **Devices & apps** in their Drime settings.

## Redirect URLs

* Compared **exactly**, after normalizing the case of the scheme and host. No prefix, no wildcard, no subdomain matching.
* `https://` only, except `http://127.0.0.1`, `http://[::1]` and `http://localhost` for applications running on the user's own machine.
* On `127.0.0.1` and `[::1]` the **port is ignored** (RFC 8252, section 7.3), so a desktop application can listen on whatever port is free. `localhost` keeps its port.
* No fragment, no wildcard, no credentials in the URL, no `..`.
* Custom schemes such as `myapp://callback` are refused: any other application on the same machine can register the same scheme and catch the code. Use the loopback address instead.
* Up to 10 redirect URLs per application, 2,000 characters each.

<Tip>
  A desktop application opens the authorization URL in the system browser, not in a web view inside the app, and listens on `http://127.0.0.1:<any free port>/callback` for the code. The user sees the real Drime page and can check the address bar.
</Tip>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.