···22 NodeOAuthClient,
33 NodeSavedSession,
44 NodeSavedState,
55+ RuntimeLock,
56 Session,
67} from "@atproto/oauth-client-node";
78import { JoseKey } from "@atproto/jwk-jose";
89import { oauth_metadata } from "app/api/oauth/[route]/oauth-metadata";
910import { supabaseServerClient } from "supabase/serverClient";
10111212+import { Redis } from "@upstash/redis";
1313+import Redlock from "redlock";
1114export async function createOauthClient() {
1215 let keyset =
1316 process.env.NODE_ENV === "production"
···1518 JoseKey.fromImportable(process.env.JOSE_PRIVATE_KEY_1!),
1619 ])
1720 : undefined;
2121+ let requestLock: RuntimeLock | undefined;
2222+ if (process.env.NODE_ENV === "production") {
2323+ const redis = Redis.fromEnv();
2424+ const redlock = new Redlock([redis]);
2525+ requestLock = async (key, fn) => {
2626+ // 30 seconds should be enough. Since we will be using one lock per user id
2727+ // we can be quite liberal with the lock duration here.
2828+ const lock = await redlock.acquire([key], 45e3);
2929+ try {
3030+ return await fn();
3131+ } finally {
3232+ await lock.release();
3333+ }
3434+ };
3535+ }
1836 return new NodeOAuthClient({
1937 // This object will be used to build the payload of the /client-metadata.json
2038 // endpoint metadata, exposing the client metadata to the OAuth server.
···2846 stateStore,
2947 // Interface to store authenticated session data
3048 sessionStore,
4949+ requestLock,
3150 });
3251}
3352