clone

every nostr nip as a chapter of an illustrated, early-2000s printed game manual

main
nip.guide / site / nips / 42.html
42.html 20.6K
1<!DOCTYPE html>2<html lang="en">3<head>4<meta charset="utf-8">5<meta name="viewport" content="width=device-width, initial-scale=1">6<title>NIP-42 - Authentication of Clients to Relays — Nostr Instruction Manual</title>7<meta name="description" content="NIP-42: how a client authenticates to a relay with a signed kind 22242 event. AUTH messages, challenges, auth-required and restricted prefixes.">8<link rel="preload" href="../assets/fonts/michroma-400-latin.woff2" as="font" type="font/woff2" crossorigin>9<link rel="preload" href="../assets/fonts/exo2-var-latin.woff2" as="font" type="font/woff2" crossorigin>10<link rel="preload" href="../assets/fonts/sairacondensed-600-latin.woff2" as="font" type="font/woff2" crossorigin>11<link rel="preload" href="../assets/fonts/sairacondensed-700-latin.woff2" as="font" type="font/woff2" crossorigin>12<link rel="preload" href="../assets/fonts/sairacondensed-500-latin.woff2" as="font" type="font/woff2" crossorigin>13<link rel="preload" href="../assets/fonts/sharetechmono-400-latin.woff2" as="font" type="font/woff2" crossorigin>14<link rel="preload" href="../assets/fonts/exo2-400i-latin.woff2" as="font" type="font/woff2" crossorigin>15<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml">16<link rel="stylesheet" href="../assets/manual.css">17</head>18<body>1920<svg width="0" height="0" style="position:absolute">21 <defs>22 <marker id="arr" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto">23 <path d="M1,1 L8,4.5 L1,8 Z" fill="#f26a00"/>24 </marker>25 <marker id="arrb" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto">26 <path d="M1,1 L8,4.5 L1,8 Z" fill="#2e5f9e"/>27 </marker>28 </defs>29</svg>3031<div class="page">32 <span class="staple s1"></span><span class="staple s2"></span>3334 <header class="band">35 <a class="logo" href="../index.html">NOSTR<b>▸</b>PROTOCOL MANUAL</a>36 <span class="booklet-note">Chapter 42 - Relay Ops</span>37 </header>38 <nav class="tabs">39 <a href="../index.html">Contents</a>40 <a href="#overview" class="on">Overview</a>41 <a href="#spec">Spec</a>42 <a href="#examples">Examples</a>43 <a href="#history">History</a>44 </nav>4546 <div class="chapter">47 <div class="nipno" data-n="42">42</div>48 <div class="titles">49 <div class="kicker">Nostr Implementation Possibility - Hex № 42</div>50 <h1>Authentication of Clients to Relays</h1>51 <p class="sub">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.</p>52 </div>53 </div>5455 <main class="spread">56 <div class="main">5758 <h2 class="bar" id="overview"><span class="num">1</span> Overview</h2>59 <p>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 <strong>ephemeral event</strong> that contains a challenge string from the relay. The relay verifies the signature and marks the connection as authenticated for that pubkey.</p>60 <p>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 <code>kind: 4</code> direct messages (<a href="04.html">NIP-04</a>) so only the two chat parties can query them. It may limit subscriptions of any kind to paying or whitelisted users.</p>61 <p>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.</p>6263 <div class="box hint">64 <span class="tag">Hint!</span>65 <p>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.</p>66 </div>6768 <figure class="diagram">69 <div class="frame">70 <svg viewBox="0 0 560 150" role="img" aria-label="Relay sends a challenge; client signs a kind 22242 event; relay verifies it and authenticates the connection">71 <rect class="d-actor" x="10" y="50" width="110" height="50" rx="8"/>72 <text class="d-actor-label" x="65" y="72" text-anchor="middle">Client</text>73 <text class="d-note" x="65" y="89" text-anchor="middle">holds keypair</text>74 <rect class="d-actor" x="440" y="50" width="110" height="50" rx="8"/>75 <text class="d-actor-label" x="495" y="72" text-anchor="middle">Relay</text>76 <text class="d-note" x="495" y="89" text-anchor="middle">holds challenge</text>77 <path class="d-msg blue d-flow" d="M438,60 L122,60"/>78 <text class="d-label" x="280" y="52" text-anchor="middle">["AUTH", "&lt;challenge&gt;"]</text>79 <path class="d-msg d-flow" d="M122,90 L438,90"/>80 <text class="d-label" x="280" y="112" text-anchor="middle">["AUTH", signed kind 22242 event]</text>81 <text class="d-note" x="280" y="138" text-anchor="middle">Relay verifies the signature. The pubkey is authenticated for this connection.</text>82 <circle r="4" fill="#2e5f9e">83 <animateMotion dur="2.6s" repeatCount="indefinite" path="M438,60 L122,60"/>84 </circle>85 <circle r="4" fill="#f26a00">86 <animateMotion dur="2.6s" begin="1.3s" repeatCount="indefinite" path="M122,90 L438,90"/>87 </circle>88 </svg>89 </div>90 <figcaption>Fig. 42-1 — <b>The handshake.</b> The relay issues a challenge. The client signs it into an event and sends it back.</figcaption>91 </figure>9293 <h2 class="bar" id="spec"><span class="num">2</span> The AUTH Message</h2>94 <p>This NIP adds one message type, <span class="kbd">AUTH</span>, to the <a href="01.html">NIP-01</a> message set. Both sides can send it, with different payloads:</p>9596 <div class="tscroll"><table class="ref">97 <caption>AUTH message forms</caption>98 <tr><th>Direction</th><th>Shape</th><th>Purpose</th></tr>99 <tr><td class="btn">relay → client</td><td><code>["AUTH", &lt;challenge-string&gt;]</code></td><td>Offer a challenge. Relays that support authentication can send this.</td></tr>100 <tr><td class="btn">client → relay</td><td><code>["AUTH", &lt;signed-event-json&gt;]</code></td><td>Prove control of a key by returning the signed challenge event.</td></tr>101 </table></div>102103 <p>The relay may send its <code>AUTH</code> 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 <code>AUTH</code> event at any point. The authenticated session then stays valid for the rest of the connection.</p>104 <p>A client may authenticate more than one pubkey. It sends signed events from multiple pubkeys in a sequence of <code>AUTH</code> messages. The relay must treat all of those pubkeys as authenticated.</p>105 <p>The relay must answer every client <code>AUTH</code> message with an <code>OK</code> message, exactly as it answers an <code>EVENT</code> message.</p>106107 <h2 class="bar" id="event"><span class="num">3</span> The Authentication Event</h2>108 <p>The proof is an ephemeral event of <code>kind: 22242</code>. 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 <code>created_at</code> field should be the current time.</p>109110 <div class="crt">111 <div class="titlebar">Wire format - kind 22242 authentication event</div>112<pre>{113 <span class="k">"kind"</span>: 22242,114 <span class="k">"tags"</span>: [115 [<span class="s">"relay"</span>, <span class="s">"wss://relay.example.com/"</span>],116 [<span class="s">"challenge"</span>, <span class="s">"challengestringhere"</span>]117 ],118 <span class="c">// other NIP-01 fields: id, pubkey, created_at, content, sig...</span>119}</pre>120 </div>121122 <div class="tscroll"><table class="ref">123 <caption>Tag reference</caption>124 <tr><th>Tag</th><th>Value</th><th>Rule</th></tr>125 <tr><td class="btn">relay</td><td>relay URL</td><td>The URL of the relay the client authenticates to.</td></tr>126 <tr><td class="btn">challenge</td><td>challenge string</td><td>The exact string received from the relay in its <code>AUTH</code> message.</td></tr>127 </table></div>128129 <div class="box warn">130 <span class="tag">Warning!</span>131 <p>Relays must exclude <code>kind: 22242</code> 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.</p>132 </div>133134 <h3>Verification rules</h3>135 <p>To verify a client <code>AUTH</code> message, the relay must check all of the following:</p>136 <ul>137 <li>The <code>kind</code> is <code>22242</code>.</li>138 <li>The <code>created_at</code> is close to the current time. About 10 minutes of tolerance is the suggested window.</li>139 <li>The <code>challenge</code> tag matches the challenge the relay sent before.</li>140 <li>The <code>relay</code> tag matches the relay's own URL. URL normalization may apply; for most cases a correct domain name is enough.</li>141 </ul>142143 <h2 class="bar" id="prefixes"><span class="num">4</span> Status Prefixes and the EOSE Hint</h2>144 <p>This NIP defines two machine-readable prefixes for the <code>OK</code> and <code>CLOSED</code> messages of <a href="01.html">NIP-01</a>. <code>OK</code> carries them when the relay rejects an event write. <code>CLOSED</code> carries them when the relay rejects a subscription.</p>145146 <div class="tscroll"><table class="ref">147 <caption>Status prefixes</caption>148 <tr><th>Prefix</th><th>Meaning</th></tr>149 <tr><td class="btn">auth-required:</td><td>The client has not performed <code>AUTH</code>. The relay requires it to fulfill this query or write this event.</td></tr>150 <tr><td class="btn">restricted:</td><td>The client already performed <code>AUTH</code>, but its key is still not allowed, or exceeds its authorization.</td></tr>151 </table></div>152153 <p>A relay may also use the <code>"auth"</code> hint in an <code>EOSE</code> message, as defined in <a href="67.html">NIP-67</a>. The hint tells the client that more results may be available after authentication. In that case the relay must send its <code>AUTH</code> challenge before the <code>EOSE</code> message.</p>154155 <div class="box tech">156 <span class="tag">Tech Note</span>157 <p>The <code>restricted:</code> prefix also appears in the standard <code>OK</code> prefix list of <a href="01.html">NIP-01</a>. The two definitions agree, but the rule lives in both documents. Also, the motivation section still cites <code>kind: 4</code> DMs from <a href="04.html">NIP-04</a>, which is deprecated in favor of <a href="17.html">NIP-17</a>; the auth mechanism itself is unaffected.</p>158 </div>159160 <h2 class="bar" id="flow"><span class="num">5</span> Protocol Flow</h2>161 <p>A relay usually requires authentication only for certain jobs, such as answering a <code>REQ</code> or accepting an <code>EVENT</code> write. The flows below are the expected common cases.</p>162163 <h3>auth-required in response to a REQ</h3>164 <p>The client asks for restricted events. The relay closes the subscription with an <code>auth-required</code> 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 <code>CLOSED</code> message. The relay may send its <code>AUTH</code> challenge right when the client connects, or immediately before the <code>CLOSED</code>.</p>165166 <figure class="diagram">167 <div class="frame">168 <svg viewBox="0 0 560 330" role="img" aria-label="Sequence diagram: relay sends AUTH challenge, client REQ is closed with auth-required, client sends AUTH event, relay replies OK, repeated REQ succeeds">169 <line class="d-wire" x1="110" y1="40" x2="110" y2="315" stroke-dasharray="3 4"/>170 <line class="d-wire" x1="450" y1="40" x2="450" y2="315" stroke-dasharray="3 4"/>171 <rect class="d-actor" x="55" y="8" width="110" height="30" rx="6"/>172 <text class="d-actor-label" x="110" y="28" text-anchor="middle">Client</text>173 <rect class="d-actor" x="395" y="8" width="110" height="30" rx="6"/>174 <text class="d-actor-label" x="450" y="28" text-anchor="middle">Relay</text>175 <path class="d-msg blue" d="M450,62 L115,62"/>176 <text class="d-label" x="280" y="55" text-anchor="middle">["AUTH", "&lt;challenge&gt;"]</text>177 <path class="d-msg" d="M110,95 L445,95"/>178 <text class="d-label" x="280" y="88" text-anchor="middle">["REQ", "sub_1", {"kinds":[4]}]</text>179 <path class="d-msg blue" d="M450,128 L115,128"/>180 <text class="d-label" x="280" y="121" text-anchor="middle">["CLOSED", "sub_1", "auth-required: …"]</text>181 <path class="d-msg" d="M110,161 L445,161"/>182 <text class="d-label" x="280" y="154" text-anchor="middle">["AUTH", {"kind":22242, …}]</text>183 <path class="d-msg blue" d="M450,194 L115,194"/>184 <text class="d-label" x="280" y="187" text-anchor="middle">["OK", "abcdef…", true, ""]</text>185 <path class="d-msg" d="M110,227 L445,227"/>186 <text class="d-label" x="280" y="220" text-anchor="middle">["REQ", "sub_1", {"kinds":[4]}]</text>187 <path class="d-msg blue d-flow" d="M450,260 L115,260"/>188 <text class="d-label" x="280" y="253" text-anchor="middle">["EVENT", "sub_1", {…}] × matches</text>189 <text class="d-note" x="280" y="295" text-anchor="middle">The session stays authenticated for the rest of the connection.</text>190 <circle r="4" fill="#f26a00">191 <animateMotion dur="2.6s" repeatCount="indefinite" path="M110,161 L445,161"/>192 </circle>193 </svg>194 </div>195 <figcaption>Fig. 42-2 — <b>Auth on demand.</b> The relay rejects the subscription, the client proves its key, then the same <code>REQ</code> succeeds.</figcaption>196 </figure>197198 <h3>auth-required in response to an EVENT</h3>199 <p>The same flow applies when the client writes an event. The relay answers the write with an <code>OK</code> message set to <code>false</code> and an <code>auth-required</code> prefix, instead of a <code>CLOSED</code> message. The client authenticates and sends the event again. See the transcript in §6.</p>200201 <h2 class="bar" id="examples"><span class="num">6</span> Worked Example</h2>202 <p>The session below shows both flows on one connection. Orange lines go to the relay. Blue lines come from the relay. Note the two <code>AUTH</code> events from different pubkeys: the relay authenticates both keys.</p>203 <div class="crt">204 <div class="titlebar">Session transcript - wss://relay.example.com</div>205<pre><span class="c">-- on connect, the relay offers a challenge --</span>206<span class="recv">← ["AUTH", "8bd1a2f9c044"]</span>207208<span class="c">-- an unauthenticated write is refused --</span>209<span class="send">→ ["EVENT", {"id":"012345ab…", "kind":1, …}]</span>210<span class="recv">← ["OK", "012345ab…", false, "auth-required: we only accept events from registered users"]</span>211212<span class="c">-- the client signs the challenge, once per pubkey --</span>213<span class="send">→ ["AUTH", {"id":"abcdef01…","pubkey":"22a12a12…","created_at":1756713600,214 "kind":22242,215 "tags":[["relay","wss://relay.example.com/"],["challenge","8bd1a2f9c044"]],216 "content":"","sig":"90cf86e1…"}]</span>217<span class="send">→ ["AUTH", {"id":"abcde202…","pubkey":"7be44a11…","kind":22242, …}]</span>218<span class="recv">← ["OK", "abcdef01…", true, ""]</span>219<span class="recv">← ["OK", "abcde202…", true, ""]</span>220221<span class="c">-- the same write now succeeds --</span>222<span class="send">→ ["EVENT", {"id":"012345ab…", "kind":1, …}]</span>223<span class="recv">← ["OK", "012345ab…", true, ""]</span>224225<span class="c">-- restricted queries work too --</span>226<span class="send">→ ["REQ", "dms", {"kinds":[4]}]</span>227<span class="recv">← ["EVENT", "dms", {…}]</span>228<span class="recv">← ["EOSE", "dms"]</span>229230<span class="c">-- but a key the relay does not allow still gets --</span>231<span class="recv">← ["CLOSED", "sub_9", "restricted: this key has no paid plan"]</span></pre>232 </div>233234 <h2 class="bar" id="history"><span class="num">7</span> Version History</h2>235 <p>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:</p>236 <ul class="patchnotes">237 <li><span class="ver">2026</span><span class="when">2026-09</span>Added the <code>"auth"</code> hint for <code>EOSE</code> messages, defined in <a href="67.html">NIP-67</a>.</li>238 <li><span class="ver">2025</span><span class="when">2025-12</span>Added the <code>relay</code> tag check to the verification checklist.</li>239 <li><span class="ver">2025</span><span class="when">2025-09</span>Allowed multi-user <code>AUTH</code>: several pubkeys can authenticate on one connection.</li>240 <li><span class="ver">2023</span><span class="when">2023-12</span>Integrated <code>CLOSED</code> messages: <code>auth-required</code> and <code>restricted</code> prefixes for rejected subscriptions and writes.</li>241 <li><span class="ver">2023</span><span class="when">2023-01</span>Added the relay-generated challenge; clarified <code>created_at</code> and session duration; switched replies to the <code>OK</code> message.</li>242 <li class="minor"><span class="ver">2023+</span><span class="when">2023-2026</span>Formatting passes, JSON cleanups, and author-list removal.</li>243 <li><span class="ver">Origin</span><span class="when">2023-01</span>fiatjaf published the NIP as a drastic simplification of semisol's auth proposal.</li>244 </ul>245246 <div class="credits">247 <h3 class="cardhead">Original Proposal - Credits</h3>248 <table>249 <tr><td>Author</td><td>fiatjaf (simplifying an earlier draft by semisol)</td></tr>250 <tr><td>First commit</td><td>2023-01-02 — “drastically simplify @semisol's auth NIP”</td></tr>251 <tr><td>Status</td><td>draft - optional - relay — relays choose whether to require it</td></tr>252 <tr><td>Source</td><td><a href="https://github.com/nostr-protocol/nips/blob/master/42.md">42.md on GitHub</a> - <a href="https://github.com/nostr-protocol/nips/commits/master/42.md">full change history</a></td></tr>253 </table>254 </div>255256 <h2 class="bar"><span class="num">8</span> See Also</h2>257 <ul>258 <li><a href="01.html">NIP-01</a> — the base protocol: <code>EVENT</code>, <code>REQ</code>, <code>OK</code>, <code>CLOSED</code>, and the ephemeral kind range that 22242 sits in.</li>259 <li><a href="67.html">NIP-67</a> — the <code>EOSE</code> completeness hint, including the <code>"auth"</code> hint used here.</li>260 <li><a href="70.html">NIP-70</a> — protected events, which lean on this NIP so only the author can publish them.</li>261 <li><a href="04.html">NIP-04</a> — the deprecated <code>kind: 4</code> DMs named in the motivation; see <a href="17.html">NIP-17</a> for the replacement.</li>262 <li><a href="11.html">NIP-11</a> — the relay information document, where a relay can advertise its limits.</li>263 </ul>264 </div>265266 <aside>267 <div class="cartridge">268 <div class="inner">269 <h3 class="cardhead">Spec Card</h3>270 <div class="lights"><span class="led on"></span><span class="led on"></span><span class="led on"></span></div>271 <dl>272 <dt>Status</dt><dd><span class="chip draft">draft</span><span class="chip">optional</span><span class="chip">relay</span></dd>273 <dt>Defines</dt><dd>AUTH message - kind 22242 - auth-required / restricted prefixes</dd>274 <dt>Kind</dt><dd>22242 (ephemeral)</dd>275 <dt>Requires</dt><dd><a href="01.html">NIP-01</a>; <a href="67.html">NIP-67</a> for the EOSE hint</dd>276 <dt>Transport</dt><dd>WebSocket - per-connection state</dd>277 <dt>First commit</dt><dd>2023-01-02</dd>278 <dt>Last updated</dt><dd>2026-09-01</dd>279 </dl>280 </div>281 </div>282 </aside>283 </main>284285 <nav class="navbtns">286 <a href="40.html">◀ NIP-40: Expiration Timestamp</a>287 <a href="43.html">NIP-43: Relay Access Metadata and Requests ▶</a>288 </nav>289290 <footer class="pagefoot">291 <a href="../index.html">Nostr Protocol Manual</a>292 <span class="pageno">NIP 42 / F4</span>293 </footer>294</div>295296<script src="../assets/webmcp.js" defer></script>297</body>298</html>