ATProto Social Bookmark
at main 103 lines 3.5 kB view raw
1import { NodeOAuthClient, NodeSavedState, NodeSavedSession } from '@atproto/oauth-client-node' 2import { JoseKey } from '@atproto/jwk-jose' 3import { prisma } from "../db.js"; 4 5export const SCOPE = [ 6 "atproto", 7 "include:blue.rito.permissionSet", 8 "repo:app.bsky.feed.post", 9 "rpc:app.bsky.actor.getProfile?aud=did:web:api.bsky.app%23bsky_appview", 10 "blob:*/*", 11]; 12 13const stateStore = { 14 async set(key: string, internalState: NodeSavedState): Promise<void> { 15 await prisma.nodeOAuthState.upsert({ 16 where: { key }, 17 update: { state: JSON.stringify(internalState) }, 18 create: { key, state: JSON.stringify(internalState) }, 19 }) 20 }, 21 22 async get(key: string): Promise<NodeSavedState | undefined> { 23 const record = await prisma.nodeOAuthState.findUnique({ where: { key } }) 24 if (!record) return undefined 25 26 try { 27 return JSON.parse(record.state) as NodeSavedState 28 } catch (err) { 29 console.error('Invalid NodeOAuthState JSON:', err) 30 return undefined 31 } 32 }, 33 34 async del(key: string): Promise<void> { 35 await prisma.nodeOAuthState.delete({ where: { key } }).catch(() => { }) 36 }, 37} 38 39const sessionStore = { 40 async set(sub: string, session: NodeSavedSession): Promise<void> { 41 await prisma.nodeOAuthSession.upsert({ 42 where: { key: sub }, 43 update: { session: JSON.stringify(session) }, 44 create: { key: sub, session: JSON.stringify(session) }, 45 }) 46 }, 47 48 async get(sub: string): Promise<NodeSavedSession | undefined> { 49 const record = await prisma.nodeOAuthSession.findUnique({ where: { key: sub } }) 50 if (!record) return undefined 51 52 try { 53 return JSON.parse(record.session) as NodeSavedSession 54 } catch (err) { 55 console.error('Invalid NodeOAuthSession JSON:', err) 56 return undefined 57 } 58 }, 59 60 async del(sub: string): Promise<void> { 61 await prisma.nodeOAuthSession.delete({ where: { key: sub } }).catch(() => { }) 62 }, 63} 64 65// JoseKey を生成 66const key1 = await JoseKey.fromImportable(process.env.OAUTH_PRIVATE_JWK || '', 'key1') 67 68export const client = new NodeOAuthClient({ 69 // This object will be used to build the payload of the /client-metadata.json 70 // endpoint metadata, exposing the client metadata to the OAuth server. 71 clientMetadata: { 72 // Must be a URL that will be exposing this metadata 73 client_id: `${process.env.NEXT_PUBLIC_URL}/api/client-metadata.json`, 74 client_name: 'Rito', 75 client_uri: `${process.env.NEXT_PUBLIC_URL}`, 76 logo_uri: `${process.env.NEXT_PUBLIC_URL}/favicon.ico`, 77 tos_uri: `${process.env.NEXT_PUBLIC_URL}/tos`, 78 policy_uri: `${process.env.NEXT_PUBLIC_URL}/privacy`, 79 redirect_uris: [`${process.env.NEXT_PUBLIC_URL}/api/oauth/callback`], 80 grant_types: ['authorization_code', 'refresh_token'], 81 scope: SCOPE.join(" "), 82 response_types: ['code'], 83 application_type: 'web', 84 token_endpoint_auth_method: 'private_key_jwt', 85 token_endpoint_auth_signing_alg: 'RS256', 86 dpop_bound_access_tokens: true, 87 jwks_uri: `${process.env.NEXT_PUBLIC_URL}/api/jwks.json`, 88 }, 89 90 // Used to authenticate the client to the token endpoint. Will be used to 91 // build the jwks object to be exposed on the "jwks_uri" endpoint. 92 keyset: [key1], 93 94 // Interface to store authorization state data (during authorization flows) 95 stateStore, 96 97 // Interface to store authenticated session data 98 sessionStore, 99 100 // A lock to prevent concurrent access to the session store. Optional if only one instance is running. 101 102 requestLock: async (_key, fn) => await fn(), 103})