← Cloudflare One / cloudflare-one / access-controls / applications / http-apps / authorization-cookie
Ověřit JWT
Když Cloudflare odešle požadavek na váš origin server, bude požadavek obsahovat token aplikace jako Cf-Access-Jwt-Assertion hlavička požadavku. Požadavky odeslané přes prohlížeč token předají také jako CF_Authorization cookie.
Cloudflare podepisuje token párem klíčů jedinečným pro váš účet. Token byste měli ověřit pomocí svého veřejného klíče, abyste se ujistili, že požadavek pochází z Access a ne od škodlivé třetí strany. Doporučujeme ověřit Cf-Access-Jwt-Assertion hlavičku místo CF_Authorization cookie, protože není zaručeno, že se tento cookie předá.
Podpisové klíče Access
Veřejný klíč pro pár podpisových klíčů se nachází na https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/certs, kde <your-team-name> je váš Cloudflare One název týmu.
Access ve výchozím nastavení otáčí podepisovací klíč každých 6 týdnů. To znamená, že při každé rotaci budete muset klíče aktualizovat programově nebo ručně. Předchozí klíče zůstávají platné ještě 7 dní po rotaci, aby na aktualizaci zbyl dostatek času.
Rotaci klíče můžete také provést ručně pomocí API. To lze provést pro účely testování nebo zabezpečení.
Jak je uvedeno v příkladu níže, https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/certs obsahuje dva veřejné klíče: aktuální klíč, kterým se podepisují všechny nové tokeny, a předchozí klíč, který byl nahrazen.
keys: oba klíče ve formátu JWKpublic_cert: aktuální klíč ve formátu PEMpublic_certs: oba klíče ve formátu PEM
{
"keys": [
{
"kid": "1a1c3986a44ce6390be42ec772b031df8f433fdc71716db821dc0c39af3bce49",
"kty": "RSA",
"alg": "RS256",
"use": "sig",
"e": "AQAB",
"n": "5PKw-...-AG7MyQ"
},
{
"kid": "6c3bffef71bb0a90c9cbef3b7c0d4a1c7b4b8b76b80292a623afd9dac45d1c65",
"kty": "RSA",
"alg": "RS256",
"use": "sig",
"e": "AQAB",
"n": "pwVn...AA6Hw"
}
],
"public_cert": {
"kid": "6c3bffef71bb0a90c9cbef3b7c0d4a1c7b4b8b76b80292a623afd9dac45d1c65",
"cert": "-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- "
},
"public_certs": [
{
"kid": "1a1c3986a44ce6390be42ec772b031df8f433fdc71716db821dc0c39af3bce49",
"cert": "-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- "
},
{
"kid": "6c3bffef71bb0a90c9cbef3b7c0d4a1c7b4b8b76b80292a623afd9dac45d1c65",
"cert": "-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- "
}
]
}Ověřte JWT ručně
Chcete-li ověřit token ručně:
-
Zkopírujte JWT z
Cf-Access-Jwt-Assertionhlavička požadavku. -
Přejděte na jwt.io ↗.
-
Vyberte algoritmus RS256.
-
Vložte JWT do Zakódováno poli.
-
V Payload poli zajistěte, aby
isspole odkazuje na doménu vašeho týmu (https://<your-team-name>.cloudflareaccess.com).jwt.iopoužíváisshodnotu k načtení veřejného klíče pro ověření tokenu. -
Ujistěte se, že stránka zobrazuje text Podpis ověřen.
Nyní si můžete být jisti, že tento požadavek odeslal Access.
Programové ověření
Na svém origin serveru můžete spustit automatizovaný skript, který ověřuje příchozí požadavky. Ukázkový kód získá z požadavku token aplikace a porovná jeho podpis s vaším veřejným klíčem. Do ukázkového kódu musíte doplnit vlastní team doménu a tag Application Audience (AUD).
Získání AUD tagu
Cloudflare Access přiřadí každé aplikaci jedinečný AUD tag. aud claim v payloadu tokenu určuje, pro kterou aplikaci je JWT platný.
Chcete-li získat AUD tag:
- V Cloudflare dashboard ↗, přejděte na Zero Trust > Řízení přístupu > Aplikace.
- Vyberte Konfigurovat pro vaši aplikaci.
- Od Další nastavení, zkopírujte Application Audience (AUD) Tag.
AUD tag nyní můžete vložit do svého ověřovacího skriptu tokenu. AUD tag se nezmění, dokud aplikaci Access neodstraníte nebo znovu nevytvoříte.
Příklad Cloudflare Workers
Když se Cloudflare Access nachází před vaší Worker, váš Worker musí i tak ověřit JWT, který Cloudflare Access přidává do Cf-Access-Jwt-Assertion hlavičku v příchozím požadavku.
Následující kód ověří JWT pomocí balíček NPM jose ↗:
import { jwtVerify, createRemoteJWKSet } from "jose";
export default {
async fetch(request, env, ctx) {
// Verify the POLICY_AUD environment variable is set
if (!env.POLICY_AUD) {
return new Response("Missing required audience", {
status: 403,
headers: { "Content-Type": "text/plain" },
});
}
// Get the JWT from the request headers
const token = request.headers.get("cf-access-jwt-assertion");
// Check if token exists
if (!token) {
return new Response("Missing required CF Access JWT", {
status: 403,
headers: { "Content-Type": "text/plain" },
});
}
try {
// Create JWKS from your team domain
const JWKS = createRemoteJWKSet(
new URL(`${env.TEAM_DOMAIN}/cdn-cgi/access/certs`),
);
// Verify the JWT
const { payload } = await jwtVerify(token, JWKS, {
issuer: env.TEAM_DOMAIN,
audience: env.POLICY_AUD,
});
// Token is valid, proceed with your application logic
return new Response(`Hello ${payload.email || "authenticated user"}!`, {
headers: { "Content-Type": "text/plain" },
});
} catch (error) {
// Token verification failed
const message = error instanceof Error ? error.message : "Unknown error";
return new Response(`Invalid token: ${message}`, {
status: 403,
headers: { "Content-Type": "text/plain" },
});
}
},
};import { jwtVerify, createRemoteJWKSet } from "jose";
interface Env {
POLICY_AUD: string;
TEAM_DOMAIN: string;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
// Verify the POLICY_AUD environment variable is set
if (!env.POLICY_AUD) {
return new Response("Missing required audience", {
status: 403,
headers: { "Content-Type": "text/plain" },
});
}
// Get the JWT from the request headers
const token = request.headers.get("cf-access-jwt-assertion");
// Check if token exists
if (!token) {
return new Response("Missing required CF Access JWT", {
status: 403,
headers: { "Content-Type": "text/plain" },
});
}
try {
// Create JWKS from your team domain
const JWKS = createRemoteJWKSet(
new URL(`${env.TEAM_DOMAIN}/cdn-cgi/access/certs`)
);
// Verify the JWT
const { payload } = await jwtVerify(token, JWKS, {
issuer: env.TEAM_DOMAIN,
audience: env.POLICY_AUD,
});
// Token is valid, proceed with your application logic
return new Response(
`Hello ${payload.email || "authenticated user"}!`,
{
headers: { "Content-Type": "text/plain" },
}
);
} catch (error) {
// Token verification failed
const message = error instanceof Error ? error.message : "Unknown error";
return new Response(`Invalid token: ${message}`, {
status: 403,
headers: { "Content-Type": "text/plain" },
});
}
},
};Požadované proměnné prostředí
Přidejte tyto proměnné prostředí do vašeho Workeru:
POLICY_AUD: Vaší aplikace Značka AUDTEAM_DOMAIN:https://<your-team-name>.cloudflareaccess.com, kde<your-team-name>se nahrazuje vaší skutečnou název týmu.
Tyto proměnné můžete nastavit přidáním do svého Workeru Konfigurační soubor Wrangler, nebo prostřednictvím dashboardu Cloudflare v části Workers & Pages > your-worker > Nastavení > Proměnné prostředí.
Příklad v Golangu
package main
import (
"context"
"fmt"
"net/http"
"github.com/coreos/go-oidc/v3/oidc"
)
var (
ctx = context.TODO()
teamDomain = "https://test.cloudflareaccess.com"
certsURL = fmt.Sprintf("%s/cdn-cgi/access/certs", teamDomain)
// The Application Audience (AUD) tag for your application
policyAUD = "4714c1358e65fe4b408ad6d432a5f878f08194bdb4752441fd56faefa9b2b6f2"
config = &oidc.Config{
ClientID: policyAUD,
}
keySet = oidc.NewRemoteKeySet(ctx, certsURL)
verifier = oidc.NewVerifier(teamDomain, keySet, config)
)
// VerifyToken is a middleware to verify a CF Access token
func VerifyToken(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
headers := r.Header
// Make sure that the incoming request has our token header
// Could also look in the cookies for CF_AUTHORIZATION
accessJWT := headers.Get("Cf-Access-Jwt-Assertion")
if accessJWT == "" {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("No token on the request"))
return
}
// Verify the access token
ctx := r.Context()
_, err := verifier.Verify(ctx, accessJWT)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(fmt.Sprintf("Invalid token: %s", err.Error())))
return
}
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
func MainHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("welcome"))
})
}
func main() {
http.Handle("/", VerifyToken(MainHandler()))
http.ListenAndServe(":3000", nil)
}Příklad v Pythonu
pip nainstalujte následující:
- flask
- požadavky
- PyJWT
- kryptografie
from flask import Flask, request
import requests
import jwt
import json
import os
app = Flask(__name__)
# The Application Audience (AUD) tag for your application
POLICY_AUD = os.getenv("POLICY_AUD")
# Your CF Access team domain
TEAM_DOMAIN = os.getenv("TEAM_DOMAIN")
CERTS_URL = "{}/cdn-cgi/access/certs".format(TEAM_DOMAIN)
def _get_public_keys():
"""
Returns:
List of RSA public keys usable by PyJWT.
"""
r = requests.get(CERTS_URL)
public_keys = []
jwk_set = r.json()
for key_dict in jwk_set['keys']:
public_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(key_dict))
public_keys.append(public_key)
return public_keys
def verify_token(f):
"""
Decorator that wraps a Flask API call to verify the CF Access JWT
"""
def wrapper():
# Check for the POLICY_AUD environment variable
if not POLICY_AUD:
return "missing required audience", 403
token = ''
if 'CF_Authorization' in request.cookies:
token = request.cookies['CF_Authorization']
else:
return "missing required cf authorization token", 403
keys = _get_public_keys()
# Loop through the keys since we can't pass the key set to the decoder
valid_token = False
for key in keys:
try:
# decode returns the claims that has the email when needed
jwt.decode(token, key=key, audience=POLICY_AUD, algorithms=['RS256'])
valid_token = True
break
except:
pass
if not valid_token:
return "invalid token", 403
return f()
return wrapper
@app.route('/')
@verify_token
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run()Příklad v JavaScriptu (Node.js)
const express = require("express");
const jose = require("jose");
// The Application Audience (AUD) tag for your application
const AUD = process.env.POLICY_AUD;
// Your CF Access team domain
const TEAM_DOMAIN = process.env.TEAM_DOMAIN;
const CERTS_URL = `${TEAM_DOMAIN}/cdn-cgi/access/certs`;
const JWKS = jose.createRemoteJWKSet(new URL(CERTS_URL));
// verifyToken is a middleware to verify a CF authorization token
const verifyToken = async (req, res, next) => {
// Check for the AUD environment variable
if (!AUD) {
return res.status(403).send({
status: false,
message: "missing required audience",
});
}
const token = req.headers["cf-access-jwt-assertion"];
// Make sure that the incoming request has our token header
if (!token) {
return res.status(403).send({
status: false,
message: "missing required cf authorization token",
});
}
try {
const result = await jose.jwtVerify(token, JWKS, {
issuer: TEAM_DOMAIN,
audience: AUD,
});
req.user = result.payload;
next();
} catch (err) {
return res.status(403).send({
status: false,
message: "invalid token",
});
}
};
const app = express();
app.use(verifyToken);
app.get("/", (req, res) => {
res.send("Hello World!");
});
app.listen(3333);