Daniel Holmgren 50c0ec176c
Service auth method binding (lxm) (#2663)
* add scopes to service auth impl

* add error to getServiceAuth

* send scoped tokens from pds

* clean up privileged access scopes & allow simple service auth tokens for app passwords

* integration into ozone

* fix up bsky tests

* cleanup xrpc-server tests

* fix up tests & types

* one more test

* fix read after write tests

* fix mod auth test

* convert scopes to be a single method name

* add scope check callback for auth verifier

* pds changes only

* fix feed generation tests

* use scope for ozone service profile

* dont verify scopes on pds yet

* tidy

* tidy imports

* changeset

* add tests

* tidy

* another changeset

* scope -> lxm

* tidy

* clean up scope references

* update nonce size

* pr feedback

* trim trailing slash

* nonce -> jti

* fix xrpc-server test

* allow service auth on uploadBlob

* fix build error

* changeset

* build, tidy

* xrpc-server: update lxm claim check error

* appview: temporarily permit labeler service calls to omit lxm claim

* xrpc-server: fix test

* changeset

* fix merged tests

---------

Co-authored-by: Devin Ivy <devinivy@gmail.com>
2024-08-18 15:46:07 -04:00

96 lines
2.7 KiB
TypeScript

import { once } from 'events'
import http from 'http'
import { AddressInfo } from 'net'
import express from 'express'
import { AtpAgent } from '@atproto/api'
import { TestNetworkNoAppView, SeedClient } from '@atproto/dev-env'
import { verifyJwt } from '@atproto/xrpc-server'
import usersSeed from '../seeds/users'
import { createServer } from '../../src/lexicon'
describe('notif service proxy', () => {
let network: TestNetworkNoAppView
let notifServer: http.Server
let notifDid: string
let agent: AtpAgent
let sc: SeedClient
const spy: { current: unknown } = { current: null }
beforeAll(async () => {
network = await TestNetworkNoAppView.create({
dbPostgresSchema: 'proxy_notifs',
})
network.pds.server.app.get
const plc = network.plc.getClient()
agent = network.pds.getClient()
sc = network.getSeedClient()
await usersSeed(sc)
await network.processAll()
// piggybacking existing plc did, turn it into a notif service
notifServer = await createMockNotifService(spy)
notifDid = sc.dids.dan
await plc.updateData(notifDid, network.pds.ctx.plcRotationKey, (x) => {
const addr = notifServer.address() as AddressInfo
x.services['bsky_notif'] = {
type: 'BskyNotificationService',
endpoint: `http://localhost:${addr.port}`,
}
return x
})
await network.pds.ctx.idResolver.did.resolve(notifDid, true)
})
afterAll(async () => {
await network.close()
notifServer.close()
await once(notifServer, 'close')
})
it('proxies to notif service.', async () => {
await agent.api.app.bsky.notification.registerPush(
{
serviceDid: notifDid,
token: 'tok1',
platform: 'web',
appId: 'app1',
},
{
headers: sc.getHeaders(sc.dids.bob),
encoding: 'application/json',
},
)
expect(spy.current?.['input']).toEqual({
serviceDid: notifDid,
token: 'tok1',
platform: 'web',
appId: 'app1',
})
const auth = await verifyJwt(
spy.current?.['jwt'] as string,
notifDid,
'app.bsky.notification.registerPush',
async (did) => {
const keypair = await network.pds.ctx.actorStore.keypair(did)
return keypair.did()
},
)
expect(auth.iss).toEqual(sc.dids.bob)
})
})
async function createMockNotifService(ref: { current: unknown }) {
const app = express()
const svc = createServer()
svc.app.bsky.notification.registerPush(({ input, req }) => {
ref.current = {
input: input.body,
jwt: req.headers.authorization?.replace('Bearer ', ''),
}
})
app.use(svc.xrpc.router)
const server = app.listen()
await once(server, 'listening')
return server
}