ditto/src/sign.ts

60 lines
2.0 KiB
TypeScript
Raw Normal View History

2023-04-30 02:48:22 +00:00
import { type AppContext } from '@/app.ts';
import { getEventHash, getPublicKey, getSignature, HTTPException } from '@/deps.ts';
2023-04-30 02:48:22 +00:00
import type { Event, EventTemplate, SignedEvent } from '@/event.ts';
2023-05-14 00:46:47 +00:00
/** Map of OAuth tokens to WebSocket signing streams. */
2023-05-14 01:16:44 +00:00
// FIXME: People can eavesdrop on other people's signing streams.
// TODO: Add a secret to the Authorization header.
2023-05-14 00:46:47 +00:00
export const signStreams = new Map<string, WebSocket>();
/** Get signing WebSocket from app context. */
function getSignStream(c: AppContext): WebSocket | undefined {
const token = c.req.headers.get('authorization')?.replace(/^Bearer /, '');
return token ? signStreams.get(token) : undefined;
}
2023-05-14 01:16:44 +00:00
/**
* Sign Nostr event using the app context.
*
* - If a secret key is provided, it will be used to sign the event.
* - If a signing WebSocket is provided, it will be used to sign the event.
*/
2023-04-30 02:48:22 +00:00
async function signEvent<K extends number = number>(event: EventTemplate<K>, c: AppContext): Promise<SignedEvent<K>> {
const seckey = c.get('seckey');
2023-05-14 00:46:47 +00:00
const stream = getSignStream(c);
2023-05-14 01:16:44 +00:00
if (!seckey && stream) {
2023-05-14 00:46:47 +00:00
try {
return await new Promise<SignedEvent<K>>((resolve, reject) => {
stream.addEventListener('message', (e) => {
const data = JSON.parse(e.data);
if (data.event === 'nostr.sign') {
resolve(JSON.parse(data.payload));
}
});
stream.send(JSON.stringify({ event: 'nostr.sign', payload: JSON.stringify(event) }));
setTimeout(reject, 60000);
2023-05-14 00:46:47 +00:00
});
} catch (_e) {
throw new HTTPException(504, {
res: c.json({ id: 'ditto.timeout', error: 'Signing timeout' }, 504),
});
}
}
if (!seckey) {
throw new HTTPException(400, {
res: c.json({ id: 'ditto.private_key', error: 'No private key' }, 400),
});
}
2023-04-30 02:48:22 +00:00
(event as Event<K>).pubkey = getPublicKey(seckey);
(event as Event<K>).id = getEventHash(event as Event<K>);
(event as Event<K>).sig = getSignature(event as Event<K>, seckey);
return event as SignedEvent<K>;
}
export { signEvent };