/** * OAuth Session Store for @atproto/oauth-client-node * * Stores persistent OAuth sessions (access/refresh tokens, DPoP keys). * Sessions are keyed by the user's DID. */ import Surreal from 'surrealdb'; import type { NodeSavedSessionStore, NodeSavedSession } from '@atproto/oauth-client-node'; /** * Get a SurrealDB connection with root credentials. * Used for OAuth session management. */ async function getDB(): Promise { const db = new Surreal(); await db.connect(process.env.SURREALDB_URL!); await db.signin({ username: process.env.SURREALDB_USER!, password: process.env.SURREALDB_PASS!, }); await db.use({ namespace: process.env.SURREALDB_NS!, database: process.env.SURREALDB_DB!, }); return db; } /** * Create an OAuth session store backed by SurrealDB. * * The session store persists authenticated user sessions across * server restarts. The @atproto/oauth-client-node library manages * token refresh automatically, updating the store when tokens change. * * Sessions are indexed by DID (decentralized identifier). */ export function createSessionStore(): NodeSavedSessionStore { return { async set(did: string, sessionData: NodeSavedSession): Promise { const db = await getDB(); try { // Upsert: create if doesn't exist, update if it does await db.query( `INSERT INTO oauth_session (did, session_data) VALUES ($did, $session_data) ON DUPLICATE KEY UPDATE session_data = $session_data, updated_at = time::now()`, { did, session_data: sessionData } ); } finally { await db.close(); } }, async get(did: string): Promise { const db = await getDB(); try { const [result] = await db.query<[{ session_data: NodeSavedSession }[]]>( 'SELECT session_data FROM oauth_session WHERE did = $did', { did } ); return result?.[0]?.session_data; } finally { await db.close(); } }, async del(did: string): Promise { const db = await getDB(); try { await db.query( 'DELETE oauth_session WHERE did = $did', { did } ); } finally { await db.close(); } }, }; }