← Cloudflare One / cloudflare-one / access-controls
Ověřte kódovací agenty
Kódovací agenti jako Claude Code, OpenCode nebo Windsurf často potřebují přistupovat k prostředkům chráněným pomocí Cloudflare Access. Pokud se prostředek nachází za Access, neověřené požadavky obdrží přesměrování nebo 403 chybu místo očekávané odpovědi. Váš agent potřebuje způsob, jak se ověřit, než se ke zdroji dostane.
Tato stránka popisuje dvě metody ověřování:
- cloudflared : ověřuje se pod vaší uživatelskou identitou. Použijte pro interaktivní vývoj, kdy můžete dokončit přihlášení v prohlížeči.
- Service tokens : ověřuje se pomocí statické dvojice přihlašovacích údajů. Použijte pro headless nebo automatizované úlohy, kde není k dispozici prohlížeč.
Použít cloudflared
S cloudflared, váš agent se ověřuje pod vaší uživatelskou identitou. Při prvním použití, cloudflared otevře okno prohlížeče pro interaktivní přihlášení. Poté relace přetrvává po dobu doba trvání relace nakonfigurováno pro aplikaci. Po vypršení relace vyžaduje další požadavek nové přihlášení v prohlížeči.
Předpoklady
Stáhněte a nainstalujte cloudflared.
Odesílat požadavky pomocí cloudflared access curl
Pro přímé požadavky na chráněný prostředek použijte cloudflared access curl. Toto řeší ověřování automaticky a nevyžaduje správu tokenů.
cloudflared access curl https://example.com/api/endpointPokud jde o první požadavek v relaci, cloudflared otevře prohlížeč, ve kterém se uživatel může ověřit. V případě potřeby uživatele vyzvěte k dokončení přihlášení.
Použít opakovaně použitelný token
Někteří agenti odesílají HTTP požadavky pomocí vlastních klientských knihoven místo volání cloudflared přímo. V takovém případě se přihlaste, získejte token a předejte ho jako hlavičku:
CF_TOKEN=$(cloudflared access login https://example.com)
curl --header "cf-access-token: $CF_TOKEN" https://example.com/api/endpointToken je platný po dobu trvání relace nakonfigurovanou pro danou aplikaci.
Další informace najdete v tématu Připojení přes Access pomocí CLI a cloudflared na straně klienta.
Použít service tokens
Service tokeny jsou statické páry přihlašovacích údajů, které ověřují požadavky bez přihlášení přes prohlížeč. Používejte je pro automatizované úlohy, při kterých není přítomen žádný uživatel.
-
Vytvořit service token a uložte Client ID a Client Secret.
-
V konfiguraci zásad aplikace Access přidejte Zásada Service Auth. Tento typ zásady přijímá přihlašovací údaje service token místo přihlášení přes poskytovatele identity. Použijte Service Token selektor a vyberte token, který jste vytvořili.
Akce Typ pravidla Selektor Hodnota Service Auth Include Service Token Váš token agenta -
Uložte Client ID a Client Secret na bezpečné místo ve svém počítači, ze kterého je bude moci agent přečíst.
-
Do požadavků na chráněný prostředek zahrňte obě hodnoty jako hlavičky:
curl --header "CF-Access-Client-Id: $CF_ACCESS_CLIENT_ID" \ --header "CF-Access-Client-Secret: $CF_ACCESS_CLIENT_SECRET" \ https://example.com/api/endpoint
Další informace najdete v tématu Service tokens.
Konfigurace agenta
Přidejte AGENTS.md soubor do kořenového adresáře projektu s následující definicí skill. Ta instruuje agenty pro psaní kódu, aby automaticky rozpoznávaly prostředky chráněné Cloudflare Access a ověřovaly se pomocí standardního OAuth 2.0 flow s PKCE (RFC 9728).
---
name: access-oauth
description: "Detect Cloudflare Access-protected websites and authenticate via the standard OAuth 2.0 flow (RFC 9728 resource metadata, dynamic client registration, authorization code + PKCE)"
license: MIT
compatibility: opencode
metadata:
category: authentication
audience: developers
---
# Access OAuth Authentication
Authenticate to Cloudflare Access-protected resources using standard OAuth 2.0
(resource metadata discovery, dynamic client registration, authorization code with PKCE).
## When to Use
Use this skill when:
- You need to access a URL that returns HTTP 401
- The response contains a `www-authenticate: Bearer` header with a `resource_metadata` URL
- The resource metadata indicates it is a Cloudflare Access-protected resource
- You want to authenticate interactively through the user's IdP
## Step 1: Detect a Protected Resource
Make a request and inspect the response headers:
```bash
curl -sI -L <URL> 2>&1
```
Look for a **401** response with a `www-authenticate` header like:
```
www-authenticate: Bearer realm="OAuth", error="invalid_token",
error_description="Missing or invalid access token",
resource_metadata="https://<hostname>/.well-known/cloudflare-access-protected-resource/"
```
If you see this header, the site supports the OAuth flow. Proceed to Step 2.
The JSON body of the 401 will also contain:
```json
{
"error": "invalid_token",
"error_description": "Missing or invalid access token",
"resource_metadata": "https://<hostname>/.well-known/cloudflare-access-protected-resource/"
}
```
### If No `www-authenticate` Header
If the 401 does not include `www-authenticate` with `resource_metadata`, the site may
not support this OAuth flow. Fall back to `cloudflared access curl` or browser-based
authentication.
## Step 2: Fetch Resource Metadata
Fetch the resource metadata URL from the `www-authenticate` header:
```bash
curl -s https://<hostname>/.well-known/cloudflare-access-protected-resource/
```
Expected response:
```json
{
"resource": "https://<hostname>",
"protected": true,
"team_domain": "<team>.cloudflareaccess.com",
"authorization_servers": ["https://<team>.cloudflareaccess.com"],
"authentication_method": "cloudflared",
"authentication_method_description": "Use `cloudflared access curl`...",
"authentication_method_documentation": "https://developers.cloudflare.com/cloudflare-one/tutorials/cli/"
}
```
Extract the **authorization server** URL from `authorization_servers[0]` (e.g. `https://<team>.cloudflareaccess.com`).
## Step 3: Fetch OAuth Authorization Server Metadata
```bash
curl -s https://<team>.cloudflareaccess.com/.well-known/oauth-authorization-server
```
Expected response:
```json
{
"issuer": "<team>.cloudflareaccess.com",
"authorization_endpoint": "https://<team>.cloudflareaccess.com/cdn-cgi/access/oauth/authorization",
"token_endpoint": "https://<team>.cloudflareaccess.com/cdn-cgi/access/oauth/token",
"response_types_supported": ["code"],
"response_modes_supported": ["query"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post",
"none"
],
"revocation_endpoint": "https://<team>.cloudflareaccess.com/cdn-cgi/access/oauth/revoke",
"registration_endpoint": "https://<team>.cloudflareaccess.com/cdn-cgi/access/oauth/registration",
"code_challenge_methods_supported": ["S256"]
}
```
Verify that:
- `"none"` is in `token_endpoint_auth_methods_supported` (allows public clients)
- `"authorization_code"` is in `grant_types_supported`
- `"S256"` is in `code_challenge_methods_supported`
- A `registration_endpoint` is present
Extract the **registration_endpoint**, **authorization_endpoint**, and **token_endpoint**.
## Step 4: Dynamic Client Registration
Register a public OAuth client:
```bash
curl -s -X POST <registration_endpoint> \
-H "Content-Type: application/json" \
-d '{
"redirect_uris": ["http://localhost:8400/callback"],
"token_endpoint_auth_method": "none",
"grant_types": ["authorization_code"],
"response_types": ["code"],
"resource": "https://<hostname>"
}'
```
Expected response:
```json
{
"client_id": "<uuid>",
"redirect_uris": ["http://localhost:8400/callback"],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "none",
"registration_client_uri": "...",
"client_id_issued_at": 1234567890
}
```
Save the **client_id**.
## Step 5: Generate PKCE Challenge
Generate a code verifier and S256 challenge. Ensure the challenge starts with an
alphanumeric character to avoid URL parsing issues:
```bash
while true; do
CODE_VERIFIER=$(openssl rand -base64 32 | tr -d '=' | tr '/+' '_-')
CODE_CHALLENGE=$(printf '%s' "$CODE_VERIFIER" | openssl dgst -sha256 -binary | base64 | tr -d '=' | tr '/+' '_-')
if [[ "$CODE_CHALLENGE" =~ ^[a-zA-Z0-9] ]]; then
break
fi
done
```
**Important**: The code challenge MUST start with `[a-zA-Z0-9]`. A leading `-` or `_`
can cause URL parameter parsing failures on the authorization server.
## Step 6: Authorization Code Flow with Local Callback
Start a local HTTP server to catch the callback, then direct the user to the
authorization URL.
### Build the Authorization URL
```
<authorization_endpoint>?
client_id=<client_id>&
redirect_uri=http%3A%2F%2Flocalhost%3A8400%2Fcallback&
response_type=code&
code_challenge=<CODE_CHALLENGE>&
code_challenge_method=S256&
resource=<URL-encoded target resource>
```
### Start the Callback Listener and Prompt the User
Run a Python HTTP server on port 8400 that captures the authorization code:
```python
python3 -c '
import http.server, urllib.parse
class Handler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
parsed = urllib.parse.urlparse(self.path)
params = urllib.parse.parse_qs(parsed.query)
if "code" in params:
code = params["code"][0]
with open("/tmp/oauth_code.txt", "w") as f:
f.write(code)
self.send_response(200)
self.send_header("Content-Type", "text/html")
self.end_headers()
self.wfile.write(b"<h1>Got it!</h1><p>Authorization code received. You can close this tab.</p>")
print(f"CODE={code}", flush=True)
elif "error" in params:
err = params.get("error", [""])[0]
desc = params.get("error_description", [""])[0]
self.send_response(200)
self.send_header("Content-Type", "text/html")
self.end_headers()
self.wfile.write(f"<h1>Error</h1><p>{err}: {desc}</p>".encode())
print(f"ERROR: {err} - {desc}", flush=True)
else:
self.send_response(400)
self.end_headers()
self.wfile.write(b"Unexpected request")
print(f"Unexpected: {self.path}", flush=True)
import threading
threading.Thread(target=self.server.shutdown).start()
def log_message(self, format, *args):
pass
print("Listening on http://localhost:8400 ...", flush=True)
print("Open the authorization URL in your browser.", flush=True)
http.server.HTTPServer(("", 8400), Handler).serve_forever()
'
```
**Important**: Use a timeout of at least 120000ms for this bash command since the user
needs time to authenticate in the browser.
Tell the user to open the authorization URL in their browser. After they authenticate
with their IdP, the browser will redirect to `http://localhost:8400/callback?code=<code>`,
the server will capture it and shut down.
## Step 7: Exchange Code for Token
```bash
curl -s -X POST <token_endpoint> \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=<AUTH_CODE>" \
-d "client_id=<CLIENT_ID>" \
-d "redirect_uri=http://localhost:8400/callback" \
-d "code_verifier=<CODE_VERIFIER>"
```
Expected response:
```json
{
"access_token": "oauth:<token>",
"token_type": "bearer",
"expires_in": 900,
"scope": "",
"resource": "https://<hostname>/",
"refresh_token": "oauth:<refresh_token>"
}
```
Save the **access_token** and **refresh_token**.
## Step 8: Access the Protected Resource
```bash
curl -s https://<hostname>/ \
-H "Authorization: Bearer <access_token>"
```
This should now return the actual content behind Cloudflare Access.
## Step 9: Refresh the Token (if needed)
If the access token expires (default 900 seconds), use the refresh token:
```bash
curl -s -X POST <token_endpoint> \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=<REFRESH_TOKEN>" \
-d "client_id=<CLIENT_ID>"
```
## Quick Reference: Full Flow Summary
```
1. curl -sI <URL> # Detect 401 + www-authenticate header
2. curl -s <resource_metadata_url> # Get authorization server
3. curl -s <as>/.well-known/oauth-authorization-server # Get endpoints
4. POST <registration_endpoint> # Register public client
5. Generate PKCE code_verifier + challenge # S256, alphanumeric start
6. Start localhost:8400 listener # Catch callback
7. User opens authorization URL # Browser-based IdP auth
8. POST <token_endpoint> # Exchange code for token
9. curl -H "Authorization: Bearer <token>" # Access resource
```
## Troubleshooting
| Problem | Cause | Fix |
| ------------------------------------------------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `code_challenge_method must be S256 for public clients` | Code challenge starts with `-` or `_`, corrupting the URL parameter | Regenerate until challenge starts with `[a-zA-Z0-9]` |
| `invalid_grant` on token exchange | Code expired or verifier mismatch | Redo the auth flow; codes are single-use and short-lived |
| 401 after using token | Token expired (default 15 min) | Use refresh token to get a new access token |
| No `www-authenticate` header | Site doesn't support OAuth resource metadata | Fall back to `cloudflared access curl` or browser auth |
| No `registration_endpoint` in AS metadata | Dynamic registration not enabled | Must use a pre-registered client or different auth method |
| Port 8400 already in use | Previous listener didn't shut down | Kill the process or use a different port (update redirect_uri accordingly) |