diff --git a/.github/workflows/build-and-push-bsky-ghcr.yaml b/.github/workflows/build-and-push-bsky-ghcr.yaml index f1bf0bd10f5..cfa76dc5a46 100644 --- a/.github/workflows/build-and-push-bsky-ghcr.yaml +++ b/.github/workflows/build-and-push-bsky-ghcr.yaml @@ -3,7 +3,7 @@ on: push: branches: - main - - appview-v2 + - pds-proxy-headers env: REGISTRY: ghcr.io USERNAME: ${{ github.actor }} diff --git a/.github/workflows/build-and-push-ozone-aws.yaml b/.github/workflows/build-and-push-ozone-aws.yaml index b934d192b6f..34c461a0b42 100644 --- a/.github/workflows/build-and-push-ozone-aws.yaml +++ b/.github/workflows/build-and-push-ozone-aws.yaml @@ -3,7 +3,7 @@ on: push: branches: - main - - ozone-cdn-invalidation + - pds-proxy-headers env: REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }} USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }} diff --git a/.github/workflows/build-and-push-pds-ghcr.yaml b/.github/workflows/build-and-push-pds-ghcr.yaml index b11230ab531..988e6b02f84 100644 --- a/.github/workflows/build-and-push-pds-ghcr.yaml +++ b/.github/workflows/build-and-push-pds-ghcr.yaml @@ -3,6 +3,7 @@ on: push: branches: - main + - pds-proxy-headers env: REGISTRY: ghcr.io USERNAME: ${{ github.actor }} diff --git a/packages/common-web/src/did-doc.ts b/packages/common-web/src/did-doc.ts index 715c364b3ca..b6821e74f5c 100644 --- a/packages/common-web/src/did-doc.ts +++ b/packages/common-web/src/did-doc.ts @@ -81,7 +81,7 @@ export const getNotifEndpoint = (doc: DidDocument): string | undefined => { export const getServiceEndpoint = ( doc: DidDocument, - opts: { id: string; type: string }, + opts: { id: string; type?: string }, ) => { const did = getDid(doc) let services = doc.service @@ -94,7 +94,7 @@ export const getServiceEndpoint = ( (service) => service.id === opts.id || service.id === `${did}${opts.id}`, ) if (!found) return undefined - if (found.type !== opts.type) { + if (opts.type && found.type !== opts.type) { return undefined } if (typeof found.serviceEndpoint !== 'string') { diff --git a/packages/dev-env/src/pds.ts b/packages/dev-env/src/pds.ts index 430f42e5a2a..cd73c286960 100644 --- a/packages/dev-env/src/pds.ts +++ b/packages/dev-env/src/pds.ts @@ -35,6 +35,7 @@ export class TestPds { await fs.mkdir(dataDirectory, { recursive: true }) const env: pds.ServerEnvironment = { + devMode: true, port, dataDirectory: dataDirectory, blobstoreDiskLocation: blobstoreLoc, diff --git a/packages/pds/src/api/app/bsky/actor/getProfile.ts b/packages/pds/src/api/app/bsky/actor/getProfile.ts index e2cbe47dcab..f05465d267a 100644 --- a/packages/pds/src/api/app/bsky/actor/getProfile.ts +++ b/packages/pds/src/api/app/bsky/actor/getProfile.ts @@ -1,6 +1,5 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' import { OutputSchema } from '../../../../lexicon/types/app/bsky/actor/getProfile' import { LocalViewer, @@ -15,18 +14,10 @@ export default function (server: Server, ctx: AppContext) { const { bskyAppView } = ctx.cfg if (!bskyAppView) return server.app.bsky.actor.getProfile({ - auth: ctx.authVerifier.accessOrRole, - handler: async ({ req, auth, params }) => { - const requester = - auth.credentials.type === 'access' ? auth.credentials.did : null - const res = await pipethrough( - bskyAppView.url, - METHOD_NSID, - params, - requester - ? await ctx.appviewAuthHeaders(requester, req) - : authPassthru(req), - ) + auth: ctx.authVerifier.access, + handler: async ({ req, auth }) => { + const requester = auth.credentials.did + const res = await pipethrough(ctx, req, requester) if (!requester) { return res } diff --git a/packages/pds/src/api/app/bsky/actor/getProfiles.ts b/packages/pds/src/api/app/bsky/actor/getProfiles.ts index d3802e4833a..3d1b2f1f579 100644 --- a/packages/pds/src/api/app/bsky/actor/getProfiles.ts +++ b/packages/pds/src/api/app/bsky/actor/getProfiles.ts @@ -15,15 +15,10 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.actor.getProfiles({ auth: ctx.authVerifier.access, - handler: async ({ auth, params, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - const res = await pipethrough( - bskyAppView.url, - METHOD_NSID, - params, - await ctx.appviewAuthHeaders(requester, req), - ) + const res = await pipethrough(ctx, req, requester) return handleReadAfterWrite( ctx, METHOD_NSID, diff --git a/packages/pds/src/api/app/bsky/actor/getSuggestions.ts b/packages/pds/src/api/app/bsky/actor/getSuggestions.ts index 3a91306d4c5..fadcac2e9fc 100644 --- a/packages/pds/src/api/app/bsky/actor/getSuggestions.ts +++ b/packages/pds/src/api/app/bsky/actor/getSuggestions.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.actor.getSuggestions({ auth: ctx.authVerifier.access, - handler: async ({ params, auth, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.actor.getSuggestions', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/actor/searchActors.ts b/packages/pds/src/api/app/bsky/actor/searchActors.ts index bf8eff4fd42..777f4e7a2d0 100644 --- a/packages/pds/src/api/app/bsky/actor/searchActors.ts +++ b/packages/pds/src/api/app/bsky/actor/searchActors.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.actor.searchActors({ auth: ctx.authVerifier.access, - handler: async ({ params, auth, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.actor.searchActors', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/actor/searchActorsTypeahead.ts b/packages/pds/src/api/app/bsky/actor/searchActorsTypeahead.ts index c1f1acd8e2a..58d5df0d049 100644 --- a/packages/pds/src/api/app/bsky/actor/searchActorsTypeahead.ts +++ b/packages/pds/src/api/app/bsky/actor/searchActorsTypeahead.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.actor.searchActorsTypeahead({ auth: ctx.authVerifier.access, - handler: async ({ params, auth, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.actor.searchActorsTypeahead', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/feed/getActorFeeds.ts b/packages/pds/src/api/app/bsky/feed/getActorFeeds.ts index 63f15110b70..16082a916e8 100644 --- a/packages/pds/src/api/app/bsky/feed/getActorFeeds.ts +++ b/packages/pds/src/api/app/bsky/feed/getActorFeeds.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.feed.getActorFeeds({ auth: ctx.authVerifier.access, - handler: async ({ auth, params, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.feed.getActorFeeds', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/feed/getActorLikes.ts b/packages/pds/src/api/app/bsky/feed/getActorLikes.ts index 7eca9aa9862..3d17bff8e05 100644 --- a/packages/pds/src/api/app/bsky/feed/getActorLikes.ts +++ b/packages/pds/src/api/app/bsky/feed/getActorLikes.ts @@ -1,6 +1,5 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' import { OutputSchema } from '../../../../lexicon/types/app/bsky/feed/getAuthorFeed' import { LocalViewer, @@ -15,18 +14,10 @@ export default function (server: Server, ctx: AppContext) { const { bskyAppView } = ctx.cfg if (!bskyAppView) return server.app.bsky.feed.getActorLikes({ - auth: ctx.authVerifier.accessOrRole, - handler: async ({ req, params, auth }) => { - const requester = - auth.credentials.type === 'access' ? auth.credentials.did : null - const res = await pipethrough( - bskyAppView.url, - METHOD_NSID, - params, - requester - ? await ctx.appviewAuthHeaders(requester, req) - : authPassthru(req), - ) + auth: ctx.authVerifier.access, + handler: async ({ req, auth }) => { + const requester = auth.credentials.did + const res = await pipethrough(ctx, req, requester) if (!requester) { return res diff --git a/packages/pds/src/api/app/bsky/feed/getAuthorFeed.ts b/packages/pds/src/api/app/bsky/feed/getAuthorFeed.ts index 7a9f2ea3e74..88f646842a5 100644 --- a/packages/pds/src/api/app/bsky/feed/getAuthorFeed.ts +++ b/packages/pds/src/api/app/bsky/feed/getAuthorFeed.ts @@ -1,6 +1,5 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' import { OutputSchema } from '../../../../lexicon/types/app/bsky/feed/getAuthorFeed' import { isReasonRepost } from '../../../../lexicon/types/app/bsky/feed/defs' import { @@ -16,18 +15,10 @@ export default function (server: Server, ctx: AppContext) { const { bskyAppView } = ctx.cfg if (!bskyAppView) return server.app.bsky.feed.getAuthorFeed({ - auth: ctx.authVerifier.accessOrRole, - handler: async ({ req, params, auth }) => { - const requester = - auth.credentials.type === 'access' ? auth.credentials.did : null - const res = await pipethrough( - bskyAppView.url, - METHOD_NSID, - params, - requester - ? await ctx.appviewAuthHeaders(requester, req) - : authPassthru(req), - ) + auth: ctx.authVerifier.access, + handler: async ({ req, auth }) => { + const requester = auth.credentials.did + const res = await pipethrough(ctx, req, requester) if (!requester) { return res } diff --git a/packages/pds/src/api/app/bsky/feed/getFeed.ts b/packages/pds/src/api/app/bsky/feed/getFeed.ts index c141d2bbb5d..601dbba8eed 100644 --- a/packages/pds/src/api/app/bsky/feed/getFeed.ts +++ b/packages/pds/src/api/app/bsky/feed/getFeed.ts @@ -14,22 +14,9 @@ export default function (server: Server, ctx: AppContext) { const { data: feed } = await appViewAgent.api.app.bsky.feed.getFeedGenerator( { feed: params.feed }, - await ctx.appviewAuthHeaders(requester, req), + await ctx.appviewAuthHeaders(requester), ) - const serviceAuthHeaders = await ctx.serviceAuthHeaders( - requester, - feed.view.did, - req, - ) - // forward accept-language header to upstream services - serviceAuthHeaders.headers['accept-language'] = - req.headers['accept-language'] - return pipethrough( - bskyAppView.url, - 'app.bsky.feed.getFeed', - params, - serviceAuthHeaders, - ) + return pipethrough(ctx, req, requester, feed.view.did) }, }) } diff --git a/packages/pds/src/api/app/bsky/feed/getFeedGenerator.ts b/packages/pds/src/api/app/bsky/feed/getFeedGenerator.ts index ac235065e6c..278ee9af7c2 100644 --- a/packages/pds/src/api/app/bsky/feed/getFeedGenerator.ts +++ b/packages/pds/src/api/app/bsky/feed/getFeedGenerator.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.feed.getFeedGenerator({ auth: ctx.authVerifier.access, - handler: async ({ params, auth, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.feed.getFeedGenerator', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/feed/getFeedGenerators.ts b/packages/pds/src/api/app/bsky/feed/getFeedGenerators.ts index 8a413b663b7..2aa79b03853 100644 --- a/packages/pds/src/api/app/bsky/feed/getFeedGenerators.ts +++ b/packages/pds/src/api/app/bsky/feed/getFeedGenerators.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.feed.getFeedGenerators({ auth: ctx.authVerifier.access, - handler: async ({ params, auth, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.feed.getFeedGenerators', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/feed/getLikes.ts b/packages/pds/src/api/app/bsky/feed/getLikes.ts index 5470c3c7cda..226b10feb9d 100644 --- a/packages/pds/src/api/app/bsky/feed/getLikes.ts +++ b/packages/pds/src/api/app/bsky/feed/getLikes.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.feed.getLikes({ auth: ctx.authVerifier.access, - handler: async ({ params, auth, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.feed.getLikes', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/feed/getListFeed.ts b/packages/pds/src/api/app/bsky/feed/getListFeed.ts index 328dd1902eb..2d059eb442f 100644 --- a/packages/pds/src/api/app/bsky/feed/getListFeed.ts +++ b/packages/pds/src/api/app/bsky/feed/getListFeed.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.feed.getListFeed({ auth: ctx.authVerifier.access, - handler: async ({ auth, params, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.feed.getListFeed', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/feed/getPostThread.ts b/packages/pds/src/api/app/bsky/feed/getPostThread.ts index 720a6b2796b..c6231d4bf04 100644 --- a/packages/pds/src/api/app/bsky/feed/getPostThread.ts +++ b/packages/pds/src/api/app/bsky/feed/getPostThread.ts @@ -3,7 +3,6 @@ import { AtUri } from '@atproto/syntax' import { Headers, XRPCError } from '@atproto/xrpc' import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' import { ThreadViewPost, isThreadViewPost, @@ -30,27 +29,12 @@ export default function (server: Server, ctx: AppContext) { const { bskyAppView } = ctx.cfg if (!bskyAppView) return server.app.bsky.feed.getPostThread({ - auth: ctx.authVerifier.accessOrRole, - handler: async ({ req, params, auth }) => { - const requester = - auth.credentials.type === 'access' ? auth.credentials.did : null - - if (!requester) { - return pipethrough( - bskyAppView.url, - METHOD_NSID, - params, - authPassthru(req), - ) - } + auth: ctx.authVerifier.access, + handler: async ({ req, auth, params }) => { + const requester = auth.credentials.did try { - const res = await pipethrough( - bskyAppView.url, - METHOD_NSID, - params, - await ctx.appviewAuthHeaders(requester, req), - ) + const res = await pipethrough(ctx, req, requester) return await handleReadAfterWrite( ctx, @@ -206,7 +190,7 @@ const readAfterWriteNotFound = async ( assert(ctx.appViewAgent) const parentsRes = await ctx.appViewAgent.api.app.bsky.feed.getPostThread( { uri: highestParent, parentHeight: params.parentHeight, depth: 0 }, - await ctx.appviewAuthHeaders(requester, null), + await ctx.appviewAuthHeaders(requester), ) thread.parent = parentsRes.data.thread } catch (err) { diff --git a/packages/pds/src/api/app/bsky/feed/getPosts.ts b/packages/pds/src/api/app/bsky/feed/getPosts.ts index ec8ff1b35e3..a07be55a516 100644 --- a/packages/pds/src/api/app/bsky/feed/getPosts.ts +++ b/packages/pds/src/api/app/bsky/feed/getPosts.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.feed.getPosts({ auth: ctx.authVerifier.access, - handler: async ({ params, auth, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.feed.getPosts', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/feed/getRepostedBy.ts b/packages/pds/src/api/app/bsky/feed/getRepostedBy.ts index e3cbe112e24..3b8cff2f4cf 100644 --- a/packages/pds/src/api/app/bsky/feed/getRepostedBy.ts +++ b/packages/pds/src/api/app/bsky/feed/getRepostedBy.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.feed.getRepostedBy({ auth: ctx.authVerifier.access, - handler: async ({ params, auth, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.feed.getRepostedBy', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/feed/getSuggestedFeeds.ts b/packages/pds/src/api/app/bsky/feed/getSuggestedFeeds.ts index 6e83689d5ef..002b24ef3f0 100644 --- a/packages/pds/src/api/app/bsky/feed/getSuggestedFeeds.ts +++ b/packages/pds/src/api/app/bsky/feed/getSuggestedFeeds.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.feed.getSuggestedFeeds({ auth: ctx.authVerifier.access, - handler: async ({ auth, params, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.feed.getSuggestedFeeds', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/feed/getTimeline.ts b/packages/pds/src/api/app/bsky/feed/getTimeline.ts index 810f6931daa..05f0a0ea90f 100644 --- a/packages/pds/src/api/app/bsky/feed/getTimeline.ts +++ b/packages/pds/src/api/app/bsky/feed/getTimeline.ts @@ -15,14 +15,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.feed.getTimeline({ auth: ctx.authVerifier.access, - handler: async ({ params, auth, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - const res = await pipethrough( - bskyAppView.url, - METHOD_NSID, - params, - await ctx.appviewAuthHeaders(requester, req), - ) + const res = await pipethrough(ctx, req, requester) return await handleReadAfterWrite( ctx, METHOD_NSID, diff --git a/packages/pds/src/api/app/bsky/feed/searchPosts.ts b/packages/pds/src/api/app/bsky/feed/searchPosts.ts index 263729e6de6..45ad78ad401 100644 --- a/packages/pds/src/api/app/bsky/feed/searchPosts.ts +++ b/packages/pds/src/api/app/bsky/feed/searchPosts.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.feed.searchPosts({ auth: ctx.authVerifier.access, - handler: async ({ params, auth, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.feed.searchPosts', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/graph/getBlocks.ts b/packages/pds/src/api/app/bsky/graph/getBlocks.ts index 63b8294ac8b..12cc5603eb7 100644 --- a/packages/pds/src/api/app/bsky/graph/getBlocks.ts +++ b/packages/pds/src/api/app/bsky/graph/getBlocks.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.graph.getBlocks({ auth: ctx.authVerifier.access, - handler: async ({ params, auth, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.graph.getBlocks', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/graph/getFollowers.ts b/packages/pds/src/api/app/bsky/graph/getFollowers.ts index 28e74f41683..78f433598aa 100644 --- a/packages/pds/src/api/app/bsky/graph/getFollowers.ts +++ b/packages/pds/src/api/app/bsky/graph/getFollowers.ts @@ -1,24 +1,15 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' import { pipethrough } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { const { bskyAppView } = ctx.cfg if (!bskyAppView) return server.app.bsky.graph.getFollowers({ - auth: ctx.authVerifier.accessOrRole, - handler: async ({ req, params, auth }) => { - const requester = - auth.credentials.type === 'access' ? auth.credentials.did : null - return pipethrough( - bskyAppView.url, - 'app.bsky.graph.getFollowers', - params, - requester - ? await ctx.appviewAuthHeaders(requester, req) - : authPassthru(req), - ) + auth: ctx.authVerifier.access, + handler: async ({ req, auth }) => { + const requester = auth.credentials.did + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/graph/getFollows.ts b/packages/pds/src/api/app/bsky/graph/getFollows.ts index d50b4bbe6e1..3c62584ee98 100644 --- a/packages/pds/src/api/app/bsky/graph/getFollows.ts +++ b/packages/pds/src/api/app/bsky/graph/getFollows.ts @@ -1,24 +1,15 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' import { pipethrough } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { const { bskyAppView } = ctx.cfg if (!bskyAppView) return server.app.bsky.graph.getFollows({ - auth: ctx.authVerifier.accessOrRole, - handler: async ({ req, params, auth }) => { - const requester = - auth.credentials.type === 'access' ? auth.credentials.did : null - return pipethrough( - bskyAppView.url, - 'app.bsky.graph.getFollows', - params, - requester - ? await ctx.appviewAuthHeaders(requester, req) - : authPassthru(req), - ) + auth: ctx.authVerifier.access, + handler: async ({ req, auth }) => { + const requester = auth.credentials.did + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/graph/getList.ts b/packages/pds/src/api/app/bsky/graph/getList.ts index 3da09accb89..0b39c8eeae5 100644 --- a/packages/pds/src/api/app/bsky/graph/getList.ts +++ b/packages/pds/src/api/app/bsky/graph/getList.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.graph.getList({ auth: ctx.authVerifier.access, - handler: async ({ params, auth, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.graph.getList', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/graph/getListBlocks.ts b/packages/pds/src/api/app/bsky/graph/getListBlocks.ts index 8ee29641b51..472a7c6f24c 100644 --- a/packages/pds/src/api/app/bsky/graph/getListBlocks.ts +++ b/packages/pds/src/api/app/bsky/graph/getListBlocks.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.graph.getListBlocks({ auth: ctx.authVerifier.access, - handler: async ({ auth, params, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.graph.getListBlocks', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/graph/getListMutes.ts b/packages/pds/src/api/app/bsky/graph/getListMutes.ts index 1c7519065b8..f77f35155a5 100644 --- a/packages/pds/src/api/app/bsky/graph/getListMutes.ts +++ b/packages/pds/src/api/app/bsky/graph/getListMutes.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.graph.getListMutes({ auth: ctx.authVerifier.access, - handler: async ({ params, auth, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.graph.getListMutes', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/graph/getLists.ts b/packages/pds/src/api/app/bsky/graph/getLists.ts index 297deb6ea97..8aabf93ba78 100644 --- a/packages/pds/src/api/app/bsky/graph/getLists.ts +++ b/packages/pds/src/api/app/bsky/graph/getLists.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.graph.getLists({ auth: ctx.authVerifier.access, - handler: async ({ params, auth, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.graph.getLists', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/graph/getMutes.ts b/packages/pds/src/api/app/bsky/graph/getMutes.ts index f741c6d8cd1..0d3c131d28e 100644 --- a/packages/pds/src/api/app/bsky/graph/getMutes.ts +++ b/packages/pds/src/api/app/bsky/graph/getMutes.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.graph.getMutes({ auth: ctx.authVerifier.access, - handler: async ({ auth, params, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.graph.getMutes', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/graph/getSuggestedFollowsByActor.ts b/packages/pds/src/api/app/bsky/graph/getSuggestedFollowsByActor.ts index 4d3866bf121..1c3a7d4ed86 100644 --- a/packages/pds/src/api/app/bsky/graph/getSuggestedFollowsByActor.ts +++ b/packages/pds/src/api/app/bsky/graph/getSuggestedFollowsByActor.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.graph.getSuggestedFollowsByActor({ auth: ctx.authVerifier.access, - handler: async ({ auth, params, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.graph.getSuggestedFollowsByActor', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/graph/muteActor.ts b/packages/pds/src/api/app/bsky/graph/muteActor.ts index 8492c1715a5..9613e8736d0 100644 --- a/packages/pds/src/api/app/bsky/graph/muteActor.ts +++ b/packages/pds/src/api/app/bsky/graph/muteActor.ts @@ -1,18 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' +import { pipethroughProcedure } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { appViewAgent } = ctx - if (!appViewAgent) return server.app.bsky.graph.muteActor({ auth: ctx.authVerifier.access, handler: async ({ auth, input, req }) => { const requester = auth.credentials.did - - await appViewAgent.api.app.bsky.graph.muteActor(input.body, { - ...(await ctx.appviewAuthHeaders(requester, req)), - encoding: 'application/json', - }) + await pipethroughProcedure(ctx, req, input.body, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/graph/muteActorList.ts b/packages/pds/src/api/app/bsky/graph/muteActorList.ts index 09b5cf913e2..41e74ccd713 100644 --- a/packages/pds/src/api/app/bsky/graph/muteActorList.ts +++ b/packages/pds/src/api/app/bsky/graph/muteActorList.ts @@ -1,18 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' +import { pipethroughProcedure } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { appViewAgent } = ctx - if (!appViewAgent) return server.app.bsky.graph.muteActorList({ auth: ctx.authVerifier.access, handler: async ({ auth, input, req }) => { const requester = auth.credentials.did - - await appViewAgent.api.app.bsky.graph.muteActorList(input.body, { - ...(await ctx.appviewAuthHeaders(requester, req)), - encoding: 'application/json', - }) + await pipethroughProcedure(ctx, req, input.body, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/graph/unmuteActor.ts b/packages/pds/src/api/app/bsky/graph/unmuteActor.ts index baccec3e4da..8d9898f442e 100644 --- a/packages/pds/src/api/app/bsky/graph/unmuteActor.ts +++ b/packages/pds/src/api/app/bsky/graph/unmuteActor.ts @@ -1,18 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' +import { pipethroughProcedure } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { appViewAgent } = ctx - if (!appViewAgent) return server.app.bsky.graph.unmuteActor({ auth: ctx.authVerifier.access, handler: async ({ auth, input, req }) => { const requester = auth.credentials.did - - await appViewAgent.api.app.bsky.graph.unmuteActor(input.body, { - ...(await ctx.appviewAuthHeaders(requester, req)), - encoding: 'application/json', - }) + await pipethroughProcedure(ctx, req, input.body, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/graph/unmuteActorList.ts b/packages/pds/src/api/app/bsky/graph/unmuteActorList.ts index eaaf081c81c..dc6e254fbb4 100644 --- a/packages/pds/src/api/app/bsky/graph/unmuteActorList.ts +++ b/packages/pds/src/api/app/bsky/graph/unmuteActorList.ts @@ -1,18 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' +import { pipethroughProcedure } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { appViewAgent } = ctx - if (!appViewAgent) return server.app.bsky.graph.unmuteActorList({ auth: ctx.authVerifier.access, handler: async ({ auth, input, req }) => { const requester = auth.credentials.did - - await appViewAgent.api.app.bsky.graph.unmuteActorList(input.body, { - ...(await ctx.appviewAuthHeaders(requester, req)), - encoding: 'application/json', - }) + await pipethroughProcedure(ctx, req, input.body, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/labeler/getServices.ts b/packages/pds/src/api/app/bsky/labeler/getServices.ts index d1cf93e7f87..a884c476a9f 100644 --- a/packages/pds/src/api/app/bsky/labeler/getServices.ts +++ b/packages/pds/src/api/app/bsky/labeler/getServices.ts @@ -1,21 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' +import { pipethrough } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { appViewAgent } = ctx - if (!appViewAgent) return server.app.bsky.labeler.getServices({ auth: ctx.authVerifier.access, - handler: async ({ params, auth, req }) => { + handler: async ({ auth, req }) => { const requester = auth.credentials.did - const res = await appViewAgent.api.app.bsky.labeler.getServices( - params, - await ctx.appviewAuthHeaders(requester, req), - ) - return { - encoding: 'application/json', - body: res.data, - } + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/notification/getUnreadCount.ts b/packages/pds/src/api/app/bsky/notification/getUnreadCount.ts index 8483f8a9dc4..d5f09f92bb3 100644 --- a/packages/pds/src/api/app/bsky/notification/getUnreadCount.ts +++ b/packages/pds/src/api/app/bsky/notification/getUnreadCount.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.notification.getUnreadCount({ auth: ctx.authVerifier.access, - handler: async ({ auth, params, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.notification.getUnreadCount', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/notification/listNotifications.ts b/packages/pds/src/api/app/bsky/notification/listNotifications.ts index 80bb770eeec..68d08fde137 100644 --- a/packages/pds/src/api/app/bsky/notification/listNotifications.ts +++ b/packages/pds/src/api/app/bsky/notification/listNotifications.ts @@ -7,14 +7,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.notification.listNotifications({ auth: ctx.authVerifier.access, - handler: async ({ params, auth, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.notification.listNotifications', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/notification/registerPush.ts b/packages/pds/src/api/app/bsky/notification/registerPush.ts index b06dcee3f55..ec6084c41aa 100644 --- a/packages/pds/src/api/app/bsky/notification/registerPush.ts +++ b/packages/pds/src/api/app/bsky/notification/registerPush.ts @@ -10,13 +10,13 @@ export default function (server: Server, ctx: AppContext) { if (!appViewAgent) return server.app.bsky.notification.registerPush({ auth: ctx.authVerifier.accessDeactived, - handler: async ({ auth, input, req }) => { + handler: async ({ auth, input }) => { const { serviceDid } = input.body const { credentials: { did }, } = auth - const authHeaders = await ctx.serviceAuthHeaders(did, serviceDid, req) + const authHeaders = await ctx.serviceAuthHeaders(did, serviceDid) if (ctx.cfg.bskyAppView?.did === serviceDid) { await appViewAgent.api.app.bsky.notification.registerPush(input.body, { diff --git a/packages/pds/src/api/app/bsky/notification/updateSeen.ts b/packages/pds/src/api/app/bsky/notification/updateSeen.ts index 5de6d9481a4..e7a97f3eddd 100644 --- a/packages/pds/src/api/app/bsky/notification/updateSeen.ts +++ b/packages/pds/src/api/app/bsky/notification/updateSeen.ts @@ -1,18 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' +import { pipethroughProcedure } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { appViewAgent } = ctx - if (!appViewAgent) return server.app.bsky.notification.updateSeen({ auth: ctx.authVerifier.access, handler: async ({ input, auth, req }) => { const requester = auth.credentials.did - - await appViewAgent.api.app.bsky.notification.updateSeen(input.body, { - ...(await ctx.appviewAuthHeaders(requester, req)), - encoding: 'application/json', - }) + await pipethroughProcedure(ctx, req, input.body, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/unspecced/getPopularFeedGenerators.ts b/packages/pds/src/api/app/bsky/unspecced/getPopularFeedGenerators.ts index ff810021dd6..0d53ffff5ad 100644 --- a/packages/pds/src/api/app/bsky/unspecced/getPopularFeedGenerators.ts +++ b/packages/pds/src/api/app/bsky/unspecced/getPopularFeedGenerators.ts @@ -8,14 +8,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.unspecced.getPopularFeedGenerators({ auth: ctx.authVerifier.access, - handler: async ({ auth, params, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.unspecced.getPopularFeedGenerators', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/app/bsky/unspecced/getTaggedSuggestions.ts b/packages/pds/src/api/app/bsky/unspecced/getTaggedSuggestions.ts index 591afbee592..c9d9ed90dfe 100644 --- a/packages/pds/src/api/app/bsky/unspecced/getTaggedSuggestions.ts +++ b/packages/pds/src/api/app/bsky/unspecced/getTaggedSuggestions.ts @@ -8,14 +8,9 @@ export default function (server: Server, ctx: AppContext) { if (!bskyAppView) return server.app.bsky.unspecced.getTaggedSuggestions({ auth: ctx.authVerifier.access, - handler: async ({ auth, params, req }) => { + handler: async ({ req, auth }) => { const requester = auth.credentials.did - return pipethrough( - bskyAppView.url, - 'app.bsky.unspecced.getTaggedSuggestions', - params, - await ctx.appviewAuthHeaders(requester, req), - ) + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/com/atproto/admin/createCommunicationTemplate.ts b/packages/pds/src/api/com/atproto/admin/createCommunicationTemplate.ts index 7b6939270a2..d3d1ebfd968 100644 --- a/packages/pds/src/api/com/atproto/admin/createCommunicationTemplate.ts +++ b/packages/pds/src/api/com/atproto/admin/createCommunicationTemplate.ts @@ -1,22 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' +import { pipethroughProcedure } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { moderationAgent } = ctx - if (!moderationAgent) return server.com.atproto.admin.createCommunicationTemplate({ - auth: ctx.authVerifier.role, - handler: async ({ req, input }) => { - const { data: result } = - await moderationAgent.com.atproto.admin.createCommunicationTemplate( - input.body, - authPassthru(req, true), - ) - return { - encoding: 'application/json', - body: result, - } + auth: ctx.authVerifier.access, + handler: async ({ req, input, auth }) => { + const requester = auth.credentials.did + return pipethroughProcedure(ctx, req, input.body, requester) }, }) } diff --git a/packages/pds/src/api/com/atproto/admin/deleteCommunicationTemplate.ts b/packages/pds/src/api/com/atproto/admin/deleteCommunicationTemplate.ts index d10c2564571..7a612dc1162 100644 --- a/packages/pds/src/api/com/atproto/admin/deleteCommunicationTemplate.ts +++ b/packages/pds/src/api/com/atproto/admin/deleteCommunicationTemplate.ts @@ -1,17 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' +import { pipethroughProcedure } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { moderationAgent } = ctx - if (!moderationAgent) return server.com.atproto.admin.deleteCommunicationTemplate({ - auth: ctx.authVerifier.role, - handler: async ({ req, input }) => { - await moderationAgent.com.atproto.admin.deleteCommunicationTemplate( - input.body, - authPassthru(req, true), - ) + auth: ctx.authVerifier.access, + handler: async ({ req, input, auth }) => { + const requester = auth.credentials.did + await pipethroughProcedure(ctx, req, input.body, requester) }, }) } diff --git a/packages/pds/src/api/com/atproto/admin/emitModerationEvent.ts b/packages/pds/src/api/com/atproto/admin/emitModerationEvent.ts index 65bb4c36d0e..1658720d674 100644 --- a/packages/pds/src/api/com/atproto/admin/emitModerationEvent.ts +++ b/packages/pds/src/api/com/atproto/admin/emitModerationEvent.ts @@ -1,22 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' +import { pipethroughProcedure } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { moderationAgent } = ctx - if (!moderationAgent) return server.com.atproto.admin.emitModerationEvent({ - auth: ctx.authVerifier.role, - handler: async ({ req, input }) => { - const { data: result } = - await moderationAgent.com.atproto.admin.emitModerationEvent( - input.body, - authPassthru(req, true), - ) - return { - encoding: 'application/json', - body: result, - } + auth: ctx.authVerifier.access, + handler: async ({ req, input, auth }) => { + const requester = auth.credentials.did + return pipethroughProcedure(ctx, req, input.body, requester) }, }) } diff --git a/packages/pds/src/api/com/atproto/admin/getModerationEvent.ts b/packages/pds/src/api/com/atproto/admin/getModerationEvent.ts index a5e579baa58..9d81d7dee59 100644 --- a/packages/pds/src/api/com/atproto/admin/getModerationEvent.ts +++ b/packages/pds/src/api/com/atproto/admin/getModerationEvent.ts @@ -1,22 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' +import { pipethrough } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { moderationAgent } = ctx - if (!moderationAgent) return server.com.atproto.admin.getModerationEvent({ - auth: ctx.authVerifier.role, - handler: async ({ req, params }) => { - const { data } = - await moderationAgent.com.atproto.admin.getModerationEvent( - params, - authPassthru(req), - ) - return { - encoding: 'application/json', - body: data, - } + auth: ctx.authVerifier.access, + handler: async ({ req, auth }) => { + const requester = auth.credentials.did + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/com/atproto/admin/getRecord.ts b/packages/pds/src/api/com/atproto/admin/getRecord.ts index 3cff5508683..52c7686b5b1 100644 --- a/packages/pds/src/api/com/atproto/admin/getRecord.ts +++ b/packages/pds/src/api/com/atproto/admin/getRecord.ts @@ -1,22 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' +import { pipethrough } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { moderationAgent } = ctx - if (!moderationAgent) return server.com.atproto.admin.getRecord({ - auth: ctx.authVerifier.role, - handler: async ({ req, params }) => { - const { data: recordDetailAppview } = - await moderationAgent.com.atproto.admin.getRecord( - params, - authPassthru(req), - ) - return { - encoding: 'application/json', - body: recordDetailAppview, - } + auth: ctx.authVerifier.access, + handler: async ({ req, auth }) => { + const requester = auth.credentials.did + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/com/atproto/admin/getRepo.ts b/packages/pds/src/api/com/atproto/admin/getRepo.ts index 880b407ce79..d380570c16b 100644 --- a/packages/pds/src/api/com/atproto/admin/getRepo.ts +++ b/packages/pds/src/api/com/atproto/admin/getRepo.ts @@ -1,21 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' +import { pipethrough } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { moderationAgent } = ctx - if (!moderationAgent) return server.com.atproto.admin.getRepo({ - auth: ctx.authVerifier.role, - handler: async ({ req, params }) => { - const res = await moderationAgent.com.atproto.admin.getRepo( - params, - authPassthru(req), - ) - return { - encoding: 'application/json', - body: res.data, - } + auth: ctx.authVerifier.access, + handler: async ({ req, auth }) => { + const requester = auth.credentials.did + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/com/atproto/admin/listCommunicationTemplates.ts b/packages/pds/src/api/com/atproto/admin/listCommunicationTemplates.ts index dfe3a74bce8..520e0b68c97 100644 --- a/packages/pds/src/api/com/atproto/admin/listCommunicationTemplates.ts +++ b/packages/pds/src/api/com/atproto/admin/listCommunicationTemplates.ts @@ -1,22 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' +import { pipethrough } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { moderationAgent } = ctx - if (!moderationAgent) return server.com.atproto.admin.listCommunicationTemplates({ - auth: ctx.authVerifier.role, - handler: async ({ req }) => { - const { data: result } = - await moderationAgent.com.atproto.admin.listCommunicationTemplates( - {}, - authPassthru(req, true), - ) - return { - encoding: 'application/json', - body: result, - } + auth: ctx.authVerifier.access, + handler: async ({ req, auth }) => { + const requester = auth.credentials.did + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/com/atproto/admin/queryModerationEvents.ts b/packages/pds/src/api/com/atproto/admin/queryModerationEvents.ts index 2d33ca6d466..87e173917a6 100644 --- a/packages/pds/src/api/com/atproto/admin/queryModerationEvents.ts +++ b/packages/pds/src/api/com/atproto/admin/queryModerationEvents.ts @@ -1,22 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' +import { pipethrough } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { moderationAgent } = ctx - if (!moderationAgent) return server.com.atproto.admin.queryModerationEvents({ - auth: ctx.authVerifier.role, - handler: async ({ req, params }) => { - const { data: result } = - await moderationAgent.com.atproto.admin.queryModerationEvents( - params, - authPassthru(req), - ) - return { - encoding: 'application/json', - body: result, - } + auth: ctx.authVerifier.access, + handler: async ({ req, auth }) => { + const requester = auth.credentials.did + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/com/atproto/admin/queryModerationStatuses.ts b/packages/pds/src/api/com/atproto/admin/queryModerationStatuses.ts index c31125ce114..b10c4c0efc0 100644 --- a/packages/pds/src/api/com/atproto/admin/queryModerationStatuses.ts +++ b/packages/pds/src/api/com/atproto/admin/queryModerationStatuses.ts @@ -1,22 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' +import { pipethrough } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { moderationAgent } = ctx - if (!moderationAgent) return server.com.atproto.admin.queryModerationStatuses({ - auth: ctx.authVerifier.role, - handler: async ({ req, params }) => { - const { data } = - await moderationAgent.com.atproto.admin.queryModerationStatuses( - params, - authPassthru(req), - ) - return { - encoding: 'application/json', - body: data, - } + auth: ctx.authVerifier.access, + handler: async ({ req, auth }) => { + const requester = auth.credentials.did + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/com/atproto/admin/searchRepos.ts b/packages/pds/src/api/com/atproto/admin/searchRepos.ts index d09ff7b2327..e3d67470e29 100644 --- a/packages/pds/src/api/com/atproto/admin/searchRepos.ts +++ b/packages/pds/src/api/com/atproto/admin/searchRepos.ts @@ -1,22 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' +import { pipethrough } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { moderationAgent } = ctx - if (!moderationAgent) return server.com.atproto.admin.searchRepos({ - auth: ctx.authVerifier.role, - handler: async ({ req, params }) => { - const { data: result } = - await moderationAgent.com.atproto.admin.searchRepos( - params, - authPassthru(req), - ) - return { - encoding: 'application/json', - body: result, - } + auth: ctx.authVerifier.access, + handler: async ({ req, auth }) => { + const requester = auth.credentials.did + return pipethrough(ctx, req, requester) }, }) } diff --git a/packages/pds/src/api/com/atproto/admin/sendEmail.ts b/packages/pds/src/api/com/atproto/admin/sendEmail.ts index 028a0b55079..6e30159c204 100644 --- a/packages/pds/src/api/com/atproto/admin/sendEmail.ts +++ b/packages/pds/src/api/com/atproto/admin/sendEmail.ts @@ -7,7 +7,7 @@ import { resultPassthru } from '../../../proxy' export default function (server: Server, ctx: AppContext) { server.com.atproto.admin.sendEmail({ auth: ctx.authVerifier.roleOrModService, - handler: async ({ input, auth, req }) => { + handler: async ({ input, auth }) => { if (auth.credentials.type === 'role' && !auth.credentials.moderator) { throw new AuthRequiredError('Insufficient privileges') } @@ -34,7 +34,6 @@ export default function (server: Server, ctx: AppContext) { ...(await ctx.serviceAuthHeaders( recipientDid, ctx.cfg.entryway?.did, - req, )), }), ) diff --git a/packages/pds/src/api/com/atproto/admin/updateCommunicationTemplate.ts b/packages/pds/src/api/com/atproto/admin/updateCommunicationTemplate.ts index c548a83bf03..ef7d05667b2 100644 --- a/packages/pds/src/api/com/atproto/admin/updateCommunicationTemplate.ts +++ b/packages/pds/src/api/com/atproto/admin/updateCommunicationTemplate.ts @@ -1,23 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { authPassthru } from '../../../proxy' +import { pipethroughProcedure } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - const { moderationAgent } = ctx - if (!moderationAgent) return server.com.atproto.admin.updateCommunicationTemplate({ - auth: ctx.authVerifier.role, - handler: async ({ req, input }) => { - const { data: result } = - await moderationAgent.com.atproto.admin.updateCommunicationTemplate( - input.body, - authPassthru(req, true), - ) - - return { - encoding: 'application/json', - body: result, - } + auth: ctx.authVerifier.access, + handler: async ({ req, input, auth }) => { + const requester = auth.credentials.did + return pipethroughProcedure(ctx, req, input.body, requester) }, }) } diff --git a/packages/pds/src/api/com/atproto/moderation/createReport.ts b/packages/pds/src/api/com/atproto/moderation/createReport.ts index 64ed5c20005..f5b65fbd0cb 100644 --- a/packages/pds/src/api/com/atproto/moderation/createReport.ts +++ b/packages/pds/src/api/com/atproto/moderation/createReport.ts @@ -1,29 +1,13 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { InvalidRequestError } from '@atproto/xrpc-server' +import { pipethroughProcedure } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { server.com.atproto.moderation.createReport({ auth: ctx.authVerifier.accessCheckTakedown, - handler: async ({ input, auth }) => { + handler: async ({ req, input, auth }) => { const requester = auth.credentials.did - if (!ctx.reportingAgent) { - throw new InvalidRequestError( - 'Your hosting service is not configured with a moderation provider. If this seems in error, reach out to your hosting provider.', - ) - } - const { data: result } = - await ctx.reportingAgent.com.atproto.moderation.createReport( - input.body, - { - ...(await ctx.reportingAuthHeaders(requester)), - encoding: 'application/json', - }, - ) - return { - encoding: 'application/json', - body: result, - } + return pipethroughProcedure(ctx, req, input.body, requester) }, }) } diff --git a/packages/pds/src/api/com/atproto/repo/getRecord.ts b/packages/pds/src/api/com/atproto/repo/getRecord.ts index 3d8b44099d4..5a0d0fae441 100644 --- a/packages/pds/src/api/com/atproto/repo/getRecord.ts +++ b/packages/pds/src/api/com/atproto/repo/getRecord.ts @@ -5,7 +5,7 @@ import { InvalidRequestError } from '@atproto/xrpc-server' import { pipethrough } from '../../../../pipethrough' export default function (server: Server, ctx: AppContext) { - server.com.atproto.repo.getRecord(async ({ params }) => { + server.com.atproto.repo.getRecord(async ({ req, params }) => { const { repo, collection, rkey, cid } = params const did = await ctx.accountManager.getDidForActor(repo) @@ -32,10 +32,6 @@ export default function (server: Server, ctx: AppContext) { throw new InvalidRequestError(`Could not locate record`) } - return await pipethrough( - ctx.cfg.bskyAppView.url, - 'com.atproto.repo.getRecord', - params, - ) + return await pipethrough(ctx, req) }) } diff --git a/packages/pds/src/config/config.ts b/packages/pds/src/config/config.ts index 8ca807d39b5..bbdb5ee797c 100644 --- a/packages/pds/src/config/config.ts +++ b/packages/pds/src/config/config.ts @@ -24,6 +24,7 @@ export const envToCfg = (env: ServerEnvironment): ServerConfig => { termsOfServiceUrl: env.termsOfServiceUrl, acceptingImports: env.acceptingImports ?? true, blobUploadLimit: env.blobUploadLimit ?? 5 * 1024 * 1024, // 5mb + devMode: env.devMode ?? false, } const dbLoc = (name: string) => { @@ -280,6 +281,7 @@ export type ServiceConfig = { termsOfServiceUrl?: string acceptingImports: boolean blobUploadLimit: number + devMode: boolean } export type DatabaseConfig = { diff --git a/packages/pds/src/config/env.ts b/packages/pds/src/config/env.ts index fb5aed8232f..a334c0c51d3 100644 --- a/packages/pds/src/config/env.ts +++ b/packages/pds/src/config/env.ts @@ -11,6 +11,7 @@ export const readEnv = (): ServerEnvironment => { termsOfServiceUrl: envStr('PDS_TERMS_OF_SERVICE_URL'), acceptingImports: envBool('PDS_ACCEPTING_REPO_IMPORTS'), blobUploadLimit: envInt('PDS_BLOB_UPLOAD_LIMIT'), + devMode: envBool('PDS_DEV_MODE'), // database dataDirectory: envStr('PDS_DATA_DIRECTORY'), @@ -118,6 +119,7 @@ export type ServerEnvironment = { termsOfServiceUrl?: string acceptingImports?: boolean blobUploadLimit?: number + devMode?: boolean // database dataDirectory?: string diff --git a/packages/pds/src/context.ts b/packages/pds/src/context.ts index dcffec1bde4..f2e3834e23d 100644 --- a/packages/pds/src/context.ts +++ b/packages/pds/src/context.ts @@ -1,5 +1,4 @@ import assert from 'node:assert' -import express from 'express' import * as nodemailer from 'nodemailer' import { Redis } from 'ioredis' import * as plc from '@did-plc/lib' @@ -251,37 +250,18 @@ export class AppContext { }) } - async appviewAuthHeaders(did: string, req: express.Request | null) { + async appviewAuthHeaders(did: string) { assert(this.cfg.bskyAppView) - return this.serviceAuthHeaders(did, this.cfg.bskyAppView.did, req) + return this.serviceAuthHeaders(did, this.cfg.bskyAppView.did) } - async moderationAuthHeaders(did: string) { - assert(this.cfg.modService) - return this.serviceAuthHeaders(did, this.cfg.modService.did, null) - } - - async reportingAuthHeaders(did: string) { - assert(this.cfg.reportService) - return this.serviceAuthHeaders(did, this.cfg.reportService.did, null) - } - - async serviceAuthHeaders( - did: string, - aud: string, - req: express.Request | null, - ) { + async serviceAuthHeaders(did: string, aud: string) { const keypair = await this.actorStore.keypair(did) - const authHeaders = await createServiceAuthHeaders({ + return createServiceAuthHeaders({ iss: did, aud, keypair, }) - const labelerHeader = req?.header('atproto-labelers') - if (labelerHeader) { - authHeaders.headers['atproto-labelers'] = labelerHeader - } - return authHeaders } } diff --git a/packages/pds/src/pipethrough.ts b/packages/pds/src/pipethrough.ts index a4c11856502..0d9c00737b5 100644 --- a/packages/pds/src/pipethrough.ts +++ b/packages/pds/src/pipethrough.ts @@ -1,22 +1,141 @@ +import express from 'express' import * as ui8 from 'uint8arrays' -import { jsonToLex } from '@atproto/lexicon' -import { HandlerPipeThrough } from '@atproto/xrpc-server' -import { CallOptions, ResponseType, XRPCError } from '@atproto/xrpc' -import { lexicons } from './lexicon/lexicons' +import net from 'node:net' +import { LexValue, jsonToLex, stringifyLex } from '@atproto/lexicon' +import { HandlerPipeThrough, InvalidRequestError } from '@atproto/xrpc-server' +import { ResponseType, XRPCError } from '@atproto/xrpc' +import { ids, lexicons } from './lexicon/lexicons' import { httpLogger } from './logger' -import { noUndefinedVals } from '@atproto/common' +import { getServiceEndpoint, noUndefinedVals } from '@atproto/common' +import AppContext from './context' + +const defaultService = ( + ctx: AppContext, + path: string, +): { url: string; did: string } | null => { + const nsid = path.replace('/xrpc/', '') + switch (nsid) { + case ids.ComAtprotoAdminCreateCommunicationTemplate: + case ids.ComAtprotoAdminDeleteCommunicationTemplate: + case ids.ComAtprotoAdminEmitModerationEvent: + case ids.ComAtprotoAdminGetModerationEvent: + case ids.ComAtprotoAdminGetRecord: + case ids.ComAtprotoAdminGetRepo: + case ids.ComAtprotoAdminListCommunicationTemplates: + case ids.ComAtprotoAdminQueryModerationEvents: + case ids.ComAtprotoAdminQueryModerationStatuses: + case ids.ComAtprotoAdminSearchRepos: + case ids.ComAtprotoAdminUpdateCommunicationTemplate: + return ctx.cfg.modService + case ids.ComAtprotoModerationCreateReport: + return ctx.cfg.reportService + default: + return ctx.cfg.bskyAppView + } +} export const pipethrough = async ( - serviceUrl: string, - nsid: string, - params: Record, - opts?: CallOptions, + ctx: AppContext, + req: express.Request, + requester?: string, + audOverride?: string, ): Promise => { - const url = constructUrl(serviceUrl, nsid, params) + const { url, headers } = await createUrlAndHeaders( + ctx, + req, + requester, + audOverride, + ) + const reqInit: RequestInit = { + headers, + } + return doProxy(url, reqInit) +} + +export const pipethroughProcedure = async ( + ctx: AppContext, + req: express.Request, + body: LexValue, + requester?: string, + audOverride?: string, +) => { + const { url, headers } = await createUrlAndHeaders( + ctx, + req, + requester, + audOverride, + ) + const reqInit: RequestInit & { duplex: string } = { + method: 'post', + headers, + body: new TextEncoder().encode(stringifyLex(body)), + duplex: 'half', + } + return doProxy(url, reqInit) +} + +export const parseProxyHeader = async ( + ctx: AppContext, + req: express.Request, +): Promise<{ did: string; serviceUrl: string } | undefined> => { + const proxyTo = req.header('atproto-proxy') + if (!proxyTo) return + const [did, serviceId] = proxyTo.split('#') + if (!serviceId) { + throw new InvalidRequestError('no service id specified') + } + const didDoc = await ctx.idResolver.did.resolve(did) + if (!didDoc) { + throw new InvalidRequestError('could not resolve proxy did') + } + const serviceUrl = getServiceEndpoint(didDoc, { id: `#${serviceId}` }) + if (!serviceUrl) { + throw new InvalidRequestError('could not resolve proxy did service url') + } + return { did, serviceUrl } +} + +const HEADERS_TO_FORWARD = [ + 'accept-language', + 'content-type', + 'atproto-labelers', +] + +export const createUrlAndHeaders = async ( + ctx: AppContext, + req: express.Request, + requester?: string, + audOverride?: string, +): Promise<{ url: URL; headers: { authorization?: string } }> => { + const proxyTo = await parseProxyHeader(ctx, req) + const defaultProxy = defaultService(ctx, req.path) + const serviceUrl = proxyTo?.serviceUrl ?? defaultProxy?.url + const aud = audOverride ?? proxyTo?.did ?? defaultProxy?.did + if (!serviceUrl || !aud) { + throw new InvalidRequestError(`No service configured for ${req.path}`) + } + const url = new URL(req.originalUrl, serviceUrl) + if (!ctx.cfg.service.devMode && !isSafeUrl(url)) { + throw new InvalidRequestError(`Invalid service url: ${url.toString()}`) + } + const headers = requester + ? (await ctx.serviceAuthHeaders(requester, aud)).headers + : {} + // forward select headers to upstream services + for (const header of HEADERS_TO_FORWARD) { + const val = req.headers[header] + if (val) { + headers[header] = val + } + } + return { url, headers } +} + +export const doProxy = async (url: URL, reqInit: RequestInit) => { let res: Response let buffer: ArrayBuffer try { - res = await fetch(url, opts) + res = await fetch(url, reqInit) buffer = await res.arrayBuffer() } catch (err) { httpLogger.warn({ err }, 'pipethrough network error') @@ -35,34 +154,18 @@ export const pipethrough = async ( const encoding = res.headers.get('content-type') ?? 'application/json' const repoRevHeader = res.headers.get('atproto-repo-rev') const contentLanguage = res.headers.get('content-language') - const headers = noUndefinedVals({ + const resHeaders = noUndefinedVals({ ['atproto-repo-rev']: repoRevHeader ?? undefined, ['content-language']: contentLanguage ?? undefined, }) - return { encoding, buffer, headers } + return { encoding, buffer, headers: resHeaders } } -export const constructUrl = ( - serviceUrl: string, - nsid: string, - params?: Record, -): string => { - const uri = new URL(serviceUrl) - uri.pathname = `/xrpc/${nsid}` - - for (const [key, value] of Object.entries(params ?? {})) { - if (value === undefined) { - continue - } else if (Array.isArray(value)) { - for (const item of value) { - uri.searchParams.append(key, String(item)) - } - } else { - uri.searchParams.set(key, String(value)) - } - } - - return uri.toString() +const isSafeUrl = (url: URL) => { + if (url.protocol !== 'https:') return false + if (!url.hostname || url.hostname === 'localhost') return false + if (net.isIP(url.hostname) !== 0) return false + return true } export const parseRes = (nsid: string, res: HandlerPipeThrough): T => { diff --git a/packages/pds/tests/proxied/proxy-header.test.ts b/packages/pds/tests/proxied/proxy-header.test.ts new file mode 100644 index 00000000000..d00dc3bb342 --- /dev/null +++ b/packages/pds/tests/proxied/proxy-header.test.ts @@ -0,0 +1,168 @@ +import http from 'node:http' +import assert from 'node:assert' +import express from 'express' +import axios from 'axios' +import * as plc from '@did-plc/lib' +import { SeedClient, TestNetworkNoAppView, usersSeed } from '@atproto/dev-env' +import getPort from 'get-port' +import { Keypair } from '@atproto/crypto' +import { verifyJwt } from '@atproto/xrpc-server' + +describe('proxy header', () => { + let network: TestNetworkNoAppView + let sc: SeedClient + + let alice: string + + let proxyServer: ProxyServer + + beforeAll(async () => { + network = await TestNetworkNoAppView.create({ + dbPostgresSchema: 'proxy_header', + }) + sc = network.getSeedClient() + await usersSeed(sc) + + proxyServer = await ProxyServer.create( + network.pds.ctx.plcClient, + network.pds.ctx.plcRotationKey, + 'atproto_test', + ) + + alice = sc.dids.alice + await network.processAll() + }) + + afterAll(async () => { + await proxyServer.close() + await network.close() + }) + + const assertAxiosErr = async (promise: Promise, msg: string) => { + try { + await promise + } catch (err) { + if (!axios.isAxiosError(err)) { + throw err + } + expect(err.response?.data?.['message']).toEqual(msg) + return + } + throw new Error('no error thrown') + } + + it('proxies requests based on header', async () => { + const path = `/xrpc/app.bsky.actor.getProfile?actor=${alice}` + await axios.get(`${network.pds.url}${path}`, { + headers: { + ...sc.getHeaders(alice), + 'atproto-proxy': `${proxyServer.did}#atproto_test`, + }, + }) + const req = proxyServer.requests.at(-1) + assert(req) + expect(req.url).toEqual(path) + assert(req.auth) + const verified = await verifyJwt( + req.auth.replace('Bearer ', ''), + proxyServer.did, + (iss) => network.pds.ctx.idResolver.did.resolveAtprotoKey(iss, true), + ) + expect(verified.aud).toBe(proxyServer.did) + expect(verified.iss).toBe(alice) + }) + + it('fails on a non-existant did', async () => { + const path = `/xrpc/app.bsky.actor.getProfile?actor=${alice}` + const attempt = axios.get(`${network.pds.url}${path}`, { + headers: { + ...sc.getHeaders(alice), + 'atproto-proxy': `did:plc:12345678123456781234578#atproto_test`, + }, + }) + await assertAxiosErr(attempt, 'could not resolve proxy did') + expect(proxyServer.requests.length).toBe(1) + }) + + it('fails when a service is not specified', async () => { + const path = `/xrpc/app.bsky.actor.getProfile?actor=${alice}` + const attempt = axios.get(`${network.pds.url}${path}`, { + headers: { + ...sc.getHeaders(alice), + 'atproto-proxy': proxyServer.did, + }, + }) + await assertAxiosErr(attempt, 'no service id specified') + expect(proxyServer.requests.length).toBe(1) + }) + + it('fails on a non-existant service', async () => { + const path = `/xrpc/app.bsky.actor.getProfile?actor=${alice}` + const attempt = axios.get(`${network.pds.url}${path}`, { + headers: { + ...sc.getHeaders(alice), + 'atproto-proxy': `${proxyServer.did}#atproto_bad`, + }, + }) + await assertAxiosErr(attempt, 'could not resolve proxy did service url') + expect(proxyServer.requests.length).toBe(1) + }) +}) + +type ProxyReq = { + url: string + auth: string | undefined +} + +class ProxyServer { + constructor( + public server: http.Server, + public url: string, + public did: string, + public requests: ProxyReq[], + ) {} + + static async create( + plcClient: plc.Client, + keypair: Keypair, + serviceId: string, + ): Promise { + const requests: ProxyReq[] = [] + const app = express() + app.get('*', (req, res) => { + requests.push({ + url: req.url, + auth: req.header('authorization'), + }) + res.sendStatus(200) + }) + const port = await getPort() + const server = app.listen(port) + const url = `http://localhost:${port}` + const plcOp = await plc.signOperation( + { + type: 'plc_operation', + rotationKeys: [keypair.did()], + alsoKnownAs: [], + verificationMethods: {}, + services: { + [serviceId]: { + type: 'TestAtprotoService', + endpoint: url, + }, + }, + prev: null, + }, + keypair, + ) + const did = await plc.didForCreateOp(plcOp) + await plcClient.sendOperation(did, plcOp) + return new ProxyServer(server, url, did, requests) + } + + close(): Promise { + return new Promise((resolve) => { + this.server.close(() => resolve()) + }) + } +}