nostr-tools
Advanced tools
Comparing version 2.1.3 to 2.1.4
@@ -141,3 +141,3 @@ "use strict"; | ||
let [name, domain] = lud16.split("@"); | ||
lnurl = `https://${domain}/.well-known/lnurlp/${name}`; | ||
lnurl = new URL(`/.well-known/lnurlp/${name}`, `https://${domain}`).toString(); | ||
} else { | ||
@@ -144,0 +144,0 @@ return null; |
@@ -27,9 +27,17 @@ "use strict"; | ||
validateEvent: () => validateEvent2, | ||
validateEventKind: () => validateEventKind, | ||
validateEventMethodTag: () => validateEventMethodTag, | ||
validateEventPayloadTag: () => validateEventPayloadTag, | ||
validateEventTimestamp: () => validateEventTimestamp, | ||
validateEventUrlTag: () => validateEventUrlTag, | ||
validateToken: () => validateToken | ||
}); | ||
module.exports = __toCommonJS(nip98_exports); | ||
var import_sha2562 = require("@noble/hashes/sha256"); | ||
var import_utils3 = require("@noble/hashes/utils"); | ||
var import_sha2562 = require("@noble/hashes/sha256"); | ||
var import_base = require("@scure/base"); | ||
// kinds.ts | ||
var HTTPAuth = 27235; | ||
// pure.ts | ||
@@ -125,11 +133,4 @@ var import_secp256k1 = require("@noble/curves/secp256k1"); | ||
// kinds.ts | ||
var HTTPAuth = 27235; | ||
// nip98.ts | ||
var _authorizationScheme = "Nostr "; | ||
function hashPayload(payload) { | ||
const hash = (0, import_sha2562.sha256)(utf8Encoder.encode(JSON.stringify(payload))); | ||
return (0, import_utils3.bytesToHex)(hash); | ||
} | ||
async function getToken(loginUrl, httpMethod, sign, includeAuthorizationScheme = false, payload) { | ||
@@ -146,3 +147,3 @@ const event = { | ||
if (payload) { | ||
event.tags.push(["payload", (0, import_utils3.bytesToHex)((0, import_sha2562.sha256)(utf8Encoder.encode(JSON.stringify(payload))))]); | ||
event.tags.push(["payload", hashPayload(payload)]); | ||
} | ||
@@ -174,31 +175,56 @@ const signedEvent = await sign(event); | ||
} | ||
function validateEventTimestamp(event) { | ||
if (!event.created_at) { | ||
return false; | ||
} | ||
return Math.round(new Date().getTime() / 1e3) - event.created_at < 60; | ||
} | ||
function validateEventKind(event) { | ||
return event.kind === HTTPAuth; | ||
} | ||
function validateEventUrlTag(event, url) { | ||
const urlTag = event.tags.find((t) => t[0] === "u"); | ||
if (!urlTag) { | ||
return false; | ||
} | ||
return urlTag.length > 0 && urlTag[1] === url; | ||
} | ||
function validateEventMethodTag(event, method) { | ||
const methodTag = event.tags.find((t) => t[0] === "method"); | ||
if (!methodTag) { | ||
return false; | ||
} | ||
return methodTag.length > 0 && methodTag[1].toLowerCase() === method.toLowerCase(); | ||
} | ||
function hashPayload(payload) { | ||
const hash = (0, import_sha2562.sha256)(utf8Encoder.encode(JSON.stringify(payload))); | ||
return (0, import_utils3.bytesToHex)(hash); | ||
} | ||
function validateEventPayloadTag(event, payload) { | ||
const payloadTag = event.tags.find((t) => t[0] === "payload"); | ||
if (!payloadTag) { | ||
return false; | ||
} | ||
const payloadHash = hashPayload(payload); | ||
return payloadTag.length > 0 && payloadTag[1] === payloadHash; | ||
} | ||
async function validateEvent2(event, url, method, body) { | ||
if (!event) { | ||
throw new Error("Invalid nostr event"); | ||
} | ||
if (!verifyEvent(event)) { | ||
throw new Error("Invalid nostr event, signature invalid"); | ||
} | ||
if (event.kind !== HTTPAuth) { | ||
if (!validateEventKind(event)) { | ||
throw new Error("Invalid nostr event, kind invalid"); | ||
} | ||
if (!event.created_at) { | ||
throw new Error("Invalid nostr event, created_at invalid"); | ||
if (!validateEventTimestamp(event)) { | ||
throw new Error("Invalid nostr event, created_at timestamp invalid"); | ||
} | ||
if (Math.round(new Date().getTime() / 1e3) - event.created_at > 60) { | ||
throw new Error("Invalid nostr event, expired"); | ||
} | ||
const urlTag = event.tags.find((t) => t[0] === "u"); | ||
if (urlTag?.length !== 1 && urlTag?.[1] !== url) { | ||
if (!validateEventUrlTag(event, url)) { | ||
throw new Error("Invalid nostr event, url tag invalid"); | ||
} | ||
const methodTag = event.tags.find((t) => t[0] === "method"); | ||
if (methodTag?.length !== 1 && methodTag?.[1].toLowerCase() !== method.toLowerCase()) { | ||
if (!validateEventMethodTag(event, method)) { | ||
throw new Error("Invalid nostr event, method tag invalid"); | ||
} | ||
if (Boolean(body) && Object.keys(body).length > 0) { | ||
const payloadTag = event.tags.find((t) => t[0] === "payload"); | ||
const payloadHash = (0, import_utils3.bytesToHex)((0, import_sha2562.sha256)(utf8Encoder.encode(JSON.stringify(body)))); | ||
if (payloadTag?.[1] !== payloadHash) { | ||
throw new Error("Invalid payload tag hash, does not match request body hash"); | ||
if (Boolean(body) && typeof body === "object" && Object.keys(body).length > 0) { | ||
if (!validateEventPayloadTag(event, body)) { | ||
throw new Error("Invalid nostr event, payload tag does not match request body hash"); | ||
} | ||
@@ -205,0 +231,0 @@ } |
@@ -1980,3 +1980,3 @@ var __defProp = Object.defineProperty; | ||
let [name, domain] = lud16.split("@"); | ||
lnurl = `https://${domain}/.well-known/lnurlp/${name}`; | ||
lnurl = new URL(`/.well-known/lnurlp/${name}`, `https://${domain}`).toString(); | ||
} else { | ||
@@ -2071,12 +2071,13 @@ return null; | ||
validateEvent: () => validateEvent2, | ||
validateEventKind: () => validateEventKind, | ||
validateEventMethodTag: () => validateEventMethodTag, | ||
validateEventPayloadTag: () => validateEventPayloadTag, | ||
validateEventTimestamp: () => validateEventTimestamp, | ||
validateEventUrlTag: () => validateEventUrlTag, | ||
validateToken: () => validateToken | ||
}); | ||
import { sha256 as sha2563 } from "@noble/hashes/sha256"; | ||
import { bytesToHex as bytesToHex4 } from "@noble/hashes/utils"; | ||
import { sha256 as sha2563 } from "@noble/hashes/sha256"; | ||
import { base64 as base643 } from "@scure/base"; | ||
var _authorizationScheme = "Nostr "; | ||
function hashPayload(payload) { | ||
const hash = sha2563(utf8Encoder.encode(JSON.stringify(payload))); | ||
return bytesToHex4(hash); | ||
} | ||
async function getToken(loginUrl, httpMethod, sign, includeAuthorizationScheme = false, payload) { | ||
@@ -2093,3 +2094,3 @@ const event = { | ||
if (payload) { | ||
event.tags.push(["payload", bytesToHex4(sha2563(utf8Encoder.encode(JSON.stringify(payload))))]); | ||
event.tags.push(["payload", hashPayload(payload)]); | ||
} | ||
@@ -2121,31 +2122,56 @@ const signedEvent = await sign(event); | ||
} | ||
function validateEventTimestamp(event) { | ||
if (!event.created_at) { | ||
return false; | ||
} | ||
return Math.round(new Date().getTime() / 1e3) - event.created_at < 60; | ||
} | ||
function validateEventKind(event) { | ||
return event.kind === HTTPAuth; | ||
} | ||
function validateEventUrlTag(event, url) { | ||
const urlTag = event.tags.find((t) => t[0] === "u"); | ||
if (!urlTag) { | ||
return false; | ||
} | ||
return urlTag.length > 0 && urlTag[1] === url; | ||
} | ||
function validateEventMethodTag(event, method) { | ||
const methodTag = event.tags.find((t) => t[0] === "method"); | ||
if (!methodTag) { | ||
return false; | ||
} | ||
return methodTag.length > 0 && methodTag[1].toLowerCase() === method.toLowerCase(); | ||
} | ||
function hashPayload(payload) { | ||
const hash = sha2563(utf8Encoder.encode(JSON.stringify(payload))); | ||
return bytesToHex4(hash); | ||
} | ||
function validateEventPayloadTag(event, payload) { | ||
const payloadTag = event.tags.find((t) => t[0] === "payload"); | ||
if (!payloadTag) { | ||
return false; | ||
} | ||
const payloadHash = hashPayload(payload); | ||
return payloadTag.length > 0 && payloadTag[1] === payloadHash; | ||
} | ||
async function validateEvent2(event, url, method, body) { | ||
if (!event) { | ||
throw new Error("Invalid nostr event"); | ||
} | ||
if (!verifyEvent(event)) { | ||
throw new Error("Invalid nostr event, signature invalid"); | ||
} | ||
if (event.kind !== HTTPAuth) { | ||
if (!validateEventKind(event)) { | ||
throw new Error("Invalid nostr event, kind invalid"); | ||
} | ||
if (!event.created_at) { | ||
throw new Error("Invalid nostr event, created_at invalid"); | ||
if (!validateEventTimestamp(event)) { | ||
throw new Error("Invalid nostr event, created_at timestamp invalid"); | ||
} | ||
if (Math.round(new Date().getTime() / 1e3) - event.created_at > 60) { | ||
throw new Error("Invalid nostr event, expired"); | ||
} | ||
const urlTag = event.tags.find((t) => t[0] === "u"); | ||
if (urlTag?.length !== 1 && urlTag?.[1] !== url) { | ||
if (!validateEventUrlTag(event, url)) { | ||
throw new Error("Invalid nostr event, url tag invalid"); | ||
} | ||
const methodTag = event.tags.find((t) => t[0] === "method"); | ||
if (methodTag?.length !== 1 && methodTag?.[1].toLowerCase() !== method.toLowerCase()) { | ||
if (!validateEventMethodTag(event, method)) { | ||
throw new Error("Invalid nostr event, method tag invalid"); | ||
} | ||
if (Boolean(body) && Object.keys(body).length > 0) { | ||
const payloadTag = event.tags.find((t) => t[0] === "payload"); | ||
const payloadHash = bytesToHex4(sha2563(utf8Encoder.encode(JSON.stringify(body)))); | ||
if (payloadTag?.[1] !== payloadHash) { | ||
throw new Error("Invalid payload tag hash, does not match request body hash"); | ||
if (Boolean(body) && typeof body === "object" && Object.keys(body).length > 0) { | ||
if (!validateEventPayloadTag(event, body)) { | ||
throw new Error("Invalid nostr event, payload tag does not match request body hash"); | ||
} | ||
@@ -2152,0 +2178,0 @@ } |
@@ -113,3 +113,3 @@ // nip57.ts | ||
let [name, domain] = lud16.split("@"); | ||
lnurl = `https://${domain}/.well-known/lnurlp/${name}`; | ||
lnurl = new URL(`/.well-known/lnurlp/${name}`, `https://${domain}`).toString(); | ||
} else { | ||
@@ -116,0 +116,0 @@ return null; |
// nip98.ts | ||
import { sha256 as sha2562 } from "@noble/hashes/sha256"; | ||
import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils"; | ||
import { sha256 as sha2562 } from "@noble/hashes/sha256"; | ||
import { base64 } from "@scure/base"; | ||
// kinds.ts | ||
var HTTPAuth = 27235; | ||
// pure.ts | ||
@@ -96,11 +99,4 @@ import { schnorr } from "@noble/curves/secp256k1"; | ||
// kinds.ts | ||
var HTTPAuth = 27235; | ||
// nip98.ts | ||
var _authorizationScheme = "Nostr "; | ||
function hashPayload(payload) { | ||
const hash = sha2562(utf8Encoder.encode(JSON.stringify(payload))); | ||
return bytesToHex2(hash); | ||
} | ||
async function getToken(loginUrl, httpMethod, sign, includeAuthorizationScheme = false, payload) { | ||
@@ -117,3 +113,3 @@ const event = { | ||
if (payload) { | ||
event.tags.push(["payload", bytesToHex2(sha2562(utf8Encoder.encode(JSON.stringify(payload))))]); | ||
event.tags.push(["payload", hashPayload(payload)]); | ||
} | ||
@@ -145,31 +141,56 @@ const signedEvent = await sign(event); | ||
} | ||
function validateEventTimestamp(event) { | ||
if (!event.created_at) { | ||
return false; | ||
} | ||
return Math.round(new Date().getTime() / 1e3) - event.created_at < 60; | ||
} | ||
function validateEventKind(event) { | ||
return event.kind === HTTPAuth; | ||
} | ||
function validateEventUrlTag(event, url) { | ||
const urlTag = event.tags.find((t) => t[0] === "u"); | ||
if (!urlTag) { | ||
return false; | ||
} | ||
return urlTag.length > 0 && urlTag[1] === url; | ||
} | ||
function validateEventMethodTag(event, method) { | ||
const methodTag = event.tags.find((t) => t[0] === "method"); | ||
if (!methodTag) { | ||
return false; | ||
} | ||
return methodTag.length > 0 && methodTag[1].toLowerCase() === method.toLowerCase(); | ||
} | ||
function hashPayload(payload) { | ||
const hash = sha2562(utf8Encoder.encode(JSON.stringify(payload))); | ||
return bytesToHex2(hash); | ||
} | ||
function validateEventPayloadTag(event, payload) { | ||
const payloadTag = event.tags.find((t) => t[0] === "payload"); | ||
if (!payloadTag) { | ||
return false; | ||
} | ||
const payloadHash = hashPayload(payload); | ||
return payloadTag.length > 0 && payloadTag[1] === payloadHash; | ||
} | ||
async function validateEvent2(event, url, method, body) { | ||
if (!event) { | ||
throw new Error("Invalid nostr event"); | ||
} | ||
if (!verifyEvent(event)) { | ||
throw new Error("Invalid nostr event, signature invalid"); | ||
} | ||
if (event.kind !== HTTPAuth) { | ||
if (!validateEventKind(event)) { | ||
throw new Error("Invalid nostr event, kind invalid"); | ||
} | ||
if (!event.created_at) { | ||
throw new Error("Invalid nostr event, created_at invalid"); | ||
if (!validateEventTimestamp(event)) { | ||
throw new Error("Invalid nostr event, created_at timestamp invalid"); | ||
} | ||
if (Math.round(new Date().getTime() / 1e3) - event.created_at > 60) { | ||
throw new Error("Invalid nostr event, expired"); | ||
} | ||
const urlTag = event.tags.find((t) => t[0] === "u"); | ||
if (urlTag?.length !== 1 && urlTag?.[1] !== url) { | ||
if (!validateEventUrlTag(event, url)) { | ||
throw new Error("Invalid nostr event, url tag invalid"); | ||
} | ||
const methodTag = event.tags.find((t) => t[0] === "method"); | ||
if (methodTag?.length !== 1 && methodTag?.[1].toLowerCase() !== method.toLowerCase()) { | ||
if (!validateEventMethodTag(event, method)) { | ||
throw new Error("Invalid nostr event, method tag invalid"); | ||
} | ||
if (Boolean(body) && Object.keys(body).length > 0) { | ||
const payloadTag = event.tags.find((t) => t[0] === "payload"); | ||
const payloadHash = bytesToHex2(sha2562(utf8Encoder.encode(JSON.stringify(body)))); | ||
if (payloadTag?.[1] !== payloadHash) { | ||
throw new Error("Invalid payload tag hash, does not match request body hash"); | ||
if (Boolean(body) && typeof body === "object" && Object.keys(body).length > 0) { | ||
if (!validateEventPayloadTag(event, body)) { | ||
throw new Error("Invalid nostr event, payload tag does not match request body hash"); | ||
} | ||
@@ -184,3 +205,8 @@ } | ||
validateEvent2 as validateEvent, | ||
validateEventKind, | ||
validateEventMethodTag, | ||
validateEventPayloadTag, | ||
validateEventTimestamp, | ||
validateEventUrlTag, | ||
validateToken | ||
}; |
@@ -1,3 +0,1 @@ | ||
/// <reference types="web" /> | ||
/// <reference types="web" /> | ||
/// <reference types="bun-types" /> | ||
@@ -4,0 +2,0 @@ import type { Event, EventTemplate, VerifiedEvent, Nostr } from './core.ts'; |
import { Event, EventTemplate } from './pure.ts'; | ||
export declare function hashPayload(payload: any): string; | ||
/** | ||
@@ -18,3 +17,59 @@ * Generate token for NIP-98 flow. | ||
export declare function validateToken(token: string, url: string, method: string): Promise<boolean>; | ||
/** | ||
* Unpacks an event from a token. | ||
* | ||
* @param token - The token to unpack. | ||
* @returns A promise that resolves to the unpacked event. | ||
* @throws {Error} If the token is missing, invalid, or cannot be parsed. | ||
*/ | ||
export declare function unpackEventFromToken(token: string): Promise<Event>; | ||
/** | ||
* Validates the timestamp of an event. | ||
* @param event - The event object to validate. | ||
* @returns A boolean indicating whether the event timestamp is within the last 60 seconds. | ||
*/ | ||
export declare function validateEventTimestamp(event: Event): boolean; | ||
/** | ||
* Validates the kind of an event. | ||
* @param event The event to validate. | ||
* @returns A boolean indicating whether the event kind is valid. | ||
*/ | ||
export declare function validateEventKind(event: Event): boolean; | ||
/** | ||
* Validates if the given URL matches the URL tag of the event. | ||
* @param event - The event object. | ||
* @param url - The URL to validate. | ||
* @returns A boolean indicating whether the URL is valid or not. | ||
*/ | ||
export declare function validateEventUrlTag(event: Event, url: string): boolean; | ||
/** | ||
* Validates if the given event has a method tag that matches the specified method. | ||
* @param event - The event to validate. | ||
* @param method - The method to match against the method tag. | ||
* @returns A boolean indicating whether the event has a matching method tag. | ||
*/ | ||
export declare function validateEventMethodTag(event: Event, method: string): boolean; | ||
/** | ||
* Calculates the hash of a payload. | ||
* @param payload - The payload to be hashed. | ||
* @returns The hash value as a string. | ||
*/ | ||
export declare function hashPayload(payload: any): string; | ||
/** | ||
* Validates the event payload tag against the provided payload. | ||
* @param event The event object. | ||
* @param payload The payload to validate. | ||
* @returns A boolean indicating whether the payload tag is valid. | ||
*/ | ||
export declare function validateEventPayloadTag(event: Event, payload: any): boolean; | ||
/** | ||
* Validates a Nostr event for the NIP-98 flow. | ||
* | ||
* @param event - The Nostr event to validate. | ||
* @param url - The URL associated with the event. | ||
* @param method - The HTTP method associated with the event. | ||
* @param body - The request body associated with the event (optional). | ||
* @returns A promise that resolves to a boolean indicating whether the event is valid. | ||
* @throws An error if the event is invalid. | ||
*/ | ||
export declare function validateEvent(event: Event, url: string, method: string, body?: any): Promise<boolean>; |
@@ -1,3 +0,7 @@ | ||
import type { Event } from './pure.ts'; | ||
/** Build an event for testing purposes. */ | ||
import { type Event } from './pure.ts'; | ||
export declare function buildEvent(params: Partial<Event>): Event; | ||
export declare function newMockRelay(): { | ||
url: string; | ||
authors: string[]; | ||
ids: string[]; | ||
}; |
@@ -1,2 +0,2 @@ | ||
/// <reference types="web" /> | ||
/// <reference types="bun-types" /> | ||
import type { Event } from './core.ts'; | ||
@@ -3,0 +3,0 @@ export declare const utf8Decoder: TextDecoder; |
{ | ||
"type": "module", | ||
"name": "nostr-tools", | ||
"version": "2.1.3", | ||
"version": "2.1.4", | ||
"description": "Tools for making a Nostr client.", | ||
@@ -133,2 +133,7 @@ "repository": { | ||
}, | ||
"./nip29": { | ||
"import": "./lib/esm/nip29.js", | ||
"require": "./lib/cjs/nip29.js", | ||
"types": "./lib/types/nip29.d.ts" | ||
}, | ||
"./nip30": { | ||
@@ -211,2 +216,3 @@ "import": "./lib/esm/nip30.js", | ||
"mitata": "^0.1.6", | ||
"mock-socket": "^9.3.1", | ||
"node-fetch": "^2.6.9", | ||
@@ -213,0 +219,0 @@ "prettier": "^3.0.3", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2240102
214
21921
18