/** * 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 { // Use DID as the record ID for direct lookup // Escape special characters in the DID for SurrealDB record ID const recordId = `oauth_session:⟨${did}⟩`; // Upsert: update if exists, create if doesn't const existing = await db.select<{ session_data: NodeSavedSession }>(recordId); if (Array.isArray(existing) && existing.length > 0) { // Update existing record await db.merge(recordId, { session_data: sessionData, updated_at: new Date().toISOString(), }); } else { // Create new record await db.create(recordId, { did, session_data: sessionData, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }); } } finally { await db.close(); } }, async get(did: string): Promise { const db = await getDB(); try { // Select directly by record ID const result = await db.select<{ session_data: NodeSavedSession }>(`oauth_session:⟨${did}⟩`); // db.select() returns an array when selecting a specific record ID const record = Array.isArray(result) ? result[0] : result; return record?.session_data; } finally { await db.close(); } }, async del(did: string): Promise { const db = await getDB(); try { // Delete directly by record ID await db.delete(`oauth_session:⟨${did}⟩`); } finally { await db.close(); } }, }; }