Chapter 42 - Relay Ops
42
Nostr Implementation Possibility - Hex № 42

Authentication of Clients to Relays

A client proves control of a keypair to a relay by signing an ephemeral challenge event. No passwords, no accounts, no extra round trips to a server.

1 Overview

Nostr identities are keypairs, not accounts. A relay that restricts a resource still needs proof that the connected client controls a specific key. This NIP defines that proof. The client signs an ephemeral event that contains a challenge string from the relay. The relay verifies the signature and marks the connection as authenticated for that pubkey.

A relay may want this for several jobs. It may accept paid or whitelisted users only, and accept any event those users publish. It may limit kind: 4 direct messages (NIP-04) so only the two chat parties can query them. It may limit subscriptions of any kind to paying or whitelisted users.

Authentication is per connection. It uses one new message type, one new event kind, and two machine-readable status prefixes. All of them are defined below.

Hint!

Nothing here creates a session token or a cookie. The proof lives and dies with the WebSocket connection. Reconnect, and the relay hands you a fresh challenge.

Client holds keypair Relay holds challenge ["AUTH", "<challenge>"] ["AUTH", signed kind 22242 event] Relay verifies the signature. The pubkey is authenticated for this connection.
Fig. 42-1 — The handshake. The relay issues a challenge. The client signs it into an event and sends it back.

2 The AUTH Message

This NIP adds one message type, AUTH, to the NIP-01 message set. Both sides can send it, with different payloads:

AUTH message forms
DirectionShapePurpose
relay → client["AUTH", <challenge-string>]Offer a challenge. Relays that support authentication can send this.
client → relay["AUTH", <signed-event-json>]Prove control of a key by returning the signed challenge event.

The relay may send its AUTH challenge at any moment. The challenge stays valid for the duration of the connection, or until the relay sends another challenge. The client may send its AUTH event at any point. The authenticated session then stays valid for the rest of the connection.

A client may authenticate more than one pubkey. It sends signed events from multiple pubkeys in a sequence of AUTH messages. The relay must treat all of those pubkeys as authenticated.

The relay must answer every client AUTH message with an OK message, exactly as it answers an EVENT message.

3 The Authentication Event

The proof is an ephemeral event of kind: 22242. It is not meant to be published or queried. It should carry at least two tags: one with the relay URL and one with the challenge string as received from the relay. The created_at field should be the current time.

Wire format - kind 22242 authentication event
{
  "kind": 22242,
  "tags": [
    ["relay", "wss://relay.example.com/"],
    ["challenge", "challengestringhere"]
  ],
  // other NIP-01 fields: id, pubkey, created_at, content, sig...
}
Tag reference
TagValueRule
relayrelay URLThe URL of the relay the client authenticates to.
challengechallenge stringThe exact string received from the relay in its AUTH message.
Warning!

Relays must exclude kind: 22242 events from broadcast to any client. The event proves key control to one relay only. Leaking it serves no one; kind 22242 sits in the ephemeral range, so relays do not store it either.

Verification rules

To verify a client AUTH message, the relay must check all of the following:

  • The kind is 22242.
  • The created_at is close to the current time. About 10 minutes of tolerance is the suggested window.
  • The challenge tag matches the challenge the relay sent before.
  • The relay tag matches the relay's own URL. URL normalization may apply; for most cases a correct domain name is enough.

4 Status Prefixes and the EOSE Hint

This NIP defines two machine-readable prefixes for the OK and CLOSED messages of NIP-01. OK carries them when the relay rejects an event write. CLOSED carries them when the relay rejects a subscription.

Status prefixes
PrefixMeaning
auth-required:The client has not performed AUTH. The relay requires it to fulfill this query or write this event.
restricted:The client already performed AUTH, but its key is still not allowed, or exceeds its authorization.

A relay may also use the "auth" hint in an EOSE message, as defined in NIP-67. The hint tells the client that more results may be available after authentication. In that case the relay must send its AUTH challenge before the EOSE message.

Tech Note

The restricted: prefix also appears in the standard OK prefix list of NIP-01. The two definitions agree, but the rule lives in both documents. Also, the motivation section still cites kind: 4 DMs from NIP-04, which is deprecated in favor of NIP-17; the auth mechanism itself is unaffected.

5 Protocol Flow

A relay usually requires authentication only for certain jobs, such as answering a REQ or accepting an EVENT write. The flows below are the expected common cases.

auth-required in response to a REQ

The client asks for restricted events. The relay closes the subscription with an auth-required message. The client authenticates and repeats the request. The only requirement: the client must hold a stored challenge for that relay before it can react to the CLOSED message. The relay may send its AUTH challenge right when the client connects, or immediately before the CLOSED.

Client Relay ["AUTH", "<challenge>"] ["REQ", "sub_1", {"kinds":[4]}] ["CLOSED", "sub_1", "auth-required: …"] ["AUTH", {"kind":22242, …}] ["OK", "abcdef…", true, ""] ["REQ", "sub_1", {"kinds":[4]}] ["EVENT", "sub_1", {…}] × matches The session stays authenticated for the rest of the connection.
Fig. 42-2 — Auth on demand. The relay rejects the subscription, the client proves its key, then the same REQ succeeds.

auth-required in response to an EVENT

The same flow applies when the client writes an event. The relay answers the write with an OK message set to false and an auth-required prefix, instead of a CLOSED message. The client authenticates and sends the event again. See the transcript in §6.

6 Worked Example

The session below shows both flows on one connection. Orange lines go to the relay. Blue lines come from the relay. Note the two AUTH events from different pubkeys: the relay authenticates both keys.

Session transcript - wss://relay.example.com
-- on connect, the relay offers a challenge --
← ["AUTH", "8bd1a2f9c044"]

-- an unauthenticated write is refused --
→ ["EVENT", {"id":"012345ab…", "kind":1, …}]
← ["OK", "012345ab…", false, "auth-required: we only accept events from registered users"]

-- the client signs the challenge, once per pubkey --
→ ["AUTH", {"id":"abcdef01…","pubkey":"22a12a12…","created_at":1756713600,
     "kind":22242,
     "tags":[["relay","wss://relay.example.com/"],["challenge","8bd1a2f9c044"]],
     "content":"","sig":"90cf86e1…"}]
→ ["AUTH", {"id":"abcde202…","pubkey":"7be44a11…","kind":22242, …}]
← ["OK", "abcdef01…", true, ""]
← ["OK", "abcde202…", true, ""]

-- the same write now succeeds --
→ ["EVENT", {"id":"012345ab…", "kind":1, …}]
← ["OK", "012345ab…", true, ""]

-- restricted queries work too --
→ ["REQ", "dms", {"kinds":[4]}]
← ["EVENT", "dms", {…}]
← ["EOSE", "dms"]

-- but a key the relay does not allow still gets --
← ["CLOSED", "sub_9", "restricted: this key has no paid plan"]

7 Version History

NIP-42 began as a simplification of an earlier authentication draft by semisol. fiatjaf rewrote it in January 2023, and it has grown a few capabilities since. Notable changes, newest first:

  • 20262026-09Added the "auth" hint for EOSE messages, defined in NIP-67.
  • 20252025-12Added the relay tag check to the verification checklist.
  • 20252025-09Allowed multi-user AUTH: several pubkeys can authenticate on one connection.
  • 20232023-12Integrated CLOSED messages: auth-required and restricted prefixes for rejected subscriptions and writes.
  • 20232023-01Added the relay-generated challenge; clarified created_at and session duration; switched replies to the OK message.
  • 2023+2023-2026Formatting passes, JSON cleanups, and author-list removal.
  • Origin2023-01fiatjaf published the NIP as a drastic simplification of semisol's auth proposal.

Original Proposal - Credits

Authorfiatjaf (simplifying an earlier draft by semisol)
First commit2023-01-02 — “drastically simplify @semisol's auth NIP”
Statusdraft - optional - relay — relays choose whether to require it
Source42.md on GitHub - full change history

8 See Also

  • NIP-01 — the base protocol: EVENT, REQ, OK, CLOSED, and the ephemeral kind range that 22242 sits in.
  • NIP-67 — the EOSE completeness hint, including the "auth" hint used here.
  • NIP-70 — protected events, which lean on this NIP so only the author can publish them.
  • NIP-04 — the deprecated kind: 4 DMs named in the motivation; see NIP-17 for the replacement.
  • NIP-11 — the relay information document, where a relay can advertise its limits.