NAME
Net::Nostr::HttpAuth - NIP-98 HTTP auth
SYNOPSIS
use Net::Nostr::HttpAuth qw(
create_auth_event create_auth_header
parse_auth_header validate_auth_event
);
# Client: create an Authorization header for a GET request
my $header = create_auth_header(
key => $key,
url => 'https://api.example.com/data',
method => 'GET',
);
# "Nostr eyJpZCI6Ii..."
# Client: POST with payload hash
my $body = '{"name":"test"}';
my $header = create_auth_header(
key => $key,
url => 'https://api.example.com/upload',
method => 'POST',
payload => $body,
);
# Server: parse and validate
my $event = parse_auth_header($header);
validate_auth_event($event,
url => 'https://api.example.com/data',
method => 'GET',
);
DESCRIPTION
Implements NIP-98 HTTP auth. Uses kind 27235 ephemeral nostr events to authorize HTTP requests. The client signs an event containing the request URL and method, base64-encodes it, and sends it in the Authorization HTTP header with the Nostr scheme. The server decodes the event and validates the kind, timestamp, URL, and method.
When the request has a body (POST, PUT, PATCH), the client SHOULD include a payload tag containing the SHA-256 hex hash of the body. The server MAY check this tag to verify the body is authorized.
FUNCTIONS
All functions are exportable. None are exported by default.
create_auth_event
my $event = create_auth_event(
pubkey => $hex_pubkey,
url => $absolute_url,
method => $http_method,
payload => $body, # optional
created_at => time(), # optional, defaults to now
);
Creates a kind 27235 Net::Nostr::Event with u and method tags. If payload is provided, adds a payload tag with the SHA-256 hex hash of the body. The event content is always empty. Croaks if pubkey, url, or method is missing. Croaks if pubkey is not 64-character lowercase hex.
The returned event is unsigned. Use "sign_event" in Net::Nostr::Key to sign it before encoding.
my $event = create_auth_event(
pubkey => 'aa' x 32,
url => 'https://api.example.com/data',
method => 'GET',
);
create_auth_header
my $header = create_auth_header(
key => $nostr_key,
url => $absolute_url,
method => $http_method,
payload => $body, # optional
created_at => time(), # optional, defaults to now
);
Creates, signs, and base64-encodes a kind 27235 event, returning a complete Authorization header value in the format Nostr <base64>. The key must be a Net::Nostr::Key object with a private key for signing. Croaks if key, url, or method is missing.
my $header = create_auth_header(
key => $key,
url => 'https://api.example.com/data',
method => 'GET',
);
# "Nostr eyJpZCI6Ii..."
parse_auth_header
my $event = parse_auth_header($header_value);
Parses an Authorization header value. Validates the Nostr scheme, decodes the base64 payload, and parses the JSON into a Net::Nostr::Event using "from_wire" in Net::Nostr::Event. Croaks if the header is missing, uses the wrong scheme, or contains invalid base64 or JSON.
my $event = parse_auth_header('Nostr eyJpZCI6Ii...');
validate_auth_event
validate_auth_event($event,
url => $absolute_url,
method => $http_method,
payload => $body, # optional
time_window => 60, # optional, seconds, default 60
);
Performs the server-side validation checks specified by NIP-98:
- 0. The event ID and Schnorr signature are cryptographically verified via "validate" in Net::Nostr::Event. This ensures the event was actually signed by the claimed pubkey and has not been tampered with.
- 1. The
kindMUST be27235. - 2. The
created_attimestamp MUST be withintime_windowseconds of the current time (default 60 seconds). - 3. The
utag MUST exactly match the request URL (including query parameters). - 4. The
methodtag MUST match the HTTP method used.
If payload is provided and the event has a payload tag, the tag value is checked against the SHA-256 hex hash of the body. If the server does not provide payload, the tag is not checked.
Returns 1 on success. Croaks with a descriptive message on failure. Servers SHOULD respond with HTTP 401 when validation fails.
eval { validate_auth_event($event, url => $url, method => 'GET') };
if ($@) {
# respond with 401 Unauthorized
}