Skip to content

Production Deployment Testing

This guide covers obtaining a token from your Cloudflare-operated deployment. By this point, you and Cloudflare have already worked together to build and provision the pieces below. Use this guide to confirm the deployment issues and redeems tokens end to end.

Privacy Pass is not a self-serve product at the moment: a production deployment is a managed engagement with Cloudflare. If you just want to see Privacy Pass work without any setup, see Getting started.

Contact us to request access and receive your issuer configuration.


Before you begin

What Cloudflare provisions:

  • An issuer endpoint (referred to here as https://your-issuer.example.com), running a public, RFC 9578-compliant issuer.
  • A public directory endpoint at /.well-known/private-token-issuer-directory.
  • A /token-request endpoint.

What you operate or need in place:

  • A working Attester — the service that verifies your claim and, once the Client is verified, proxies the blinded token request to the Issuer. In Cloudflare Issuer deployments, the Client never contacts the Issuer directly. You operate the Attester to keep roles separate; Cloudflare can help build it. See cloudflare/privacypass-attester for an implementation for Cloudflare Workers.
  • The Origin (the web service, application, or website a Client is trying to access) configured to verify tokens against the issuer's public key (or Cloudflare redemption at the edge).
  • The mTLS client certificate your Attester uses to authenticate to the issuer.
  • The client library for running the issuance protocol. Library options include TypeScript, Go, and Rust.

Implement the Client with the Privacy Pass library for your stack:


1. Discover the issuer configuration

Every issuer publishes its configuration at the standard directory endpoint. This endpoint is public, so you can fetch it to confirm the issuer is reachable and read its public keys:

Terminal window
curl https://your-issuer.example.com/.well-known/private-token-issuer-directory

The response lists the token-request endpoint and the issuer's public keys. Token type 2 is Blind RSA:

{
"issuer-request-uri": "/token-request",
"token-keys": [
{
"token-type": 2,
"token-key": "<base64url-encoded public key>",
"not-before": 1700000000
}
]
}

These are the keys your Origin will verify redeemed tokens against.


2. Request and redeem a token

In this deployment the Client never contacts the issuer directly. Getting a token is a round trip across all four roles:

  1. The Origin returns a WWW-Authenticate challenge.
  2. The Client completes attestation with your Attester and sends it a blinded token request.
  3. The Attester verifies the Client, then authenticates to the issuer (for example, with mTLS) and proxies the request to the issuer's /token-request endpoint.
  4. The Issuer signs the blinded request and returns it; the Client unblinds it to recover the finalized token, then redeems it at the Origin.
Origin Client Attester Issuer
│ │ │ │
│- TokenChallenge ─>│ │ │
│ │<=== Attestation ==>│ │
│ │ │── TokenRequest ─>│
│ │ │<─ TokenResponse ─│
│ │<── TokenResponse ──│ │
│<─ Request+Token -─│ [finalize token] │ │
│────- 200 OK ────->│ │ │

Using the TypeScript library for this example, the Client builds the blinded token request, then sends it to your Attester, which proxies it to the issuer and returns the signed response:

TypeScript
import { publicVerif } from '@cloudflare/privacypass-ts';
const { BlindRSAMode, Client } = publicVerif;
// Declare your own sendToAttester transport — see the note below.
declare function sendToAttester(tokenRequest: Uint8Array): Promise<Uint8Array>;
// `tokenChallenge` comes from the Origin's WWW-Authenticate header.
// `issuerPublicKey` is the issuer's public key bytes (from the directory in Step 1).
const client = new Client(BlindRSAMode.PSS);
const tokenRequest = await client.createTokenRequest(tokenChallenge, issuerPublicKey);
// Send the blinded request to your Attester. It verifies the client, proxies the
// request to the issuer, and returns the issuer's signed token response.
const tokenResponseBytes = await sendToAttester(tokenRequest.serialize());
// Deserialize and unblind to recover the finalized token.
const tokenResponse = client.deserializeTokenResponse(tokenResponseBytes);
const token = await client.finalize(tokenResponse);

The Client then redeems the token by sending it to the Origin in an Authorization header. The Origin (or Cloudflare at the edge – see Deployment Models) verifies the signature against the issuer's public key and responds with 200 OK.


3. Verify it works

Two signals confirm issuance and redemption are working end to end:

  • The directory endpoint returns the issuer's token-keys.
  • A redeemed token verifies against the issuer's public key, and the Origin returns 200 OK.

To check that the directory endpoint is reachable, you can use the Privacy Pass demo tool to fetch the issuer's directory.