import { JoseKey, NodeOAuthClient, type NodeSavedSession, type NodeSavedState, } from '@atproto/oauth-client-node' import { db } from '#/lib/db' // OAuth PKCE/DPoP state is only needed for the ~10 minutes between /login and // /callback, so we prune anything older than 10 minutes on each set. const STATE_TTL_SECONDS = 600 const rootUrl = process.env.VITE_APP_URL export const client = new NodeOAuthClient({ clientMetadata: { client_id: `${rootUrl}/client-metadata`, client_name: 'Dudesky', client_uri: rootUrl, redirect_uris: [`${rootUrl}/callback`], grant_types: ['authorization_code', 'refresh_token'], scope: 'atproto transition:generic', application_type: 'web', token_endpoint_auth_method: 'private_key_jwt', token_endpoint_auth_signing_alg: 'ES256', dpop_bound_access_tokens: true, jwks_uri: `${rootUrl}/jwks`, }, keyset: await Promise.all([ // biome-ignore lint/style/noNonNullAssertion: required env vars JoseKey.fromImportable(process.env.PRIVATE_KEY_0!, 'key1'), // biome-ignore lint/style/noNonNullAssertion: required env vars JoseKey.fromImportable(process.env.PRIVATE_KEY_1!, 'key2'), // biome-ignore lint/style/noNonNullAssertion: required env vars JoseKey.fromImportable(process.env.PRIVATE_KEY_2!, 'key3'), ]), stateStore: { async set(key: string, value: NodeSavedState): Promise { db.prepare(` INSERT INTO oauth_state (key, value, created_at) VALUES (?, ?, unixepoch()) ON CONFLICT(key) DO UPDATE SET value = excluded.value, created_at = excluded.created_at `).run(key, JSON.stringify(value)) // Prune expired state entries db.prepare( `DELETE FROM oauth_state WHERE created_at < unixepoch() - ?`, ).run(STATE_TTL_SECONDS) }, async get(key: string): Promise { const row = db .prepare(` SELECT value FROM oauth_state WHERE key = ? AND created_at >= unixepoch() - ? `) .get(key, STATE_TTL_SECONDS) as { value: string } | undefined return row ? JSON.parse(row.value) : undefined }, async del(key: string): Promise { db.prepare(`DELETE FROM oauth_state WHERE key = ?`).run(key) }, }, sessionStore: { async set(did: string, value: NodeSavedSession): Promise { db.prepare(` INSERT INTO oauth_session (did, value, updated_at) VALUES (?, ?, unixepoch()) ON CONFLICT(did) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at `).run(did, JSON.stringify(value)) }, async get(did: string): Promise { const row = db .prepare(`SELECT value FROM oauth_session WHERE did = ?`) .get(did) as { value: string } | undefined return row ? JSON.parse(row.value) : undefined }, async del(did: string): Promise { db.prepare(`DELETE FROM oauth_session WHERE did = ?`).run(did) }, }, })