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

# Authorize a User

> Start the OAuth 2.1 authorization code flow and obtain a single-use authorization code

Redirect the user's browser here to begin the authorization code flow. Chamelio validates the
request, shows the user a consent screen listing the scopes you asked for, and redirects back to your
`redirect_uri` with a single-use authorization code.

This is a browser redirect, not an API call - do not fetch it from your backend.

## Endpoint

```
GET /oauth/authorize
```

## Authentication

None. The user authenticates with Chamelio on the consent screen.

## Query Parameters

| Parameter               | Type   | Required | Description                                                                                                    |
| ----------------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------- |
| `response_type`         | string | Yes      | Must be `code`. No other response type is supported                                                            |
| `client_id`             | string | Yes      | Your application's client ID (`cid_...`)                                                                       |
| `redirect_uri`          | string | Yes      | Must exactly match one of the application's registered redirect URIs                                           |
| `scope`                 | string | Yes      | Space-delimited scopes to request. Must be non-empty and within the application's `allowed_scopes`             |
| `code_challenge`        | string | Yes      | PKCE challenge - the base64url-encoded SHA-256 of your code verifier                                           |
| `code_challenge_method` | string | Yes      | Must be `S256`. `plain` is not accepted                                                                        |
| `state`                 | string | No       | Opaque value returned unchanged on the redirect. Use it to protect against CSRF and to restore session context |

<Warning>
  PKCE is mandatory for every client, confidential and public alike, and only the `S256` method is
  accepted. Generate a fresh code verifier per authorization request and keep it until you exchange
  the code.
</Warning>

## Request Example

Generate a PKCE verifier and challenge, then send the user to the authorize URL.

<CodeGroup>
  ```bash cURL theme={null}
  # Generate the PKCE pair
  CODE_VERIFIER=$(openssl rand -base64 96 | tr -d '\n=' | tr '/+' '_-')
  CODE_CHALLENGE=$(printf '%s' "$CODE_VERIFIER" \
    | openssl dgst -sha256 -binary \
    | openssl base64 | tr -d '\n=' | tr '/+' '_-')

  echo "Save this verifier for the token request: $CODE_VERIFIER"

  # Open this URL in the user's browser
  echo "https://platform.chamelio.ai/oauth/authorize\
  ?response_type=code\
  &client_id=cid_your_client_id\
  &redirect_uri=https%3A%2F%2Fyour-app.example.com%2Fcallback\
  &scope=tasks%3Aread%20tasks%3Awrite\
  &code_challenge=$CODE_CHALLENGE\
  &code_challenge_method=S256\
  &state=xyz123"
  ```

  ```python Python theme={null}
  import base64
  import hashlib
  import secrets
  from urllib.parse import urlencode

  code_verifier = secrets.token_urlsafe(96)
  code_challenge = (
      base64.urlsafe_b64encode(hashlib.sha256(code_verifier.encode()).digest())
      .decode()
      .rstrip("=")
  )

  # Persist code_verifier in the user's session - you need it at the token step
  params = {
      "response_type": "code",
      "client_id": "cid_your_client_id",
      "redirect_uri": "https://your-app.example.com/callback",
      "scope": "tasks:read tasks:write",
      "code_challenge": code_challenge,
      "code_challenge_method": "S256",
      "state": secrets.token_urlsafe(16),
  }

  authorize_url = f"https://platform.chamelio.ai/oauth/authorize?{urlencode(params)}"
  print(authorize_url)
  ```

  ```javascript JavaScript theme={null}
  const toBase64Url = (bytes) =>
    btoa(String.fromCharCode(...new Uint8Array(bytes)))
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=+$/, '');

  const codeVerifier = toBase64Url(crypto.getRandomValues(new Uint8Array(64)));
  const digest = await crypto.subtle.digest(
    'SHA-256',
    new TextEncoder().encode(codeVerifier)
  );
  const codeChallenge = toBase64Url(digest);

  // Persist codeVerifier in the user's session - you need it at the token step
  const params = new URLSearchParams({
    response_type: 'code',
    client_id: 'cid_your_client_id',
    redirect_uri: 'https://your-app.example.com/callback',
    scope: 'tasks:read tasks:write',
    code_challenge: codeChallenge,
    code_challenge_method: 'S256',
    state: crypto.randomUUID()
  });

  window.location.href =
    `https://platform.chamelio.ai/oauth/authorize?${params}`;
  ```
</CodeGroup>

## Response

### Success Response

**Status Code:** `302 Found`

The user is redirected to Chamelio's consent screen. Once they approve, their browser is redirected
to your `redirect_uri` with the authorization code appended:

```
https://your-app.example.com/callback?code=ca_single_use_code&state=xyz123
```

Exchange the `code` immediately at [`POST /oauth/token`](/api-reference/oauth/token).

<Warning>
  Authorization codes are **single use** and short-lived - by default they expire about a minute after
  they are issued. Reusing a code is rejected as `invalid_grant`.
</Warning>

If the user declines, the redirect carries an error instead:

```
https://your-app.example.com/callback?error=access_denied&state=xyz123
```

## Error Responses

Errors arrive in one of two ways depending on whether Chamelio can trust your `redirect_uri` yet.

### 400 Bad Request

Returned directly in the browser, without redirecting, when the request cannot be trusted enough to
bounce back:

| Detail                           | Reason                                                                                |
| -------------------------------- | ------------------------------------------------------------------------------------- |
| `Unknown or inactive client_id`  | The `client_id` does not exist, or the application was deleted or deactivated         |
| `redirect_uri is not registered` | The `redirect_uri` is not an exact match for one of the application's registered URIs |

```json theme={null}
{
  "detail": "redirect_uri is not registered"
}
```

### Redirect errors

Once the client and redirect URI are validated, remaining problems are returned to your
`redirect_uri` as query parameters - `error`, `error_description`, and your original `state`:

| `error`                     | Reason                                                                                      |
| --------------------------- | ------------------------------------------------------------------------------------------- |
| `unsupported_response_type` | `response_type` was not `code`                                                              |
| `unauthorized_client`       | The application is not allowed the `authorization_code` grant                               |
| `invalid_scope`             | `scope` was empty, contained an unknown value, or exceeded the application's allowed scopes |
| `invalid_request`           | `code_challenge` was missing, or `code_challenge_method` was not `S256`                     |
| `access_denied`             | The user declined the consent request                                                       |

```
https://your-app.example.com/callback?error=invalid_scope&error_description=Requested%20scope%20is%20unknown%20or%20exceeds%20the%20application%27s%20allowed%20scopes&state=xyz123
```

## Notes

<Info>
  The consenting user must belong to the same organization as the application. A user from another
  organization attempting to approve gets a `403` on the consent screen.
</Info>

<Info>
  A pending authorization request expires if the user does not act on it - around ten minutes by
  default. After that they must start the flow again.
</Info>

<Tip>
  Always send `state` and verify it on the callback. It is the only way to tell your own redirect from
  a forged one.
</Tip>

## Use Cases

* **User sign-in for your integration** - Let each user connect their own Chamelio account
* **Least-privilege access** - Request only the scopes a feature actually needs
* **Multi-user products** - Give every user their own token instead of sharing one credential
