ANProto over ATProto -- using Bluesky PDSes to store ANProto messages and blobs
1import type {
2 NodeSavedSession,
3 NodeSavedSessionStore,
4 NodeSavedState,
5 NodeSavedStateStore,
6} from '@atproto/oauth-client-node'
7import { db } from './db'
8
9/**
10 * StateStore:
11 * Stores temporary "state" parameters used during the initial OAuth handshake (authorize -> callback).
12 * This prevents CSRF attacks by ensuring the callback comes from the same flow we started.
13 * These are short-lived and can be deleted after the callback is processed.
14 */
15export class StateStore implements NodeSavedStateStore {
16 async get(key: string): Promise<NodeSavedState | undefined> {
17 const result = db.prepare('SELECT state FROM auth_state WHERE key = ?').get(key) as { state: string } | undefined
18 if (!result) return
19 return JSON.parse(result.state) as NodeSavedState
20 }
21 async set(key: string, val: NodeSavedState) {
22 const state = JSON.stringify(val)
23 db.prepare(`
24 INSERT INTO auth_state (key, state) VALUES (?, ?)
25 ON CONFLICT(key) DO UPDATE SET state = excluded.state
26 `).run(key, state)
27 }
28 async del(key: string) {
29 db.prepare('DELETE FROM auth_state WHERE key = ?').run(key)
30 }
31}
32
33/**
34 * SessionStore:
35 * Persists the long-term OAuth session data (Access Token, Refresh Token, DID).
36 * This allows the user to stay logged in even if the server restarts.
37 * Keys are usually mapped to the user's DID.
38 */
39export class SessionStore implements NodeSavedSessionStore {
40 async get(key: string): Promise<NodeSavedSession | undefined> {
41 const result = db.prepare('SELECT session FROM auth_session WHERE key = ?').get(key) as { session: string } | undefined
42 if (!result) return
43 return JSON.parse(result.session) as NodeSavedSession
44 }
45 async set(key: string, val: NodeSavedSession) {
46 const session = JSON.stringify(val)
47 db.prepare(`
48 INSERT INTO auth_session (key, session) VALUES (?, ?)
49 ON CONFLICT(key) DO UPDATE SET session = excluded.session
50 `).run(key, session)
51 }
52 async del(key: string) {
53 db.prepare('DELETE FROM auth_session WHERE key = ?').run(key)
54 }
55}