CF

IP & Location Signer

Cloudflare Worker + D1 + ES256 Asymmetric Key Management

📖 API Documentation

Cryptographically capture and verify edge geolocation metadata with ES256 signatures.

Admin Dashboard →/.well-known/jwks.json ↗

1. Service Overview

The Edge Location Signer allows your web and mobile applications to obtain cryptographically verifiable location data directly at Cloudflare's edge network.

Architecture Summary: Your backend server generates a request token using your Private Key. Your frontend client sends the token to POST /api/location. Cloudflare Worker extracts the request IP and location metadata, packages it into an ES256 JWT, signs it using the Worker's Private Key, and returns the token.

2. Authentication & Security Specifications

Authentication uses asymmetric public/private keypairs to guarantee that private keys are never stored on the Cloudflare Worker or database.

3. Request Signing Protocol

To request a signed location JWT, follow these three steps:

1
Construct JWT Header & Payload

Header: {"alg": "ES256", "typ": "JWT", "kid": "YOUR_KEY_ID"}
Payload: {"kid": "YOUR_KEY_ID", "timestamp": 1722870000}

2
Sign Payload with Client Private Key

Sign header.payload using your ES256 private key to produce a client request JWT.

3
Send POST Request to /api/location

Submit the signed JWT in the request body to receive the signed location JWT.

4. Returned Location JWT Claims

When a location request is verified, the returned token contains the following claims:

ipstringrequired
Client IP address captured at the Cloudflare edge network.
Example: "203.0.113.195"
citystring
City name associated with the request IP.
Example: "San Francisco"
regionstring
Region or state name/code.
Example: "California"
countrystringrequired
2-letter ISO country code.
Example: "US"
latitudestring
Approximate latitude coordinate.
Example: "37.7749"
longitudestring
Approximate longitude coordinate.
Example: "-122.4194"
postalCodestring
Postal or ZIP code.
Example: "94107"
timezonestring
Timezone identifier string.
Example: "America/Los_Angeles"
asnnumber
Autonomous System Number associated with the client network.
Example: 13335
asOrganizationstring
ISP or Autonomous System Organization name.
Example: "Cloudflare, Inc."
clientKidstringrequired
Key ID of the client key used to sign the request.
Example: "key_abc12345"
issstringrequired
Issuer claim identifying the location verification service.
Example: "cf-location.fution.co"
iatnumberrequired
Standard JWT Issued At claim (Unix timestamp in seconds).
Example: 1722870000

5. Integration Code Examples

Part 1: Your Backend Server (Generating Token & Verifying JWKS)

Generate a signed request token using your Private Key and verify the returned location token using OpenID JWKS:

TypeScript — Backend Server
// Backend Server (Node.js / Express / Next.js API / Fastify)
// Standard dependencies: npm install jsonwebtoken jose
import jwt from "jsonwebtoken";
import { createRemoteJWKSet, jwtVerify } from "jose";

const CF_WORKER_URL = "https://your-worker-name.workers.dev";
const KEY_ID = "key_abc12345";
const PRIVATE_KEY_PEM = process.env.CLIENT_PRIVATE_KEY_PEM!;

/**
 * Part 1A: Generate a client-signed request JWT for your frontend
 */
export function generateRequestToken(): string {
  // Sign JWT payload containing kid & Unix timestamp (in seconds)
  return jwt.sign(
    { 
      kid: KEY_ID, 
      timestamp: Math.floor(Date.now() / 1000) 
    },
    PRIVATE_KEY_PEM,
    {
      algorithm: "ES256",
      keyid: KEY_ID,
      expiresIn: "60s" // Must be within +/- 60s of server time
    }
  );
}

/**
 * Part 1B: Verify returned location JWT from Cloudflare Worker
 */
export async function verifyReturnedLocationToken(locationJwt: string) {
  // Fetch Cloudflare Worker server public key via OpenID JWKS endpoint
  const JWKS = createRemoteJWKSet(
    new URL("/.well-known/jwks.json", CF_WORKER_URL)
  );

  const { payload } = await jwtVerify(locationJwt, JWKS, {
    algorithms: ["ES256"]
  });

  console.log("Verified Edge Location Claims:", payload);
  return payload;
}

Part 2: Frontend Client (Cross-Domain POST)

The frontend receives the requestToken from your backend server and posts it to the location API:

TypeScript — Frontend Client
// Frontend Client (Browser / React / Vue / Vanilla JS)
// Note: Cross-domain CORS requests to POST /api/location are supported natively.

async function captureEdgeLocation(requestTokenFromBackend: string) {
  // 1. Post signed request token to Cloudflare Worker Location API
  const response = await fetch("https://your-worker-name.workers.dev/api/location", {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      token: requestTokenFromBackend
    })
  });

  const data = await response.json();
  if (!response.ok) {
    throw new Error(data.message || "Failed to verify location");
  }

  // 2. Returns { success: true, token: "<signed_location_jwt>" }
  console.log("Received Signed Location JWT:", data.token);

  // 3. Pass returned location JWT back to your server for session verification
  await fetch("/api/my-backend/verify-session", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ locationToken: data.token })
  });
}

6. Public API Reference

EndpointMethodAuthenticationDescription
/api/locationPOSTClient Signed JWTVerifies request signature & returns signed edge location JWT.
/.well-known/jwks.jsonGETPublic DiscoveryServes OpenID JWKS public key for verifying returned location JWTs.