import { SimpleStore } from "@atproto-labs/simple-store"; import { Jwk, Key } from "@atproto/jwk"; import { JoseKey } from "@atproto/jwk-jose"; import Storage from "expo-sqlite/kv-store"; interface HasDPoPKey { dpopKey: Key | undefined; } const NAMESPACE = `@@atproto/oauth-client-react-native`; /** * An expo-sqlite store that handles serializing and deserializing * our Jose DPoP keys. Wraps SQLiteKVStore or whatever other SimpleStore * that a user might provide. */ export class JoseKeyStore { private store: SimpleStore; constructor(store: SimpleStore) { this.store = store; } async get(key: string): Promise { const itemStr = await this.store.get(key); if (!itemStr) return undefined; const item = JSON.parse(itemStr) as T; if (item.dpopKey) { item.dpopKey = new JoseKey(item.dpopKey as unknown as Jwk); } return item; } async set(key: string, value: T): Promise { if (value.dpopKey) { value = { ...value, dpopKey: (value.dpopKey as JoseKey).privateJwk, }; } return await this.store.set(key, JSON.stringify(value)); } async del(key: string): Promise { return await this.store.del(key); } } /** * Simple wrapper around expo-sqlite's KVStore. Default implementation * unless a user brings their own KV store. */ export class SQLiteKVStore implements SimpleStore { private namespace: string; constructor(namespace: string) { this.namespace = `${NAMESPACE}:${namespace}`; } async get(key: string): Promise { return (await Storage.getItem(`${this.namespace}:${key}`)) ?? undefined; } async set(key: string, value: string): Promise { return await Storage.setItem(`${this.namespace}:${key}`, value); } async del(key: string): Promise { return await Storage.removeItem(`${this.namespace}:${key}`); } }