diff --git a/src/middleware/auth98Middleware.ts b/src/middleware/auth98Middleware.ts index abecea7..34d6937 100644 --- a/src/middleware/auth98Middleware.ts +++ b/src/middleware/auth98Middleware.ts @@ -3,7 +3,7 @@ import { HTTPException } from 'hono'; import { type AppContext, type AppMiddleware } from '@/app.ts'; import { findUser, User } from '@/db/users.ts'; -import { ConnectSigner } from '@/signers/ConnectSigner.ts'; +import { ReadOnlySigner } from '@/signers/ReadOnlySigner.ts'; import { localRequest } from '@/utils/api.ts'; import { buildAuthEventTemplate, @@ -22,7 +22,7 @@ function auth98Middleware(opts: ParseAuthRequestOpts = {}): AppMiddleware { const result = await parseAuthRequest(req, opts); if (result.success) { - c.set('signer', new ConnectSigner(result.data.pubkey)); + c.set('signer', new ReadOnlySigner(result.data.pubkey)); c.set('proof', result.data); } @@ -70,7 +70,8 @@ function withProof( opts?: ParseAuthRequestOpts, ): AppMiddleware { return async (c, next) => { - const pubkey = await c.get('signer')?.getPublicKey(); + const signer = c.get('signer'); + const pubkey = await signer?.getPublicKey(); const proof = c.get('proof') || await obtainProof(c, opts); // Prevent people from accidentally using the wrong account. This has no other security implications. @@ -79,8 +80,12 @@ function withProof( } if (proof) { - c.set('signer', new ConnectSigner(proof.pubkey)); c.set('proof', proof); + + if (!signer) { + c.set('signer', new ReadOnlySigner(proof.pubkey)); + } + await handler(c, proof, next); } else { throw new HTTPException(401, { message: 'No proof' }); diff --git a/src/middleware/signerMiddleware.ts b/src/middleware/signerMiddleware.ts index 1d35708..f52aa20 100644 --- a/src/middleware/signerMiddleware.ts +++ b/src/middleware/signerMiddleware.ts @@ -1,11 +1,11 @@ import { NSecSigner } from '@nostrify/nostrify'; -import { Stickynotes } from '@soapbox/stickynotes'; import { nip19 } from 'nostr-tools'; import { AppMiddleware } from '@/app.ts'; import { ConnectSigner } from '@/signers/ConnectSigner.ts'; - -const console = new Stickynotes('ditto:signerMiddleware'); +import { ReadOnlySigner } from '@/signers/ReadOnlySigner.ts'; +import { HTTPException } from 'hono'; +import { DittoDB } from '@/db/DittoDB.ts'; /** We only accept "Bearer" type. */ const BEARER_REGEX = new RegExp(`^Bearer (${nip19.BECH32_REGEX.source})$`); @@ -18,22 +18,38 @@ export const signerMiddleware: AppMiddleware = async (c, next) => { if (match) { const [_, bech32] = match; - try { - const decoded = nip19.decode(bech32!); + if (bech32.startsWith('token1')) { + try { + const kysely = await DittoDB.getInstance(); - switch (decoded.type) { - case 'npub': - c.set('signer', new ConnectSigner(decoded.data)); - break; - case 'nprofile': - c.set('signer', new ConnectSigner(decoded.data.pubkey, decoded.data.relays)); - break; - case 'nsec': - c.set('signer', new NSecSigner(decoded.data)); - break; + const { user_pubkey, server_seckey, relays } = await kysely + .selectFrom('connections') + .select(['user_pubkey', 'server_seckey', 'relays']) + .where('api_token', '=', bech32) + .executeTakeFirstOrThrow(); + + c.set('signer', new ConnectSigner(user_pubkey, new NSecSigner(server_seckey), JSON.parse(relays))); + } catch { + throw new HTTPException(401); + } + } else { + try { + const decoded = nip19.decode(bech32!); + + switch (decoded.type) { + case 'npub': + c.set('signer', new ReadOnlySigner(decoded.data)); + break; + case 'nprofile': + c.set('signer', new ReadOnlySigner(decoded.data.pubkey)); + break; + case 'nsec': + c.set('signer', new NSecSigner(decoded.data)); + break; + } + } catch { + throw new HTTPException(401); } - } catch { - console.debug('The user is not logged in'); } } diff --git a/src/signers/ConnectSigner.ts b/src/signers/ConnectSigner.ts index f482413..35b68fb 100644 --- a/src/signers/ConnectSigner.ts +++ b/src/signers/ConnectSigner.ts @@ -1,7 +1,6 @@ // deno-lint-ignore-file require-await import { NConnectSigner, NostrEvent, NostrSigner } from '@nostrify/nostrify'; -import { AdminSigner } from '@/signers/AdminSigner.ts'; import { Storages } from '@/storages.ts'; /** @@ -12,16 +11,16 @@ import { Storages } from '@/storages.ts'; export class ConnectSigner implements NostrSigner { private signer: Promise; - constructor(private pubkey: string, private relays?: string[]) { - this.signer = this.init(); + constructor(private pubkey: string, signer: NostrSigner, private relays?: string[]) { + this.signer = this.init(signer); } - async init(): Promise { + async init(signer: NostrSigner): Promise { return new NConnectSigner({ pubkey: this.pubkey, // TODO: use a remote relay for `nprofile` signing (if present and `Conf.relay` isn't already in the list) relay: await Storages.pubsub(), - signer: new AdminSigner(), + signer, timeout: 60000, }); } diff --git a/src/signers/ReadOnlySigner.ts b/src/signers/ReadOnlySigner.ts new file mode 100644 index 0000000..e7c45fb --- /dev/null +++ b/src/signers/ReadOnlySigner.ts @@ -0,0 +1,17 @@ +// deno-lint-ignore-file require-await +import { NostrEvent, NostrSigner } from '@nostrify/nostrify'; +import { HTTPException } from 'hono'; + +export class ReadOnlySigner implements NostrSigner { + constructor(private pubkey: string) {} + + async signEvent(): Promise { + throw new HTTPException(401, { + message: "Can't sign events with just an npub", + }); + } + + async getPublicKey(): Promise { + return this.pubkey; + } +}