Live video on the AT Protocol
1import { SimpleStore } from "@atproto-labs/simple-store";
2import { Jwk, Key } from "@atproto/jwk";
3import { JoseKey } from "@atproto/jwk-jose";
4import Storage from "expo-sqlite/kv-store";
5
6interface HasDPoPKey {
7 dpopKey: Key | undefined;
8}
9
10const NAMESPACE = `@@atproto/oauth-client-react-native`;
11
12/**
13 * An expo-sqlite store that handles serializing and deserializing
14 * our Jose DPoP keys. Wraps SQLiteKVStore or whatever other SimpleStore
15 * that a user might provide.
16 */
17export class JoseKeyStore<T extends HasDPoPKey> {
18 private store: SimpleStore<string, string>;
19 constructor(store: SimpleStore<string, string>) {
20 this.store = store;
21 }
22
23 async get(key: string): Promise<T | undefined> {
24 const itemStr = await this.store.get(key);
25 if (!itemStr) return undefined;
26 const item = JSON.parse(itemStr) as T;
27 if (item.dpopKey) {
28 item.dpopKey = new JoseKey(item.dpopKey as unknown as Jwk);
29 }
30 return item;
31 }
32
33 async set(key: string, value: T): Promise<void> {
34 if (value.dpopKey) {
35 value = {
36 ...value,
37 dpopKey: (value.dpopKey as JoseKey).privateJwk,
38 };
39 }
40 return await this.store.set(key, JSON.stringify(value));
41 }
42
43 async del(key: string): Promise<void> {
44 return await this.store.del(key);
45 }
46}
47
48/**
49 * Simple wrapper around expo-sqlite's KVStore. Default implementation
50 * unless a user brings their own KV store.
51 */
52export class SQLiteKVStore implements SimpleStore<string, string> {
53 private namespace: string;
54 constructor(namespace: string) {
55 this.namespace = `${NAMESPACE}:${namespace}`;
56 }
57
58 async get(key: string): Promise<string | undefined> {
59 return (await Storage.getItem(`${this.namespace}:${key}`)) ?? undefined;
60 }
61
62 async set(key: string, value: string): Promise<void> {
63 return await Storage.setItem(`${this.namespace}:${key}`, value);
64 }
65
66 async del(key: string): Promise<void> {
67 return await Storage.removeItem(`${this.namespace}:${key}`);
68 }
69}