Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
4.0 kB · 134 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135import { JoseKey, Keyset, NodeOAuthClient, buildAtprotoLoopbackClientMetadata, type Fetch, type OAuthClientMetadataInput, type NodeSavedSession, type NodeSavedState,} from "@atproto/oauth-client-node";import { getDb } from "../db";
export const SCOPE = "atproto repo:xyz.statusphere.status";// Next's patched fetch currently loses the body source when it receives a// Request object on Node 24. Materialize the body and pass URL + init instead.// https://github.com/vercel/next.js/issues/90826const fetchWithBodySource: Fetch = async (input, init) => { if (!(input instanceof Request)) { return fetch(input, init); }
const request = new Request(input, init); const body = request.body ? await request.arrayBuffer() : undefined;
return fetch(request.url, { ...init, method: request.method, headers: request.headers, body, redirect: request.redirect, signal: request.signal, });};
let client: NodeOAuthClient | null = null;
const PUBLIC_URL = process.env.PUBLIC_URL;const PRIVATE_KEY = process.env.PRIVATE_KEY;
function getClientMetadata(): OAuthClientMetadataInput { if (PUBLIC_URL) { return { client_id: `${PUBLIC_URL}/oauth-client-metadata.json`, client_name: "atproto OAuth Tutorial", client_uri: PUBLIC_URL, redirect_uris: [`${PUBLIC_URL}/oauth/callback`], grant_types: ["authorization_code", "refresh_token"], response_types: ["code"], scope: SCOPE, token_endpoint_auth_method: "private_key_jwt" as const, token_endpoint_auth_signing_alg: "ES256" as const, // must match the alg in scripts/gen-key.ts jwks_uri: `${PUBLIC_URL}/.well-known/jwks.json`, dpop_bound_access_tokens: true, }; } else { return buildAtprotoLoopbackClientMetadata({ scope: SCOPE, redirect_uris: ["http://127.0.0.1:3000/oauth/callback"], }); }}
async function getKeyset(): Promise<Keyset | undefined> { if (PUBLIC_URL && PRIVATE_KEY) { return new Keyset([await JoseKey.fromJWK(JSON.parse(PRIVATE_KEY))]); } else { return undefined; }}
export async function getOAuthClient(): Promise<NodeOAuthClient> { if (client) return client;
client = new NodeOAuthClient({ clientMetadata: getClientMetadata(), keyset: await getKeyset(), fetch: fetchWithBodySource,
stateStore: { async get(key: string) { const db = getDb(); const row = await db .selectFrom("auth_state") .select("value") .where("key", "=", key) .executeTakeFirst(); return row ? JSON.parse(row.value) : undefined; }, async set(key: string, value: NodeSavedState) { const db = getDb(); const valueJson = JSON.stringify(value); await db .insertInto("auth_state") .values({ key, value: valueJson }) .onConflict((oc) => oc.column("key").doUpdateSet({ value: valueJson }), ) .execute(); }, async del(key: string) { const db = getDb(); await db.deleteFrom("auth_state").where("key", "=", key).execute(); }, }, sessionStore: { async get(key: string) { const db = getDb(); const row = await db .selectFrom("auth_session") .select("value") .where("key", "=", key) .executeTakeFirst(); return row ? JSON.parse(row.value) : undefined; }, async set(key: string, value: NodeSavedSession) { const db = getDb(); const valueJson = JSON.stringify(value); await db .insertInto("auth_session") .values({ key, value: valueJson }) .onConflict((oc) => oc.column("key").doUpdateSet({ value: valueJson }), ) .execute(); }, async del(key: string) { const db = getDb(); await db.deleteFrom("auth_session").where("key", "=", key).execute(); }, }, });
return client;}