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

# PKCE (Proof Key for Code Exchange)

> How HASP implements PKCE S256 and why it is required for all OAuth flows.

HASP requires PKCE (RFC 7636) on every authorization code flow — for both public and confidential clients. PKCE prevents authorization code interception attacks by binding the code to a secret the client generates locally.

## How it works

1. **Generate a code verifier** — a cryptographically random string, 43–128 characters, using only `[A-Z a-z 0-9 - . _ ~]` (base64url without padding).
2. **Derive the code challenge** — SHA-256 hash of the verifier, then base64url-encode the raw bytes (no padding).
3. **Send the challenge** in the authorization URL as `code_challenge` with `code_challenge_method=S256`.
4. **Send the verifier** at token exchange time as `code_verifier`.

HASP rejects any authorization request that omits `code_challenge` or sets `code_challenge_method` to anything other than `S256`.

## Generating PKCE values

<CodeGroup>
  ```bash curl theme={null}
  CODE_VERIFIER=$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')
  CODE_CHALLENGE=$(echo -n "$CODE_VERIFIER" | sha256sum | xxd -r -p | base64 | tr '+/' '-_' | tr -d '=')
  ```

  ```javascript JavaScript theme={null}
  const array = new Uint8Array(32);
  crypto.getRandomValues(array);
  const verifier = btoa(String.fromCharCode(...array))
      .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');

  const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));
  const challenge = btoa(String.fromCharCode(...new Uint8Array(digest)))
      .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
  ```

  ```python Python theme={null}
  import hashlib, base64, os

  code_verifier = base64.urlsafe_b64encode(os.urandom(32)).rstrip(b'=').decode()
  digest = hashlib.sha256(code_verifier.encode()).digest()
  code_challenge = base64.urlsafe_b64encode(digest).rstrip(b'=').decode()
  ```

  ```php PHP theme={null}
  $codeVerifier = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');
  $codeChallenge = rtrim(strtr(base64_encode(hash('sha256', $codeVerifier, true)), '+/', '-_'), '=');
  ```
</CodeGroup>

## Security notes

* **Store the verifier on your server or in session**, never in a cookie or localStorage that is readable by third-party scripts.
* **Do not reuse a verifier** — generate a fresh one for every authorization request.
* **The verifier is a secret** until it is sent at token exchange. The challenge is safe to expose in the URL.

## Why public clients still need PKCE

Public clients (browser apps, mobile apps, CLIs) cannot keep a client secret confidential. PKCE acts as a per-request credential: even if an attacker intercepts the authorization code, they cannot exchange it without the verifier.

Confidential clients use PKCE in addition to their client secret for defense-in-depth.
