From 0141a732c3f7d137fd55dce4df60a0f2ef6ffeef Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 26 May 2024 19:21:15 -0500 Subject: [PATCH 1/9] Add connections table --- src/db/DittoTables.ts | 10 ++++++++++ src/db/migrations/023_add_connections.ts | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/db/migrations/023_add_connections.ts diff --git a/src/db/DittoTables.ts b/src/db/DittoTables.ts index c2d1f861..31d85425 100644 --- a/src/db/DittoTables.ts +++ b/src/db/DittoTables.ts @@ -2,6 +2,7 @@ export interface DittoTables { nostr_events: EventRow; nostr_tags: TagRow; nostr_fts5: EventFTSRow; + connections: ConnectionRow; unattached_media: UnattachedMediaRow; author_stats: AuthorStatsRow; event_stats: EventStatsRow; @@ -44,6 +45,15 @@ interface TagRow { value: string; } +interface ConnectionRow { + api_token: string; + user_pubkey: string; + server_seckey: Uint8Array; + server_pubkey: string; + relays: string; + connected_at: Date; +} + interface UnattachedMediaRow { id: string; pubkey: string; diff --git a/src/db/migrations/023_add_connections.ts b/src/db/migrations/023_add_connections.ts new file mode 100644 index 00000000..6568c50c --- /dev/null +++ b/src/db/migrations/023_add_connections.ts @@ -0,0 +1,17 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await db.schema + .createTable('connections') + .addColumn('api_token', 'text', (col) => col.primaryKey().unique().notNull()) + .addColumn('user_pubkey', 'text', (col) => col.notNull()) + .addColumn('server_seckey', 'blob', (col) => col.notNull()) + .addColumn('server_pubkey', 'text', (col) => col.notNull()) + .addColumn('relays', 'text', (col) => col.defaultTo('[]')) + .addColumn('connected_at', 'timestamp', (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)) + .execute(); +} + +export async function down(db: Kysely): Promise { + await db.schema.dropTable('connections').execute(); +} From b1a497b1276b7efe29d4ce07d1518d2eb2ef892f Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 26 May 2024 20:18:49 -0500 Subject: [PATCH 2/9] signerMiddleware: accept token1 bech32's --- src/middleware/auth98Middleware.ts | 13 +++++--- src/middleware/signerMiddleware.ts | 50 ++++++++++++++++++++---------- src/signers/ConnectSigner.ts | 9 +++--- src/signers/ReadOnlySigner.ts | 17 ++++++++++ 4 files changed, 63 insertions(+), 26 deletions(-) create mode 100644 src/signers/ReadOnlySigner.ts diff --git a/src/middleware/auth98Middleware.ts b/src/middleware/auth98Middleware.ts index abecea72..34d69379 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 1d357082..f52aa203 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 f482413d..35b68fba 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 00000000..e7c45fb0 --- /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; + } +} From c55cd2a97743c48e68c56eeea631a49579f6d30c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 26 May 2024 20:41:14 -0500 Subject: [PATCH 3/9] blob -> bytea --- src/db/migrations/023_add_connections.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/migrations/023_add_connections.ts b/src/db/migrations/023_add_connections.ts index 6568c50c..e734916b 100644 --- a/src/db/migrations/023_add_connections.ts +++ b/src/db/migrations/023_add_connections.ts @@ -5,7 +5,7 @@ export async function up(db: Kysely): Promise { .createTable('connections') .addColumn('api_token', 'text', (col) => col.primaryKey().unique().notNull()) .addColumn('user_pubkey', 'text', (col) => col.notNull()) - .addColumn('server_seckey', 'blob', (col) => col.notNull()) + .addColumn('server_seckey', 'bytea', (col) => col.notNull()) .addColumn('server_pubkey', 'text', (col) => col.notNull()) .addColumn('relays', 'text', (col) => col.defaultTo('[]')) .addColumn('connected_at', 'timestamp', (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)) From 1aa2bafc44bfd1c53755d899bb1eb3c4ab778831 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 26 May 2024 21:26:59 -0500 Subject: [PATCH 4/9] OAuth: add a "nostr" grant_type --- deno.json | 1 + src/controllers/api/oauth.ts | 55 +++++++++++++++++++++++++++++++++++- src/signers/ConnectSigner.ts | 2 +- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/deno.json b/deno.json index ac13eb4b..b925d200 100644 --- a/deno.json +++ b/deno.json @@ -24,6 +24,7 @@ "@isaacs/ttlcache": "npm:@isaacs/ttlcache@^1.4.1", "@noble/secp256k1": "npm:@noble/secp256k1@^2.0.0", "@nostrify/nostrify": "jsr:@nostrify/nostrify@^0.22.4", + "@scure/base": "npm:@scure/base@^1.1.6", "@sentry/deno": "https://deno.land/x/sentry@7.112.2/index.mjs", "@soapbox/kysely-deno-sqlite": "jsr:@soapbox/kysely-deno-sqlite@^2.1.0", "@soapbox/stickynotes": "jsr:@soapbox/stickynotes@^0.4.0", diff --git a/src/controllers/api/oauth.ts b/src/controllers/api/oauth.ts index c1407f43..b1309bce 100644 --- a/src/controllers/api/oauth.ts +++ b/src/controllers/api/oauth.ts @@ -1,12 +1,16 @@ +import { NConnectSigner, NSchema as n, NSecSigner } from '@nostrify/nostrify'; +import { bech32 } from '@scure/base'; import { encodeBase64 } from '@std/encoding/base64'; import { escape } from 'entities'; -import { nip19 } from 'nostr-tools'; +import { generateSecretKey, getPublicKey, nip19 } from 'nostr-tools'; import { z } from 'zod'; import { AppController } from '@/app.ts'; +import { DittoDB } from '@/db/DittoDB.ts'; import { nostrNow } from '@/utils.ts'; import { parseBody } from '@/utils/api.ts'; import { getClientConnectUri } from '@/utils/connect.ts'; +import { Storages } from '@/storages.ts'; const passwordGrantSchema = z.object({ grant_type: z.literal('password'), @@ -22,10 +26,18 @@ const credentialsGrantSchema = z.object({ grant_type: z.literal('client_credentials'), }); +const nostrGrantSchema = z.object({ + grant_type: z.literal('nostr'), + pubkey: n.id(), + relays: z.string().url().array().optional(), + secret: z.string().optional(), +}); + const createTokenSchema = z.discriminatedUnion('grant_type', [ passwordGrantSchema, codeGrantSchema, credentialsGrantSchema, + nostrGrantSchema, ]); const createTokenController: AppController = async (c) => { @@ -37,6 +49,13 @@ const createTokenController: AppController = async (c) => { } switch (result.data.grant_type) { + case 'nostr': + return c.json({ + access_token: await getToken(result.data), + token_type: 'Bearer', + scope: 'read write follow push', + created_at: nostrNow(), + }); case 'password': return c.json({ access_token: result.data.password, @@ -61,6 +80,40 @@ const createTokenController: AppController = async (c) => { } }; +async function getToken({ pubkey, secret, relays = [] }: z.infer): Promise<`token1${string}`> { + const kysely = await DittoDB.getInstance(); + const token = generateToken(); + + const serverSeckey = generateSecretKey(); + const serverPubkey = getPublicKey(serverSeckey); + + const signer = new NConnectSigner({ + pubkey, + signer: new NSecSigner(serverSeckey), + relay: await Storages.pubsub(), + timeout: 60_000, + }); + + await signer.connect(secret); + + await kysely.insertInto('connections').values({ + api_token: token, + user_pubkey: pubkey, + server_seckey: serverSeckey, + server_pubkey: serverPubkey, + relays: JSON.stringify(relays), + connected_at: new Date(), + }).execute(); + + return token; +} + +/** Generate a bech32 token for the API. */ +function generateToken(): `token1${string}` { + const words = bech32.toWords(generateSecretKey()); + return bech32.encode('token', words); +} + /** Display the OAuth form. */ const oauthController: AppController = async (c) => { const encodedUri = c.req.query('redirect_uri'); diff --git a/src/signers/ConnectSigner.ts b/src/signers/ConnectSigner.ts index 35b68fba..d4cf6032 100644 --- a/src/signers/ConnectSigner.ts +++ b/src/signers/ConnectSigner.ts @@ -21,7 +21,7 @@ export class ConnectSigner implements NostrSigner { // TODO: use a remote relay for `nprofile` signing (if present and `Conf.relay` isn't already in the list) relay: await Storages.pubsub(), signer, - timeout: 60000, + timeout: 60_000, }); } From a6a74a16a8edba11f9900fb060ef3c2c411d9f62 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 28 May 2024 13:48:27 -0500 Subject: [PATCH 5/9] Have OAuth form accept a bunker URI --- src/controllers/api/oauth.ts | 66 ++++++++++-------------------------- 1 file changed, 18 insertions(+), 48 deletions(-) diff --git a/src/controllers/api/oauth.ts b/src/controllers/api/oauth.ts index b1309bce..f4e68e1d 100644 --- a/src/controllers/api/oauth.ts +++ b/src/controllers/api/oauth.ts @@ -1,15 +1,13 @@ import { NConnectSigner, NSchema as n, NSecSigner } from '@nostrify/nostrify'; import { bech32 } from '@scure/base'; -import { encodeBase64 } from '@std/encoding/base64'; import { escape } from 'entities'; -import { generateSecretKey, getPublicKey, nip19 } from 'nostr-tools'; +import { generateSecretKey, getPublicKey } from 'nostr-tools'; import { z } from 'zod'; import { AppController } from '@/app.ts'; import { DittoDB } from '@/db/DittoDB.ts'; import { nostrNow } from '@/utils.ts'; import { parseBody } from '@/utils/api.ts'; -import { getClientConnectUri } from '@/utils/connect.ts'; import { Storages } from '@/storages.ts'; const passwordGrantSchema = z.object({ @@ -80,7 +78,9 @@ const createTokenController: AppController = async (c) => { } }; -async function getToken({ pubkey, secret, relays = [] }: z.infer): Promise<`token1${string}`> { +async function getToken( + { pubkey, secret, relays = [] }: { pubkey: string; secret?: string; relays?: string[] }, +): Promise<`token1${string}`> { const kysely = await DittoDB.getInstance(); const token = generateToken(); @@ -115,49 +115,26 @@ function generateToken(): `token1${string}` { } /** Display the OAuth form. */ -const oauthController: AppController = async (c) => { +const oauthController: AppController = (c) => { const encodedUri = c.req.query('redirect_uri'); if (!encodedUri) { return c.text('Missing `redirect_uri` query param.', 422); } const redirectUri = maybeDecodeUri(encodedUri); - const connectUri = await getClientConnectUri(c.req.raw.signal); - - const script = ` - window.addEventListener('load', function() { - if ('nostr' in window) { - nostr.getPublicKey().then(function(pubkey) { - document.getElementById('pubkey').value = pubkey; - document.getElementById('oauth_form').submit(); - }); - } - }); - `; - - const hash = encodeBase64(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(script))); - - c.res.headers.set( - 'content-security-policy', - `default-src 'self' 'sha256-${hash}'`, - ); return c.html(` Log in with Ditto -
- - +
-
- Nostr Connect `); @@ -178,16 +155,8 @@ function maybeDecodeUri(uri: string): string { /** Schema for FormData POSTed to the OAuthController. */ const oauthAuthorizeSchema = z.object({ - pubkey: z.string().regex(/^[0-9a-f]{64}$/).optional().catch(undefined), - nip19: z.string().regex(new RegExp(`^${nip19.BECH32_REGEX.source}$`)).optional().catch(undefined), + bunker_uri: z.string().url().refine((v) => v.startsWith('bunker://')), redirect_uri: z.string().url(), -}).superRefine((data, ctx) => { - if (!data.pubkey && !data.nip19) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: 'Missing `pubkey` or `nip19`.', - }); - } }); /** Controller the OAuth form is POSTed to. */ @@ -200,18 +169,19 @@ const oauthAuthorizeController: AppController = async (c) => { } // Parsed FormData values. - const { pubkey, nip19: nip19id, redirect_uri: redirectUri } = result.data; + const { bunker_uri, redirect_uri: redirectUri } = result.data; - if (pubkey) { - const encoded = nip19.npubEncode(pubkey!); - const url = addCodeToRedirectUri(redirectUri, encoded); - return c.redirect(url); - } else if (nip19id) { - const url = addCodeToRedirectUri(redirectUri, nip19id); - return c.redirect(url); - } + const bunker = new URL(bunker_uri); - return c.text('The Nostr ID was not provided or invalid.', 422); + const token = await getToken({ + pubkey: bunker.hostname, + secret: bunker.searchParams.get('secret') || undefined, + relays: bunker.searchParams.getAll('relay'), + }); + + const url = addCodeToRedirectUri(redirectUri, token); + + return c.redirect(url); }; /** Append the given `code` as a query param to the `redirect_uri`. */ From 981f8c5d226a39f9bfd88ed0ac2b49425b9429c0 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 28 May 2024 13:59:57 -0500 Subject: [PATCH 6/9] relay: remove enforceFilters function --- src/controllers/nostr/relay.ts | 40 ++++++++-------------------------- 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/src/controllers/nostr/relay.ts b/src/controllers/nostr/relay.ts index 4d239990..6fc08d05 100644 --- a/src/controllers/nostr/relay.ts +++ b/src/controllers/nostr/relay.ts @@ -4,29 +4,19 @@ import { NostrClientEVENT, NostrClientMsg, NostrClientREQ, - NostrEvent, - NostrFilter, + NostrRelayMsg, NSchema as n, } from '@nostrify/nostrify'; + +import { AppController } from '@/app.ts'; import { relayInfoController } from '@/controllers/nostr/relay-info.ts'; import * as pipeline from '@/pipeline.ts'; import { RelayError } from '@/RelayError.ts'; import { Storages } from '@/storages.ts'; -import type { AppController } from '@/app.ts'; -import { Conf } from '@/config.ts'; - /** Limit of initial events returned for a subscription. */ const FILTER_LIMIT = 100; -/** NIP-01 relay to client message. */ -type RelayMsg = - | ['EVENT', string, NostrEvent] - | ['NOTICE', string] - | ['EOSE', string] - | ['OK', string, boolean, string] - | ['COUNT', string, { count: number; approximate?: boolean }]; - /** Set up the Websocket connection. */ function connectStream(socket: WebSocket) { const controllers = new Map(); @@ -65,17 +55,15 @@ function connectStream(socket: WebSocket) { } /** Handle REQ. Start a subscription. */ - async function handleReq([_, subId, ...rest]: NostrClientREQ): Promise { - const filters = prepareFilters(rest); - + async function handleReq([_, subId, ...filters]: NostrClientREQ): Promise { const controller = new AbortController(); controllers.get(subId)?.abort(); controllers.set(subId, controller); - const db = await Storages.db(); + const store = await Storages.db(); const pubsub = await Storages.pubsub(); - for (const event of await db.query(filters, { limit: FILTER_LIMIT })) { + for (const event of await store.query(filters, { limit: FILTER_LIMIT })) { send(['EVENT', subId, event]); } @@ -118,30 +106,20 @@ function connectStream(socket: WebSocket) { } /** Handle COUNT. Return the number of events matching the filters. */ - async function handleCount([_, subId, ...rest]: NostrClientCOUNT): Promise { + async function handleCount([_, subId, ...filters]: NostrClientCOUNT): Promise { const store = await Storages.db(); - const { count } = await store.count(prepareFilters(rest)); + const { count } = await store.count(filters); send(['COUNT', subId, { count, approximate: false }]); } /** Send a message back to the client. */ - function send(msg: RelayMsg): void { + function send(msg: NostrRelayMsg): void { if (socket.readyState === WebSocket.OPEN) { socket.send(JSON.stringify(msg)); } } } -/** Enforce the filters with certain criteria. */ -function prepareFilters(filters: NostrClientREQ[2][]): NostrFilter[] { - return filters.map((filter) => { - const narrow = Boolean(filter.ids?.length || filter.authors?.length); - const search = narrow ? filter.search : `domain:${Conf.url.host} ${filter.search ?? ''}`; - // Return only local events unless the query is already narrow. - return { ...filter, search }; - }); -} - const relayController: AppController = (c, next) => { const upgrade = c.req.header('upgrade'); From 7221b5f2033e76096655500e9760e8a2af3cca33 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 29 May 2024 16:14:21 -0500 Subject: [PATCH 7/9] grant_type nostr -> nostr_bunker, connections -> nip46_tokens --- src/controllers/api/oauth.ts | 8 ++++---- src/db/DittoTables.ts | 4 ++-- .../{023_add_connections.ts => 023_add_nip46_tokens.ts} | 4 ++-- src/middleware/signerMiddleware.ts | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) rename src/db/migrations/{023_add_connections.ts => 023_add_nip46_tokens.ts} (87%) diff --git a/src/controllers/api/oauth.ts b/src/controllers/api/oauth.ts index f4e68e1d..242646b4 100644 --- a/src/controllers/api/oauth.ts +++ b/src/controllers/api/oauth.ts @@ -25,7 +25,7 @@ const credentialsGrantSchema = z.object({ }); const nostrGrantSchema = z.object({ - grant_type: z.literal('nostr'), + grant_type: z.literal('nostr_bunker'), pubkey: n.id(), relays: z.string().url().array().optional(), secret: z.string().optional(), @@ -47,7 +47,7 @@ const createTokenController: AppController = async (c) => { } switch (result.data.grant_type) { - case 'nostr': + case 'nostr_bunker': return c.json({ access_token: await getToken(result.data), token_type: 'Bearer', @@ -90,13 +90,13 @@ async function getToken( const signer = new NConnectSigner({ pubkey, signer: new NSecSigner(serverSeckey), - relay: await Storages.pubsub(), + relay: await Storages.pubsub(), // TODO: Use the relays from the request. timeout: 60_000, }); await signer.connect(secret); - await kysely.insertInto('connections').values({ + await kysely.insertInto('nip46_tokens').values({ api_token: token, user_pubkey: pubkey, server_seckey: serverSeckey, diff --git a/src/db/DittoTables.ts b/src/db/DittoTables.ts index 31d85425..65bc4261 100644 --- a/src/db/DittoTables.ts +++ b/src/db/DittoTables.ts @@ -2,7 +2,7 @@ export interface DittoTables { nostr_events: EventRow; nostr_tags: TagRow; nostr_fts5: EventFTSRow; - connections: ConnectionRow; + nip46_tokens: NIP46TokenRow; unattached_media: UnattachedMediaRow; author_stats: AuthorStatsRow; event_stats: EventStatsRow; @@ -45,7 +45,7 @@ interface TagRow { value: string; } -interface ConnectionRow { +interface NIP46TokenRow { api_token: string; user_pubkey: string; server_seckey: Uint8Array; diff --git a/src/db/migrations/023_add_connections.ts b/src/db/migrations/023_add_nip46_tokens.ts similarity index 87% rename from src/db/migrations/023_add_connections.ts rename to src/db/migrations/023_add_nip46_tokens.ts index e734916b..144bd1ec 100644 --- a/src/db/migrations/023_add_connections.ts +++ b/src/db/migrations/023_add_nip46_tokens.ts @@ -2,7 +2,7 @@ import { Kysely, sql } from 'kysely'; export async function up(db: Kysely): Promise { await db.schema - .createTable('connections') + .createTable('nip46_tokens') .addColumn('api_token', 'text', (col) => col.primaryKey().unique().notNull()) .addColumn('user_pubkey', 'text', (col) => col.notNull()) .addColumn('server_seckey', 'bytea', (col) => col.notNull()) @@ -13,5 +13,5 @@ export async function up(db: Kysely): Promise { } export async function down(db: Kysely): Promise { - await db.schema.dropTable('connections').execute(); + await db.schema.dropTable('nip46_tokens').execute(); } diff --git a/src/middleware/signerMiddleware.ts b/src/middleware/signerMiddleware.ts index f52aa203..5ea4235c 100644 --- a/src/middleware/signerMiddleware.ts +++ b/src/middleware/signerMiddleware.ts @@ -23,7 +23,7 @@ export const signerMiddleware: AppMiddleware = async (c, next) => { const kysely = await DittoDB.getInstance(); const { user_pubkey, server_seckey, relays } = await kysely - .selectFrom('connections') + .selectFrom('nip46_tokens') .select(['user_pubkey', 'server_seckey', 'relays']) .where('api_token', '=', bech32) .executeTakeFirstOrThrow(); From 625b9a3ae53bb0844dbc676d22b4482706d093dc Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 29 May 2024 16:16:17 -0500 Subject: [PATCH 8/9] ReadOnlySigner: improve error message --- src/signers/ReadOnlySigner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/signers/ReadOnlySigner.ts b/src/signers/ReadOnlySigner.ts index e7c45fb0..8ba15554 100644 --- a/src/signers/ReadOnlySigner.ts +++ b/src/signers/ReadOnlySigner.ts @@ -7,7 +7,7 @@ export class ReadOnlySigner implements NostrSigner { async signEvent(): Promise { throw new HTTPException(401, { - message: "Can't sign events with just an npub", + message: 'Log out and back in', }); } From 03c6f236051e189c2e7f250c06e219031ebb7218 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 29 May 2024 16:29:46 -0500 Subject: [PATCH 9/9] Use a specific version of nostr-tools --- deno.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deno.json b/deno.json index b925d200..d7b454f0 100644 --- a/deno.json +++ b/deno.json @@ -52,7 +52,7 @@ "linkifyjs": "npm:linkifyjs@^4.1.1", "lru-cache": "npm:lru-cache@^10.2.2", "nostr-relaypool": "npm:nostr-relaypool2@0.6.34", - "nostr-tools": "npm:nostr-tools@^2.5.1", + "nostr-tools": "npm:nostr-tools@2.5.1", "nostr-wasm": "npm:nostr-wasm@^0.1.0", "tldts": "npm:tldts@^6.0.14", "tseep": "npm:tseep@^1.2.1",