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