nips/42.md
2024-02-26 09:26:37 -08:00

5.0 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.

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 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 of kind: 22242 and it should have at least two tags, one for the relay URL, and one containing evidence of access. This may be one of:

  • challenge - the challenge string recieved from the relay.
  • claim - an arbitrary token exchanged out-of-band. Relays SHOULD store this authorization so that the challenge method can be used in the future.

Clients MUST NOT publish these events. Relays MUST exclude kind: 22242 events from being broadcasted to any client. Relays MUST validate that created_at is the current time, adjusting for clock skew.

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 performed AUTH and the relay requires that to fulfill the query or write the event.
  • "restricted: " - for when a client has already performed AUTH 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 is 22242;
  • 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.