[READ-ONLY] Mirror of https://github.com/flo-bit/svelte-cloudflare-statusphere.
statusphere.atmo.tools
atproto
cloudflare-workers
oauth
svelte
2.7 kB
94 lines
1import {
2 OAuthClient,
3 MemoryStore,
4 type ClientAssertionPrivateJwk,
5 type OAuthClientStores,
6 type OAuthSession,
7 type StoredSession,
8 type StoredState
9} from '@atcute/oauth-node-client';
10import type { Did } from '@atcute/lexicons';
11import {
12 CompositeDidDocumentResolver,
13 CompositeHandleResolver,
14 DohJsonHandleResolver,
15 LocalActorResolver,
16 PlcDidDocumentResolver,
17 WebDidDocumentResolver,
18 WellKnownHandleResolver
19} from '@atcute/identity-resolver';
20import { KVStore } from './kv-store';
21import { DOH_RESOLVER, REDIRECT_PATH } from '$lib/atproto/settings';
22import { scope } from '$lib/atproto/metadata';
23import { dev } from '$app/environment';
24
25function createActorResolver() {
26 return new LocalActorResolver({
27 handleResolver: new CompositeHandleResolver({
28 methods: {
29 dns: new DohJsonHandleResolver({ dohUrl: DOH_RESOLVER }),
30 http: new WellKnownHandleResolver()
31 }
32 }),
33 didDocumentResolver: new CompositeDidDocumentResolver({
34 methods: {
35 plc: new PlcDidDocumentResolver(),
36 web: new WebDidDocumentResolver()
37 }
38 })
39 });
40}
41
42function createStores(env?: App.Platform['env']): OAuthClientStores {
43 if (env?.OAUTH_SESSIONS && env?.OAUTH_STATES) {
44 return {
45 sessions: new KVStore<Did, StoredSession>(env.OAUTH_SESSIONS),
46 states: new KVStore<string, StoredState>(env.OAUTH_STATES, { expirationTtl: 600 })
47 };
48 }
49 // Fallback to in-memory stores (dev without wrangler)
50 return {
51 sessions: new MemoryStore<Did, StoredSession>(),
52 states: new MemoryStore<string, StoredState>({ ttl: 600_000 })
53 };
54}
55
56export function createOAuthClient(env?: App.Platform['env']): OAuthClient {
57 const actorResolver = createActorResolver();
58 const stores = createStores(env);
59
60 if (dev) {
61 // In development, use loopback client (public, no keyset).
62 // Omit client_id — the library builds it automatically from redirect_uris + scope.
63 // redirect_uris must use 127.0.0.1 (not localhost).
64 return new OAuthClient({
65 metadata: {
66 redirect_uris: [`http://127.0.0.1:5183${REDIRECT_PATH}`],
67 scope
68 },
69 actorResolver,
70 stores
71 });
72 }
73
74 // In production, use confidential client with keyset
75 if (!env?.CLIENT_ASSERTION_KEY) {
76 throw new Error('CLIENT_ASSERTION_KEY secret is not set. Run: pnpm generate-key && npx wrangler secret put CLIENT_ASSERTION_KEY');
77 }
78 const key: ClientAssertionPrivateJwk = JSON.parse(env.CLIENT_ASSERTION_KEY);
79 const SITE = 'https://flo-bit.dev';
80
81 return new OAuthClient({
82 metadata: {
83 client_id: SITE + '/oauth-client-metadata.json',
84 redirect_uris: [SITE + REDIRECT_PATH],
85 scope,
86 jwks_uri: SITE + '/oauth/jwks.json'
87 },
88 keyset: [key],
89 actorResolver,
90 stores
91 });
92}
93
94export type { OAuthSession };