NIP-42
Authentication of clients to relays
draft optional relay
This NIP defines a way for clients to authenticate to relays by signing an ephemeral event.
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: 4DMs 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 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>]
Clients MAY provide signed events from multiple pubkeys in a
sequence of AUTH messages. Relays MUST treat all
pubkeys as authenticated accordingly.
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"]
],
// other fields...
}
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 performedAUTHand the relay requires that to fulfill the query or write the event."restricted: "- for when a client has already performedAUTHbut 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...", ...}]
client: ["AUTH", {"id": "abcde2...", ...}]
relay: ["OK", "abcdef...", true, ""]
relay: ["OK", "abcde2...", 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 hint in
EOSE
Relays MAY use the "auth" hint in an
EOSE message (as defined in NIP-67) to notify the client that more
results may be available if they authenticate. In this case, the
relay MUST send the AUTH challenge before the
EOSE 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
kindis22242; - that the event
created_atis 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.