| 1 | import { JoseKey } from "@atproto/jwk-jose"; |
| 2 | import { OAuthClient } from "@atproto/oauth-client"; |
| 3 | import { AtprotoDohHandleResolver } from "@atproto-labs/handle-resolver"; |
| 4 | import type { Database } from "bun:sqlite"; |
| 5 | import { createStateStore, createSessionStore } from "./stores"; |
| 6 | |
| 7 | export const OAUTH_SCOPE = |
| 8 | "atproto repo:site.standard.graph.subscription?action=create&action=delete"; |
| 9 | |
| 10 | export function createOAuthClient( |
| 11 | db: Database, |
| 12 | clientUrl: string, |
| 13 | clientName = "Sequoia", |
| 14 | ) { |
| 15 | const clientId = `${clientUrl}/oauth/client-metadata.json`; |
| 16 | const redirectUri = `${clientUrl}/oauth/callback`; |
| 17 | |
| 18 | const dohEndpoint = |
| 19 | process.env.DOH_ENDPOINT || "https://cloudflare-dns.com/dns-query"; |
| 20 | |
| 21 | return new OAuthClient({ |
| 22 | responseMode: "query", |
| 23 | handleResolver: new AtprotoDohHandleResolver({ dohEndpoint }), |
| 24 | clientMetadata: { |
| 25 | client_id: clientId, |
| 26 | client_name: clientName, |
| 27 | client_uri: clientUrl, |
| 28 | redirect_uris: [redirectUri], |
| 29 | grant_types: ["authorization_code", "refresh_token"], |
| 30 | response_types: ["code"], |
| 31 | scope: OAUTH_SCOPE, |
| 32 | token_endpoint_auth_method: "none", |
| 33 | application_type: "web", |
| 34 | dpop_bound_access_tokens: true, |
| 35 | }, |
| 36 | // eslint-disable-next-line @typescript-eslint/no-explicit-any -- @atproto Key class mismatch across packages |
| 37 | runtimeImplementation: { |
| 38 | createKey: (algs: string[]) => JoseKey.generate(algs) as any, |
| 39 | getRandomValues: (length: number) => |
| 40 | crypto.getRandomValues(new Uint8Array(length)), |
| 41 | digest: async (data: Uint8Array, { name }: { name: string }) => { |
| 42 | const buf = await crypto.subtle.digest( |
| 43 | name.replace("sha", "SHA-"), |
| 44 | new Uint8Array(data), |
| 45 | ); |
| 46 | return new Uint8Array(buf); |
| 47 | }, |
| 48 | requestLock: <T>(_name: string, fn: () => T | PromiseLike<T>) => fn(), |
| 49 | }, |
| 50 | stateStore: createStateStore(db), |
| 51 | sessionStore: createSessionStore(db), |
| 52 | }); |
| 53 | } |