6.2 KiB
NIP-42
Authentication of clients to relays
draft
optional
This NIP defines a way for clients to authenticate to relays by signing an ephemeral event and a way to specify which events and private and MUST require authentication to be served.
Motivation
A relay may want to require clients to authenticate to access restricted resources. For example,
- A relay may request payment or other forms of whitelisting to publish events -- this can naïvely be achieved by limiting publication to events signed by the whitelisted key, but with this NIP they may choose to accept any events as long as they are published from an authenticated user;
- A relay may limit access to
kind: 4
DMs to only the parties involved in the chat exchange, and for that it may require authentication before clients can query for that kind. - A relay may limit subscriptions of any kind to paying users or users whitelisted through any other means, and require authentication.
Definitions
New field to identify private events
Clients that want to force an authentication in any event can add a requires_auth_by
with array of authorized pubkeys to the base event object.
Relays MUST authenticate connections with at least one of the pubkeys listed in requires_auth_by
to return events.
{
"id": <32-bytes lowercase hex-encoded sha256 of the serialized event data>,
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
"requires_auth_by" [<32-bytes lowercase hex-encoded public key>, ... ]
"created_at": <unix timestamp in seconds>,
"kind": <integer between 0 and 65535>,
"tags": [
[<arbitrary string>...],
// ...
],
"content": <arbitrary string>,
"sig": <64-bytes lowercase hex of the signature of the sha256 hash of the serialized event data, which is the same as the "id" field>
}
NIP-42 supporting relays must then include the requires_auth_by
field as the 7th position in the serialization of the event. This guarantees that relays and clients that do not support this NIP cannot validate private events and will never be able to receive them, avoiding leaking the event structure to a public relay.
[
0,
<pubkey, as a lowercase hex string>,
<created_at, as a number>,
<kind, as a number>,
<tags, as an array of arrays of non-null strings>,
<content, as a string>,
<requires_auth_by, as an array of non-null strings>
]
New client-relay protocol messages
This NIP defines a new message, AUTH
, which relays CAN send when they support authentication and clients can send to relays when they want to authenticate. When sent by relays the message has the following form:
["AUTH", <challenge-string>]
And, when sent by clients, the following form:
["AUTH", <signed-event-json>]
AUTH
messages sent by clients MUST be answered with an OK
message, like any EVENT
message.
Canonical authentication event
The signed event is an ephemeral event not meant to be published or queried, it must be of kind: 22242
and it should have at least two tags, one for the relay URL and one for the challenge string as received from the relay. Relays MUST exclude kind: 22242
events from being broadcasted to any client. created_at
should be the current time. Example:
{
"kind": 22242,
"tags": [
["relay", "wss://relay.example.com/"],
["challenge", "challengestringhere"]
],
...
}
OK
and CLOSED
machine-readable prefixes
This NIP defines two new prefixes that can be used in OK
(in response to event writes by clients) and CLOSED
(in response to rejected subscriptions by clients):
"auth-required: "
- for when a client has not performedAUTH
and the relay requires that to fulfill the query or write the event."restricted: "
- for when a client has already performedAUTH
but the key used to perform it is still not allowed by the relay or is exceeding its authorization.
Protocol flow
At any moment the relay may send an AUTH
message to the client containing a challenge. The challenge is valid for the duration of the connection or until another challenge is sent by the relay. The client MAY decide to send its AUTH
event at any point and the authenticated session is valid afterwards for the duration of the connection.
auth-required
in response to a REQ
message
Given that a relay is likely to require clients to perform authentication only for certain jobs, like answering a REQ
or accepting an EVENT
write, these are some expected common flows:
relay: ["AUTH", "<challenge>"]
client: ["REQ", "sub_1", {"kinds": [4]}]
relay: ["CLOSED", "sub_1", "auth-required: we can't serve DMs to unauthenticated users"]
client: ["AUTH", {"id": "abcdef...", ...}]
relay: ["OK", "abcdef...", true, ""]
client: ["REQ", "sub_1", {"kinds": [4]}]
relay: ["EVENT", "sub_1", {...}]
relay: ["EVENT", "sub_1", {...}]
relay: ["EVENT", "sub_1", {...}]
relay: ["EVENT", "sub_1", {...}]
...
In this case, the AUTH
message from the relay could be sent right as the client connects or it can be sent immediately before the CLOSED
is sent. The only requirement is that the client must have a stored challenge associated with that relay so it can act upon that in response to the auth-required
CLOSED
message.
auth-required
in response to an EVENT
message
The same flow is valid for when a client wants to write an EVENT
to the relay, except now the relay sends back an OK
message instead of a CLOSED
message:
relay: ["AUTH", "<challenge>"]
client: ["EVENT", {"id": "012345...", ...}]
relay: ["OK", "012345...", false, "auth-required: we only accept events from registered users"]
client: ["AUTH", {"id": "abcdef...", ...}]
relay: ["OK", "abcdef...", true, ""]
client: ["EVENT", {"id": "012345...", ...}]
relay: ["OK", "012345...", true, ""]
Signed Event Verification
To verify AUTH
messages, relays must ensure:
- that the
kind
is22242
; - that the event
created_at
is close (e.g. within ~10 minutes) of the current time; - that the
"challenge"
tag matches the challenge sent before; - that the
"relay"
tag matches the relay URL:- URL normalization techniques can be applied. For most cases just checking if the domain name is correct should be enough.