A CLI for publishing standard.site documents to ATProto
sequoia.pub
standard
site
lexicon
cli
publishing
1import { JoseKey } from "@atproto/jwk-jose";
2import { OAuthClient } from "@atproto/oauth-client";
3import { AtprotoDohHandleResolver } from "@atproto-labs/handle-resolver";
4import type { Database } from "bun:sqlite";
5import { createStateStore, createSessionStore } from "./stores";
6
7export const OAUTH_SCOPE =
8 "atproto repo:site.standard.graph.subscription?action=create&action=delete";
9
10export 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}