edge-oauth-client

An edge-compatible ATProto OAuth client for Cloudflare Workers. Extends @atproto/oauth-client with WebCrypto-based cryptography, edge-friendly DID and handle resolution, and Cloudflare Workers compatibility patches.

Installation

bash
npm install @atiproto/edge-oauth-client @atiproto/kv-oauth-state-store

You will also need @atproto/oauth-client as a peer dependency.

Quick Setup

Call patchGlobalRequestObject() once at the top of your Worker entrypoint, then create an EdgeOAuthClient with your KV-backed state and session stores:

typescript
import { EdgeOAuthClient, patchGlobalRequestObject } from "@atiproto/edge-oauth-client";
import { KvStateStore, KvSessionStore } from "@atiproto/kv-oauth-state-store";
// Required once — strips unsupported Request.cache property for Workers
patchGlobalRequestObject();
interface Env {
OAUTH_STATE_KV: KVNamespace;
OAUTH_SESSION_KV: KVNamespace;
}
function createOAuthClient(env: Env) {
return new EdgeOAuthClient({
clientMetadata: {
client_id: "https://your-app.example.com/oauth/client-metadata.json",
client_name: "My App",
client_uri: "https://your-app.example.com",
redirect_uris: ["https://your-app.example.com/oauth/callback"],
grant_types: ["authorization_code", "refresh_token"],
response_types: ["code"],
scope: "atproto transition:generic",
token_endpoint_auth_method: "none",
application_type: "web",
dpop_bound_access_tokens: true,
},
stateStore: new KvStateStore(env.OAUTH_STATE_KV),
sessionStore: new KvSessionStore(env.OAUTH_SESSION_KV),
});
}

Authorization Flow

Start login

typescript
async function handleLogin(request: Request, env: Env) {
const client = createOAuthClient(env);
const handle = new URL(request.url).searchParams.get("handle");
const url = await client.authorize(handle, {
scope: "atproto transition:generic",
});
return Response.redirect(url.toString());
}

Handle callback

typescript
async function handleCallback(request: Request, env: Env) {
const client = createOAuthClient(env);
const { session } = await client.callback(
new URL(request.url).searchParams
);
// Store session.did in a cookie or session store
return new Response(JSON.stringify({ did: session.did }));
}

Restore a session

typescript
async function handleApiRequest(request: Request, env: Env) {
const client = createOAuthClient(env);
const session = await client.restore("did:plc:user123");
// session.fetchHandler is an authenticated fetch
return session.fetchHandler(
"https://bsky.social/xrpc/app.bsky.actor.getProfile",
{ method: "GET" }
);
}

Classes

  • EdgeOAuthClient — Main OAuth client; constructor options and defaults
  • EdgeRuntimeImplementation — WebCrypto-based RuntimeImplementation for edge runtimes
  • getKeyset — Builds and caches a Keyset from a private JWK for confidential clients
  • getKeyset — Builds and caches a Keyset from a private JWK for confidential clients
  • patchGlobalRequestObject — Patches globalThis.Request to strip unsupported cache property