| 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.recommend?action=create&action=delete 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: OAUTH_SCOPE, |
| 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 | } |