an atproto based link aggregator
at main 78 lines 2.4 kB view raw
1import type { 2 NodeSavedSession, 3 NodeSavedSessionStore, 4 NodeSavedState, 5 NodeSavedStateStore 6} from '@atproto/oauth-client-node'; 7import { eq, lt } from 'drizzle-orm'; 8import type { Database } from '../db'; 9import { authState, authSession } from '../db/schema'; 10 11/** 12 * Database-backed state store for OAuth flow. 13 * States are short-lived (15 min) and used during the authorization flow. 14 */ 15export class StateStore implements NodeSavedStateStore { 16 constructor(private db: Database) {} 17 18 async get(key: string): Promise<NodeSavedState | undefined> { 19 const result = await this.db.select().from(authState).where(eq(authState.key, key)).limit(1); 20 21 if (result.length === 0) return undefined; 22 return JSON.parse(result[0].state) as NodeSavedState; 23 } 24 25 async set(key: string, val: NodeSavedState): Promise<void> { 26 const state = JSON.stringify(val); 27 const now = new Date().toISOString(); 28 29 await this.db.insert(authState).values({ key, state, createdAt: now }).onConflictDoUpdate({ 30 target: authState.key, 31 set: { state } 32 }); 33 34 // Clean up old states (older than 15 minutes) 35 const fifteenMinutesAgo = new Date(Date.now() - 15 * 60 * 1000).toISOString(); 36 await this.db.delete(authState).where(lt(authState.createdAt, fifteenMinutesAgo)); 37 } 38 39 async del(key: string): Promise<void> { 40 await this.db.delete(authState).where(eq(authState.key, key)); 41 } 42} 43 44/** 45 * Database-backed session store for OAuth sessions. 46 * Sessions are long-lived and store the authenticated user's tokens. 47 */ 48export class SessionStore implements NodeSavedSessionStore { 49 constructor(private db: Database) {} 50 51 async get(key: string): Promise<NodeSavedSession | undefined> { 52 const result = await this.db 53 .select() 54 .from(authSession) 55 .where(eq(authSession.key, key)) 56 .limit(1); 57 58 if (result.length === 0) return undefined; 59 return JSON.parse(result[0].session) as NodeSavedSession; 60 } 61 62 async set(key: string, val: NodeSavedSession): Promise<void> { 63 const session = JSON.stringify(val); 64 const now = new Date().toISOString(); 65 66 await this.db 67 .insert(authSession) 68 .values({ key, session, createdAt: now, updatedAt: now }) 69 .onConflictDoUpdate({ 70 target: authSession.key, 71 set: { session, updatedAt: now } 72 }); 73 } 74 75 async del(key: string): Promise<void> { 76 await this.db.delete(authSession).where(eq(authSession.key, key)); 77 } 78}