forked from
rocksky.app/rocksky
A decentralized music tracking and discovery platform built on AT Protocol 馃幍
1import { JoseKey } from "@atproto/jwk-jose";
2import { NodeOAuthClient, type RuntimeLock } from "@atproto/oauth-client-node";
3import Redis from "ioredis";
4import Redlock from "redlock";
5import type { Database } from "../db";
6import { env } from "../lib/env";
7import { SessionStore, StateStore } from "./storage";
8
9export const createClient = async (db: Database) => {
10 const publicUrl = env.PUBLIC_URL;
11 const url = publicUrl || `http://127.0.0.1:${env.PORT}`;
12 const enc = encodeURIComponent;
13
14 const redis = new Redis(env.REDIS_URL);
15 const redlock = new Redlock([redis]);
16
17 const requestLock: RuntimeLock = async (key, fn) => {
18 const lock = await redlock.acquire([key], 45e3); // 45 seconds
19 try {
20 return await fn();
21 } finally {
22 await lock.release();
23 }
24 };
25
26 return new NodeOAuthClient({
27 clientMetadata: {
28 client_name: "AT Protocol Express App",
29 client_id: publicUrl
30 ? `${url}/client-metadata.json`
31 : `http://localhost?redirect_uri=${enc(
32 `${url}/oauth/callback`
33 )}&scope=${enc("atproto transition:generic")}`,
34 client_uri: url,
35 redirect_uris: [`${url}/oauth/callback`],
36 scope: "atproto transition:generic",
37 grant_types: ["authorization_code", "refresh_token"],
38 response_types: ["code"],
39 application_type: "web",
40 token_endpoint_auth_method: url.startsWith("https")
41 ? "private_key_jwt"
42 : "none",
43 token_endpoint_auth_signing_alg: url.startsWith("https")
44 ? "ES256"
45 : undefined,
46 dpop_bound_access_tokens: true,
47 jwks_uri: url.startsWith("https") ? `${url}/jwks.json` : undefined,
48 },
49 keyset: url.startsWith("https")
50 ? await Promise.all([
51 JoseKey.fromImportable(env.PRIVATE_KEY_1),
52 JoseKey.fromImportable(env.PRIVATE_KEY_2),
53 JoseKey.fromImportable(env.PRIVATE_KEY_3),
54 ])
55 : undefined,
56 stateStore: new StateStore(db),
57 sessionStore: new SessionStore(db),
58 requestLock,
59 });
60};