Scratch space for learning atproto app development
1import assert from 'node:assert' 2import { Keyset } from '@atproto/jwk' 3import { JoseKey } from '@atproto/jwk-jose' 4import { 5 AppViewHandleResolver, 6 atprotoLoopbackClientMetadata, 7 DidResolverCommon, 8 NodeOAuthClient, 9 OAuthClientMetadataInput, 10} from '@atproto/oauth-client-node' 11 12import type { Database } from '#/db' 13import { env } from '#/env' 14import { SessionStore, StateStore } from './storage' 15 16export async function createOAuthClient(db: Database) { 17 assert( 18 !env.PUBLIC_URL || env.PRIVATE_JWKS, 19 'ATProto requires backend clients to be confidential', 20 ) 21 22 // Confidential client require a keyset accessible on the internet. Non 23 // internet clients (e.g. development) cannot expose a keyset on the internet 24 // so they can't be private.. 25 const keyset = 26 env.PUBLIC_URL && env.PRIVATE_JWKS 27 ? new Keyset( 28 await Promise.all( 29 env.PRIVATE_JWKS.map((jwk) => JoseKey.fromJWK(jwk)), 30 ), 31 ) 32 : undefined 33 34 // If a keyset is defined (meaning the client is confidential). Let's make 35 // sure it has a private key for signing. Note: findPrivateKey will throw if 36 // the keyset does no contain a suitable private key. 37 const pk = keyset?.findPrivateKey({ use: 'sig' }) 38 39 const clientMetadata: OAuthClientMetadataInput = env.PUBLIC_URL 40 ? { 41 client_name: 'Statusphere Example App', 42 client_id: `${env.PUBLIC_URL}/oauth-client-metadata.json`, 43 jwks_uri: `${env.PUBLIC_URL}/.well-known/jwks.json`, 44 redirect_uris: [`${env.PUBLIC_URL}/oauth/callback`], 45 scope: 'atproto transition:generic', 46 grant_types: ['authorization_code', 'refresh_token'], 47 response_types: ['code'], 48 application_type: 'web', 49 token_endpoint_auth_method: pk ? 'private_key_jwt' : 'none', 50 token_endpoint_auth_signing_alg: pk ? pk[1] : undefined, 51 dpop_bound_access_tokens: true, 52 } 53 : atprotoLoopbackClientMetadata( 54 `http://localhost?${new URLSearchParams([ 55 ['redirect_uri', `http://127.0.0.1:${env.PORT}/oauth/callback`], 56 ['scope', `atproto transition:generic`], 57 ])}`, 58 ) 59 60 return new NodeOAuthClient({ 61 keyset, 62 clientMetadata, 63 stateStore: new StateStore(db), 64 sessionStore: new SessionStore(db), 65 }) 66}