···22NODE_ENV="development" # Options: 'development', 'production'
33PORT="8080" # The port your server will listen on
44HOST="localhost" # Hostname for the server
55+PUBLIC_URL="https://localhost:8080"
5667# CORS Settings
78CORS_ORIGIN="http://localhost:*" # Allowed CORS origin, adjust as necessary
···910# Rate Limiting
1011COMMON_RATE_LIMIT_WINDOW_MS="1000" # Window size for rate limiting (ms)
1112COMMON_RATE_LIMIT_MAX_REQUESTS="20" # Max number of requests per window per IP
1313+1414+# Secrets
1515+# openssl rand -base64 33
1616+COOKIE_SECRET=""
1717+# openssl ecparam -name prime256v1 -genkey | openssl pkcs8 -topk8 -nocrypt | openssl base64 -A
1818+PRIVATE_KEY_ES256_B64=""
···11+import { JoseKey } from '@atproto/jwk-jose'
22+import { NodeOAuthClient } from '@atproto/oauth-client-node'
33+import type { Database } from '#/db'
44+import { env } from '#/env'
55+import { SessionStore, StateStore } from './storage'
66+77+export const createClient = async (db: Database) => {
88+ const url = env.PUBLIC_URL
99+ const privateKeyPKCS8 = Buffer.from(env.PRIVATE_KEY_ES256_B64, 'base64').toString()
1010+ const privateKey = await JoseKey.fromImportable(privateKeyPKCS8, 'key1')
1111+ return new NodeOAuthClient({
1212+ // This object will be used to build the payload of the /client-metadata.json
1313+ // endpoint metadata, exposing the client metadata to the OAuth server.
1414+ clientMetadata: {
1515+ // Must be a URL that will be exposing this metadata
1616+ client_id: `${url}/client-metadata.json`,
1717+ client_uri: url,
1818+ client_name: 'ATProto Express App',
1919+ jwks_uri: `${url}/jwks.json`,
2020+ logo_uri: `${url}/logo.png`,
2121+ tos_uri: `${url}/tos`,
2222+ policy_uri: `${url}/policy`,
2323+ redirect_uris: [`${url}/oauth/callback`],
2424+ token_endpoint_auth_signing_alg: 'ES256',
2525+ scope: 'profile email offline_access',
2626+ grant_types: ['authorization_code', 'refresh_token'],
2727+ response_types: ['code'],
2828+ application_type: 'web',
2929+ token_endpoint_auth_method: 'private_key_jwt',
3030+ dpop_bound_access_tokens: true,
3131+ },
3232+3333+ // Used to authenticate the client to the token endpoint. Will be used to
3434+ // build the jwks object to be exposed on the "jwks_uri" endpoint.
3535+ keyset: [privateKey],
3636+3737+ // Interface to store authorization state data (during authorization flows)
3838+ stateStore: new StateStore(db),
3939+4040+ // Interface to store authenticated session data
4141+ sessionStore: new SessionStore(db),
4242+ })
4343+}
···11+import type { OAuthClient } from '@atproto/oauth-client-node'
12import type pino from 'pino'
23import type { Database } from '#/db'
34import type { Ingester } from '#/firehose/ingester'
···67 db: Database
78 ingester: Ingester
89 logger: pino.Logger
1010+ oauthClient: OAuthClient
911}