/** * Lex client utilities for ATProto record operations */ import { Client } from '@atproto/lex'; import { IdResolver } from '@atproto/identity'; import { TokenRefreshError } from '@atproto/oauth-client-node'; import { createOAuthClient, getSession } from './auth'; import { localDb } from './db'; const idResolver = new IdResolver(); export class AuthRequiredError extends Error { constructor(message = 'Authentication required') { super(message); this.name = 'AuthRequiredError'; } } /** * Get an unauthenticated lex Client for reading from a user's PDS. * Resolves the DID to find the PDS endpoint. */ export async function getPublicLexClient(did: string): Promise { const atprotoData = await idResolver.did.resolveAtprotoData(did); const pdsUrl = atprotoData.pds; return new Client(pdsUrl); } /** * Get an authenticated lex Client for the current user. * Throws AuthRequiredError if not authenticated. */ export async function getLexClient(cookies: Parameters[0]): Promise { const session = await getSession(cookies); if (!session.did) { throw new AuthRequiredError(); } const oauthClient = await createOAuthClient(localDb); let oauthSession; try { oauthSession = await oauthClient.restore(session.did); } catch (err) { if (err instanceof TokenRefreshError) { // Session was deleted or token refresh failed - clear stale cookie console.log(`[auth] Clearing stale session for ${session.did}: ${(err as Error).message}`); session.did = undefined; await session.save(); } throw err; } if (!oauthSession) { throw new AuthRequiredError(); } // Create lex Client directly from OAuth session return new Client(oauthSession); }