diff --git a/src/middleware/auth98.ts b/src/middleware/auth98.ts index 4ef5461..7e7f93a 100644 --- a/src/middleware/auth98.ts +++ b/src/middleware/auth98.ts @@ -1,7 +1,8 @@ import { type AppMiddleware } from '@/app.ts'; import { HTTPException } from '@/deps.ts'; -import { parseAuthRequest, type ParseAuthRequestOpts } from '@/utils/nip98.ts'; +import { buildAuthEventTemplate, parseAuthRequest, type ParseAuthRequestOpts } from '@/utils/nip98.ts'; import { localRequest } from '@/utils/web.ts'; +import { signNostrConnect } from '@/sign.ts'; /** * NIP-98 auth. @@ -22,12 +23,16 @@ function auth98(opts: ParseAuthRequestOpts = {}): AppMiddleware { } const requireProof: AppMiddleware = async (c, next) => { + const header = c.req.headers.get('x-nostr-sign'); const pubkey = c.get('pubkey'); - const proof = c.get('proof'); + const proof = c.get('proof') || header ? await obtainProof() : undefined; - // if (!proof && hasWebsocket(c.req)) { - // // TODO: attempt to sign nip98 event through websocket - // } + /** Get the proof over Nostr Connect. */ + async function obtainProof() { + const req = localRequest(c); + const event = await buildAuthEventTemplate(req); + return signNostrConnect(event, c); + } if (!pubkey || !proof || proof.pubkey !== pubkey) { throw new HTTPException(401); diff --git a/src/sign.ts b/src/sign.ts index cdce150..f4f8d2e 100644 --- a/src/sign.ts +++ b/src/sign.ts @@ -101,4 +101,4 @@ async function signAdminEvent(event: EventTemplate return finishEvent(event, Conf.seckey); } -export { signAdminEvent, signEvent }; +export { signAdminEvent, signEvent, signNostrConnect }; diff --git a/src/utils/nip98.ts b/src/utils/nip98.ts index 123a6ef..e1cae5d 100644 --- a/src/utils/nip98.ts +++ b/src/utils/nip98.ts @@ -1,7 +1,7 @@ -import { type Event } from '@/deps.ts'; +import { type Event, type EventTemplate } from '@/deps.ts'; import { decode64Schema, jsonSchema } from '@/schema.ts'; import { signedEventSchema } from '@/schemas/nostr.ts'; -import { eventAge, findTag, sha256 } from '@/utils.ts'; +import { eventAge, findTag, nostrNow, sha256 } from '@/utils.ts'; import { Time } from '@/utils/time.ts'; /** Decode a Nostr event from a base64 encoded string. */ @@ -38,9 +38,26 @@ function parseAuthRequest(req: Request, opts: ParseAuthRequestOpts = {}) { return schema.safeParseAsync(base64); } +/** Create an auth EventTemplate from a Request. */ +async function buildAuthEventTemplate(req: Request): Promise> { + const { method, url } = req; + const payload = await req.clone().text().then(sha256); + + return { + kind: 27235, + content: '', + tags: [ + ['method', method], + ['u', url], + ['payload', payload], + ], + created_at: nostrNow(), + }; +} + /** Get the value for the first matching tag name in the event. */ function tagValue(event: Event, tagName: string): string | undefined { return findTag(event.tags, tagName)?.[1]; } -export { parseAuthRequest, type ParseAuthRequestOpts }; +export { buildAuthEventTemplate, parseAuthRequest, type ParseAuthRequestOpts };