sign: ensure the NIP-46 result matches the template
This commit is contained in:
parent
007565b513
commit
c8a5da086e
18
src/sign.ts
18
src/sign.ts
|
@ -5,14 +5,14 @@ import { type Event, type EventTemplate, finishEvent, HTTPException } from '@/de
|
||||||
import { connectResponseSchema } from '@/schemas/nostr.ts';
|
import { connectResponseSchema } from '@/schemas/nostr.ts';
|
||||||
import { jsonSchema } from '@/schema.ts';
|
import { jsonSchema } from '@/schema.ts';
|
||||||
import { Sub } from '@/subs.ts';
|
import { Sub } from '@/subs.ts';
|
||||||
import { Time } from '@/utils.ts';
|
import { eventMatchesTemplate, Time } from '@/utils.ts';
|
||||||
import { createAdminEvent } from '@/utils/web.ts';
|
import { createAdminEvent } from '@/utils/web.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign Nostr event using the app context.
|
* Sign Nostr event using the app context.
|
||||||
*
|
*
|
||||||
* - If a secret key is provided, it will be used to sign the event.
|
* - If a secret key is provided, it will be used to sign the event.
|
||||||
* - If `X-Nostr-Sign` is passed, it will use a NIP-46 to sign the event.
|
* - If `X-Nostr-Sign` is passed, it will use NIP-46 to sign the event.
|
||||||
*/
|
*/
|
||||||
async function signEvent<K extends number = number>(event: EventTemplate<K>, c: AppContext): Promise<Event<K>> {
|
async function signEvent<K extends number = number>(event: EventTemplate<K>, c: AppContext): Promise<Event<K>> {
|
||||||
const seckey = c.get('seckey');
|
const seckey = c.get('seckey');
|
||||||
|
@ -54,13 +54,14 @@ async function signNostrConnect<K extends number = number>(event: EventTemplate<
|
||||||
tags: [['p', pubkey]],
|
tags: [['p', pubkey]],
|
||||||
}, c);
|
}, c);
|
||||||
|
|
||||||
return awaitSignedEvent<K>(pubkey, messageId, c);
|
return awaitSignedEvent<K>(pubkey, messageId, event, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Wait for signed event to be sent through Nostr relay. */
|
/** Wait for signed event to be sent through Nostr relay. */
|
||||||
async function awaitSignedEvent<K extends number = number>(
|
async function awaitSignedEvent<K extends number = number>(
|
||||||
pubkey: string,
|
pubkey: string,
|
||||||
messageId: string,
|
messageId: string,
|
||||||
|
template: EventTemplate<K>,
|
||||||
c: AppContext,
|
c: AppContext,
|
||||||
): Promise<Event<K>> {
|
): Promise<Event<K>> {
|
||||||
const sub = Sub.sub(messageId, '1', [{ kinds: [24133], authors: [pubkey], '#p': [Conf.pubkey] }]);
|
const sub = Sub.sub(messageId, '1', [{ kinds: [24133], authors: [pubkey], '#p': [Conf.pubkey] }]);
|
||||||
|
@ -79,12 +80,17 @@ async function awaitSignedEvent<K extends number = number>(
|
||||||
for await (const event of sub) {
|
for await (const event of sub) {
|
||||||
if (event.kind === 24133) {
|
if (event.kind === 24133) {
|
||||||
const decrypted = await decryptAdmin(event.pubkey, event.content);
|
const decrypted = await decryptAdmin(event.pubkey, event.content);
|
||||||
const msg = jsonSchema.pipe(connectResponseSchema).parse(decrypted);
|
|
||||||
|
|
||||||
if (msg.id === messageId) {
|
const result = jsonSchema
|
||||||
|
.pipe(connectResponseSchema)
|
||||||
|
.refine((msg) => msg.id === messageId)
|
||||||
|
.refine((msg) => eventMatchesTemplate(msg.result, template))
|
||||||
|
.safeParse(decrypted);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
close();
|
close();
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
return msg.result as Event<K>;
|
return result.data.result as Event<K>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { type Event, nip19, z } from '@/deps.ts';
|
import { type Event, type EventTemplate, getEventHash, nip19, z } from '@/deps.ts';
|
||||||
import { getAuthor } from '@/queries.ts';
|
import { getAuthor } from '@/queries.ts';
|
||||||
import { lookupNip05Cached } from '@/utils/nip05.ts';
|
import { lookupNip05Cached } from '@/utils/nip05.ts';
|
||||||
|
|
||||||
|
@ -106,11 +106,17 @@ function dedupeEvents<K extends number>(events: Event<K>[]): Event<K>[] {
|
||||||
return [...new Map(events.map((event) => [event.id, event])).values()];
|
return [...new Map(events.map((event) => [event.id, event])).values()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Ensure the template and event match on their shared keys. */
|
||||||
|
function eventMatchesTemplate(event: Event, template: EventTemplate): boolean {
|
||||||
|
return getEventHash(event) === getEventHash({ pubkey: event.pubkey, ...template });
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
bech32ToPubkey,
|
bech32ToPubkey,
|
||||||
dedupeEvents,
|
dedupeEvents,
|
||||||
eventAge,
|
eventAge,
|
||||||
eventDateComparator,
|
eventDateComparator,
|
||||||
|
eventMatchesTemplate,
|
||||||
findTag,
|
findTag,
|
||||||
isFollowing,
|
isFollowing,
|
||||||
isRelay,
|
isRelay,
|
||||||
|
|
Loading…
Reference in New Issue