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

# PKCE

> Use Proof Key for Code Exchange (PKCE) to secure the authorization flow against code interception attacks.

## Overview

PKCE (Proof Key for Code Exchange) prevents authorization code interception attacks by binding the authorization request to the token exchange. The Grantex SDK provides a `generate_pkce()` helper that creates a cryptographically random code verifier and its S256 challenge.

## Usage

```python theme={null}
from grantex import generate_pkce

pkce = generate_pkce()

print(pkce.code_verifier)         # Random 43-character URL-safe string
print(pkce.code_challenge)        # SHA-256 hash of the verifier, base64url-encoded
print(pkce.code_challenge_method) # Always "S256"
```

## Import

```python theme={null}
from grantex import generate_pkce, PkceChallenge
```

## PkceChallenge

`generate_pkce()` returns a `PkceChallenge` frozen dataclass:

| Field                   | Type  | Description                                                               |
| ----------------------- | ----- | ------------------------------------------------------------------------- |
| `code_verifier`         | `str` | A cryptographically random URL-safe string (32 bytes, base64url-encoded). |
| `code_challenge`        | `str` | The SHA-256 hash of the verifier, base64url-encoded.                      |
| `code_challenge_method` | `str` | Always `"S256"`.                                                          |

## Complete PKCE Flow

The PKCE flow has three steps: generate the challenge, authorize with the challenge, and exchange with the verifier.

### Step 1: Generate the PKCE Pair

```python theme={null}
from grantex import generate_pkce

pkce = generate_pkce()
# Store pkce.code_verifier securely -- you will need it in Step 3
```

### Step 2: Authorize with the Code Challenge

Pass the `code_challenge` and `code_challenge_method` in the authorization request:

```python theme={null}
from grantex import Grantex, AuthorizeParams

with Grantex(api_key="gx_live_...") as client:
    auth = client.authorize(AuthorizeParams(
        agent_id="agt_abc123",
        user_id="user_xyz",
        scopes=["files:read", "files:write"],
        redirect_uri="https://myapp.com/callback",
        code_challenge=pkce.code_challenge,
        code_challenge_method=pkce.code_challenge_method,
    ))

    # Redirect user to auth.consent_url
```

### Step 3: Exchange with the Code Verifier

After the user approves and you receive the authorization code at your redirect URI, include the `code_verifier` in the token exchange:

```python theme={null}
from grantex import Grantex, ExchangeTokenParams

with Grantex(api_key="gx_live_...") as client:
    token_response = client.tokens.exchange(ExchangeTokenParams(
        code="received_auth_code",
        agent_id="agt_abc123",
        code_verifier=pkce.code_verifier,
    ))

    print(f"Grant token: {token_response.grant_token}")
```

The server verifies that `SHA256(code_verifier) == code_challenge` before issuing the token. If the verifier does not match, the exchange is rejected.

## Full Example

```python theme={null}
from grantex import (
    Grantex,
    AuthorizeParams,
    ExchangeTokenParams,
    generate_pkce,
)

# 1. Generate PKCE pair
pkce = generate_pkce()

with Grantex(api_key="gx_live_...") as client:
    # 2. Start authorization with PKCE
    auth = client.authorize(AuthorizeParams(
        agent_id="agt_abc123",
        user_id="user_xyz",
        scopes=["calendar:read"],
        redirect_uri="https://myapp.com/callback",
        code_challenge=pkce.code_challenge,
        code_challenge_method=pkce.code_challenge_method,
    ))

    print(f"Send user to: {auth.consent_url}")

    # 3. User approves, you receive the code at your callback
    code = "code_from_redirect"

    # 4. Exchange with PKCE verifier
    result = client.tokens.exchange(ExchangeTokenParams(
        code=code,
        agent_id="agt_abc123",
        code_verifier=pkce.code_verifier,
    ))

    print(f"Grant token: {result.grant_token}")
    print(f"Grant ID: {result.grant_id}")
    print(f"Scopes: {result.scopes}")
```

## How It Works

1. `generate_pkce()` creates 32 random bytes and base64url-encodes them to produce the `code_verifier`.
2. The `code_challenge` is the SHA-256 digest of the verifier, base64url-encoded (without padding).
3. The challenge method is always `S256` (plain is not supported).
4. During token exchange, the server independently computes `SHA256(code_verifier)` and compares it to the stored `code_challenge`. A mismatch causes the exchange to fail.
