mirror of
https://github.com/aljazceru/ditto.git
synced 2025-12-26 01:34:26 +01:00
70 lines
2.0 KiB
TypeScript
70 lines
2.0 KiB
TypeScript
import { type AppMiddleware } from '@/app.ts';
|
|
import { Conf } from '@/config.ts';
|
|
import { type Event, HTTPException } from '@/deps.ts';
|
|
import { decode64Schema, jsonSchema } from '@/schema.ts';
|
|
import { signedEventSchema } from '@/schemas/nostr.ts';
|
|
import { eventAge, findTag, sha256, Time } from '@/utils.ts';
|
|
|
|
const decodeEventSchema = decode64Schema.pipe(jsonSchema).pipe(signedEventSchema);
|
|
|
|
interface Auth98Opts {
|
|
timeout?: number;
|
|
}
|
|
|
|
/**
|
|
* NIP-98 auth.
|
|
* https://github.com/nostr-protocol/nips/blob/master/98.md
|
|
*/
|
|
function auth98(opts: Auth98Opts = {}): AppMiddleware {
|
|
return async (c, next) => {
|
|
const authHeader = c.req.headers.get('authorization');
|
|
const base64 = authHeader?.match(/^Nostr (.+)$/)?.[1];
|
|
const { timeout = Time.minutes(1) } = opts;
|
|
|
|
const schema = decodeEventSchema
|
|
.refine((event) => event.kind === 27235)
|
|
.refine((event) => eventAge(event) < timeout)
|
|
.refine((event) => findTag(event.tags, 'method')?.[1] === c.req.method)
|
|
.refine((event) => {
|
|
const url = findTag(event.tags, 'u')?.[1];
|
|
try {
|
|
return url === Conf.local(c.req.url);
|
|
} catch (_e) {
|
|
return false;
|
|
}
|
|
})
|
|
.refine(async (event) => {
|
|
const body = await c.req.raw.clone().text();
|
|
if (!body) return true;
|
|
const hash = findTag(event.tags, 'payload')?.[1];
|
|
return hash === await sha256(body);
|
|
});
|
|
|
|
const result = await schema.safeParseAsync(base64);
|
|
|
|
if (result.success) {
|
|
c.set('pubkey', result.data.pubkey);
|
|
c.set('proof', result.data as Event<27235>);
|
|
}
|
|
|
|
await next();
|
|
};
|
|
}
|
|
|
|
const requireProof: AppMiddleware = async (c, next) => {
|
|
const pubkey = c.get('pubkey');
|
|
const proof = c.get('proof');
|
|
|
|
// if (!proof && hasWebsocket(c.req)) {
|
|
// // TODO: attempt to sign nip98 event through websocket
|
|
// }
|
|
|
|
if (!pubkey || !proof || proof.pubkey !== pubkey) {
|
|
throw new HTTPException(401);
|
|
}
|
|
|
|
await next();
|
|
};
|
|
|
|
export { auth98, requireProof };
|