Sifa professional network API (Fastify, AT Protocol, Jetstream)
sifa.id/
1import type { NodeSavedState, NodeSavedStateStore } from '@atproto/oauth-client-node';
2
3/**
4 * Valkey (Redis-compatible) backed state store for ATproto OAuth.
5 * Implements the SimpleStore<string, NodeSavedState> interface required by NodeOAuthClient.
6 *
7 * OAuth authorization state is short-lived (10 minute TTL) so Valkey is a
8 * natural fit -- no database writes needed for ephemeral data that expires
9 * quickly.
10 */
11export class ValkeyStateStore implements NodeSavedStateStore {
12 constructor(private valkey: ValkeyClient) {}
13
14 async get(key: string): Promise<NodeSavedState | undefined> {
15 const data = await this.valkey.get(`oauth-state:${key}`);
16 return data ? (JSON.parse(data) as NodeSavedState) : undefined;
17 }
18
19 async set(key: string, val: NodeSavedState): Promise<void> {
20 // 10 minute TTL -- OAuth state should be consumed quickly
21 await this.valkey.set(`oauth-state:${key}`, JSON.stringify(val), 'EX', 600);
22 }
23
24 async del(key: string): Promise<void> {
25 await this.valkey.del(`oauth-state:${key}`);
26 }
27}
28
29/**
30 * Minimal interface for a Redis/Valkey client. Compatible with ioredis and
31 * node-redis. Using a structural type avoids coupling to a specific client
32 * library.
33 */
34export interface ValkeyClient {
35 get(key: string): Promise<string | null>;
36 set(key: string, value: string, exFlag: 'EX', seconds: number): Promise<unknown>;
37 // eslint-disable-next-line @typescript-eslint/no-explicit-any
38 del(...args: any[]): Promise<number>;
39}