From 6ef581d64311eb26475680f4608c4ca47944dd6d Mon Sep 17 00:00:00 2001 From: Scott Nedderman Date: Sat, 29 Mar 2025 15:02:48 -0400 Subject: [PATCH] Added RedisStorage option for Issuer --- bun.lockb | Bin 255216 -> 258632 bytes packages/openauth/package.json | 3 +- packages/openauth/src/storage/redis.ts | 73 +++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 packages/openauth/src/storage/redis.ts diff --git a/bun.lockb b/bun.lockb index 7f4b865820733fdfc374b2177bd115d17dacfb6e..b22ae4aed1aa0e2f5a8f06eae0f01ed066c423b6 100755 GIT binary patch delta 42074 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 TK;;1+A%Z(TZr}Bo*^ChYO4=~Q delta 40485 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 diff --git a/packages/openauth/package.json b/packages/openauth/package.json index a5e3276b..33d45f47 100644 --- a/packages/openauth/package.json +++ b/packages/openauth/package.json @@ -32,7 +32,8 @@ }, "peerDependencies": { "arctic": "^2.2.2", - "hono": "^4.0.0" + "hono": "^4.0.0", + "redis": "^4.7.0" }, "dependencies": { "@standard-schema/spec": "1.0.0-beta.3", diff --git a/packages/openauth/src/storage/redis.ts b/packages/openauth/src/storage/redis.ts new file mode 100644 index 00000000..60692b29 --- /dev/null +++ b/packages/openauth/src/storage/redis.ts @@ -0,0 +1,73 @@ +/** + * Configure OpenAuth to use [Redis](https://redis.io/) as a storage adapter. + * + * ```ts + * import { RedisStorage } from '@openauthjs/openauth/storage/redis' + * + * const REDIS_URL = 'redis://myserver' + * + * export default issuer({ + * storage: await RedisStorage({ url: REDIS_URL }), + * // ... + * }) + * ``` + * + * Uses the recommended [node-redis](https://github.com/redis/node-redis) client + * + * Other [client configuration options](https://github.com/redis/node-redis/blob/master/docs/client-configuration.md#createclient-configuration) + * + * @packageDocumentation + */ +import { createClient, type RedisClientOptions } from 'redis' +import { joinKey, splitKey, StorageAdapter } from './storage.js' + +/** + * Creates a Redis KV store. + * @param options - The config for the adapter. + */ +export async function RedisStorage(options?: RedisClientOptions): Promise { + const client = await createClient(options) + .on('error', (err) => console.error('Redis Client Error', err)) + .connect() + + return { + async get(key: string[]) { + const value = await client.get(joinKey(key)) + if (!value) return + return JSON.parse(value) as Record + }, + + async set(key: string[], value: any, expiry?: Date) { + const _opts = expiry ? { EXAT: expiry.getTime() } : {} + await client.set(joinKey(key), JSON.stringify(value), _opts) + }, + + async remove(key: string[]) { + await client.del(joinKey(key)) + }, + + async *scan(prefix: string[]) { + let cursor = 0 + + while (true) { + let { cursor: next, keys } = await client.scan(cursor, { + MATCH: `${joinKey(prefix)}*`, + }) + + for (const key of keys) { + const value = await client.get(key) + if (value !== null) { + yield [splitKey(key), JSON.parse(value)] + } + } + + // Number(..) cant handle 64bit integer + if (BigInt(next) === BigInt(0)) { + break + } + + cursor = next + } + }, + } +}