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.
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
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
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
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:
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:
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
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
generate_pkce() creates 32 random bytes and base64url-encodes them to produce the code_verifier.
- The
code_challenge is the SHA-256 digest of the verifier, base64url-encoded (without padding).
- The challenge method is always
S256 (plain is not supported).
- 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.