From 82aafbef39a4a183fc6b1e8488e4a2db81a3fa90 Mon Sep 17 00:00:00 2001 From: Semisol <45574030+Semisol@users.noreply.github.com> Date: Fri, 16 Dec 2022 01:11:08 +0300 Subject: [PATCH 1/9] add nip-41: authentication --- 41.md | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 41.md diff --git a/41.md b/41.md new file mode 100644 index 0000000..56adf3f --- /dev/null +++ b/41.md @@ -0,0 +1,69 @@ +NIP-41 +====== + +Authentication of clients to relays +----------------------------------- + +`draft` `optional` `author:Semisol` + +This NIP defines a way for clients to authenticate to relays by signing an ephemeral event. + +## Event format + +An event should be signed with `kind: 22241` and `content` being the WebSockets URL of the relay. +The URL MUST: +- Have a trailing slash if the path is `/` +- Not have a port number if protocol is `ws` and port is `80` or protocol is `wss` and port is `443` +- Not include the search/query + +An example event is shown below: +```json +{ + "id": "...", + "pubkey": "...", + "created_at": 1669695536, + "kind": 22223, + "tags": [], + "content": "wss://relay.example.com/", + "sig": "..." +} +``` + +## Commands between the relay and the client + +This NIP defines a new command, `AUTH`, which can be sent by either the relay or the client with different meanings. + +A relay may send `["AUTH", , ]` when it needs authentication (examples: accessing kind 4 events, whitelist only, requiring proof of authorship for `EVENT`). +The human readable reason SHOULD be prefixed with a string in the format `: `. A list of short descriptions is listed below: +- `restricted`: This relay is restricted and requires the pubkey of the client to check if it can access the relay. (requires a whitelist, payment, etc) +- `publish`: The relay requests that the client identify who is sending an `EVENT` command. + This can be used for where only the signer of an event can publish it, or a pay-as-you-go relay allowing for you to publish others' events +- `private`: The client has attempted to access a restricted set of events (example: kind 4 DMs) and should authenticate with the relay to receive them. +- `other`: Any reason not defined here. + +`data object` MUST be a JSON object. It currently has one defined field, but may be extended by amendments to this NIP or other NIPs: +- `subscription_id`: The subscription ID that triggered the `AUTH` request. + +A client may send one of the following to the relay: +- `["AUTH", ]` to indicate it has accepted the request. This may also be sent without an authentication request. +- `["AUTH", null]` to indicate it has rejected the request. + +A relay SHOULD send the [`OK`](https://github.com/nostr-protocol/nips/blob/master/20.md) command after they receive a +non-rejecting authentication response, and use one of the following `message` prefixes if the event sent cannot be verified: +- `too-old:`: The event signed has a too old `created_at` date. +- `invalid-url:`: The URL in `content` is not matching. +- `already-used:`: This event was already used to authenticate to this relay. +- `bad-signature:`: The event has a bad signature. + +Please note that the `OK` message should only be sent as a response to other commands that the client sends instead of the `AUTH` command if the issue is not related to the authentication event being incorrectly signed (example: not on whitelist). + +Relays SHOULD send [`EOSE`](https://github.com/nostr-protocol/nips/blob/master/15.md) when an authentication request is triggered by a `REQ` command, and not send stored events after the `EOSE` when authentication is completed. +Relays SHOULD send `OK` as a response when a command triggers authentication with the reason starting with `auth:`. + +Clients SHOULD retry the action (resending event, resubscribing) after they authenticate if they receive an `AUTH` request. + +## Signed event verification +Relays when processing `AUTH` responses SHOULD verify the following before accepting the response: +- that the `kind` is `22223` +- that the event was recently signed (~10 minutes, by `created_at`) +- that the `content` field matches the relay URL From a04da3f1765a960e5994fad0044f45de1d43e683 Mon Sep 17 00:00:00 2001 From: Semisol <45574030+Semisol@users.noreply.github.com> Date: Sat, 17 Dec 2022 00:02:03 +0300 Subject: [PATCH 2/9] nip-41: fix kind mismatch on example event --- 41.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/41.md b/41.md index 56adf3f..e2fd15b 100644 --- a/41.md +++ b/41.md @@ -22,7 +22,7 @@ An example event is shown below: "id": "...", "pubkey": "...", "created_at": 1669695536, - "kind": 22223, + "kind": 22241, "tags": [], "content": "wss://relay.example.com/", "sig": "..." From df2837606441953be553f3b31e80ceae48f63a5e Mon Sep 17 00:00:00 2001 From: Semisol <45574030+Semisol@users.noreply.github.com> Date: Sat, 24 Dec 2022 14:08:34 +0300 Subject: [PATCH 3/9] nip41: fix outdated kind --- 41.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/41.md b/41.md index e2fd15b..d50096c 100644 --- a/41.md +++ b/41.md @@ -64,6 +64,6 @@ Clients SHOULD retry the action (resending event, resubscribing) after they auth ## Signed event verification Relays when processing `AUTH` responses SHOULD verify the following before accepting the response: -- that the `kind` is `22223` +- that the `kind` is `22241` - that the event was recently signed (~10 minutes, by `created_at`) - that the `content` field matches the relay URL From b9467cb428489d800e8e2729b0449ee1ceec03a5 Mon Sep 17 00:00:00 2001 From: Semisol <45574030+Semisol@users.noreply.github.com> Date: Sat, 24 Dec 2022 18:42:05 +0300 Subject: [PATCH 4/9] nip41: allow for delegated events --- 41.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/41.md b/41.md index d50096c..dfa49c3 100644 --- a/41.md +++ b/41.md @@ -16,6 +16,8 @@ The URL MUST: - Not have a port number if protocol is `ws` and port is `80` or protocol is `wss` and port is `443` - Not include the search/query +Relays SHOULD treat authenticaiton events with a valid delegation as if it was the delegator authenticating. + An example event is shown below: ```json { From c80be21cd44aa74efa42ead23a12343bf33ade34 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Mon, 2 Jan 2023 16:49:37 -0300 Subject: [PATCH 5/9] drastically simplify @semisol's auth NIP. --- 41.md | 71 ------------------------------------------------------- 42.md | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 13 ++++++---- 3 files changed, 76 insertions(+), 76 deletions(-) delete mode 100644 41.md create mode 100644 42.md diff --git a/41.md b/41.md deleted file mode 100644 index dfa49c3..0000000 --- a/41.md +++ /dev/null @@ -1,71 +0,0 @@ -NIP-41 -====== - -Authentication of clients to relays ------------------------------------ - -`draft` `optional` `author:Semisol` - -This NIP defines a way for clients to authenticate to relays by signing an ephemeral event. - -## Event format - -An event should be signed with `kind: 22241` and `content` being the WebSockets URL of the relay. -The URL MUST: -- Have a trailing slash if the path is `/` -- Not have a port number if protocol is `ws` and port is `80` or protocol is `wss` and port is `443` -- Not include the search/query - -Relays SHOULD treat authenticaiton events with a valid delegation as if it was the delegator authenticating. - -An example event is shown below: -```json -{ - "id": "...", - "pubkey": "...", - "created_at": 1669695536, - "kind": 22241, - "tags": [], - "content": "wss://relay.example.com/", - "sig": "..." -} -``` - -## Commands between the relay and the client - -This NIP defines a new command, `AUTH`, which can be sent by either the relay or the client with different meanings. - -A relay may send `["AUTH", , ]` when it needs authentication (examples: accessing kind 4 events, whitelist only, requiring proof of authorship for `EVENT`). -The human readable reason SHOULD be prefixed with a string in the format `: `. A list of short descriptions is listed below: -- `restricted`: This relay is restricted and requires the pubkey of the client to check if it can access the relay. (requires a whitelist, payment, etc) -- `publish`: The relay requests that the client identify who is sending an `EVENT` command. - This can be used for where only the signer of an event can publish it, or a pay-as-you-go relay allowing for you to publish others' events -- `private`: The client has attempted to access a restricted set of events (example: kind 4 DMs) and should authenticate with the relay to receive them. -- `other`: Any reason not defined here. - -`data object` MUST be a JSON object. It currently has one defined field, but may be extended by amendments to this NIP or other NIPs: -- `subscription_id`: The subscription ID that triggered the `AUTH` request. - -A client may send one of the following to the relay: -- `["AUTH", ]` to indicate it has accepted the request. This may also be sent without an authentication request. -- `["AUTH", null]` to indicate it has rejected the request. - -A relay SHOULD send the [`OK`](https://github.com/nostr-protocol/nips/blob/master/20.md) command after they receive a -non-rejecting authentication response, and use one of the following `message` prefixes if the event sent cannot be verified: -- `too-old:`: The event signed has a too old `created_at` date. -- `invalid-url:`: The URL in `content` is not matching. -- `already-used:`: This event was already used to authenticate to this relay. -- `bad-signature:`: The event has a bad signature. - -Please note that the `OK` message should only be sent as a response to other commands that the client sends instead of the `AUTH` command if the issue is not related to the authentication event being incorrectly signed (example: not on whitelist). - -Relays SHOULD send [`EOSE`](https://github.com/nostr-protocol/nips/blob/master/15.md) when an authentication request is triggered by a `REQ` command, and not send stored events after the `EOSE` when authentication is completed. -Relays SHOULD send `OK` as a response when a command triggers authentication with the reason starting with `auth:`. - -Clients SHOULD retry the action (resending event, resubscribing) after they authenticate if they receive an `AUTH` request. - -## Signed event verification -Relays when processing `AUTH` responses SHOULD verify the following before accepting the response: -- that the `kind` is `22241` -- that the event was recently signed (~10 minutes, by `created_at`) -- that the `content` field matches the relay URL diff --git a/42.md b/42.md new file mode 100644 index 0000000..d5122ba --- /dev/null +++ b/42.md @@ -0,0 +1,68 @@ +NIP-42 +====== + +Authentication of clients to relays +----------------------------------- + +`draft` `optional` `author:Semisol` `author:fiatjaf` + +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. + +## Protocol flow + +This NIP defines a new message, `AUTH`, which clients can send to relays when they want to authenticate. The message is of the following form: + +``` +["AUTH", ] +``` + +The signed event is an ephemeral event not meant to be published or queried, it must be of `kind: 22242` and content must be set to the +WebSocket URL of the relay. `created_at` should be the current time. Example: + +```json +{ + "id": "...", + "pubkey": "...", + "created_at": 1669695536, + "kind": 22242, + "tags": [], + "content": "wss://relay.example.com/", + "sig": "..." +} +``` + +The client may send an auth message right before performing an action for which it knows authentication will be required -- for example, right +before requesting `kind: 4` chat messages --, or it may do right on connection start or at some other moment it deems best. + +Upon receiving a message from an unauthenticated user it can't fulfill without authentication, a relay may choose to notify the client. For +that it can use a `NOTICE` message with a standard prefix `"restricted: "` that is readable both by humans and machines, for example: + +``` +["NOTICE", "restricted: we can't serve DMs to unauthenticated users, does your client implement NIP-42?"] +``` + +or + +``` +["NOTICE", "restricted: we do not accept events from unauthenticated users, please sign up at https://example.com/"] +``` + +## Signed Event Verification + +To verify `AUTH` messages, relays must ensure: + + - that the `kind` is `22242` + - that the event was recently signed (~10 minutes, by `created_at`) + - that the `content` field matches the relay URL + - URL normalization techniques can be applied. For most cases just checking if the domain name is correct should be enough. diff --git a/README.md b/README.md index cce4166..f627f4d 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ NIPs stand for **Nostr Implementation Possibilities**. They exist to document wh - [NIP-35: User Discovery](35.md) - [NIP-36: Sensitive Content](36.md) - [NIP-40: Expiration Timestamp](40.md) +- [NIP-42: Authentication of clients to relays](42.md) ## Event Kinds @@ -47,6 +48,7 @@ NIPs stand for **Nostr Implementation Possibilities**. They exist to document wh | 43 | Channel Hide Message | [28](28.md) | | 44 | Channel Mute User | [28](28.md) | | 45-49 | Public Chat Reserved | [28](28.md) | +| 22242 | Client Authentication | [42](42.md) | | 10000-19999 | Replaceable Events Reserved | [16](16.md) | | 20000-29999 | Ephemeral Events Reserved | [16](16.md) | @@ -54,11 +56,12 @@ NIPs stand for **Nostr Implementation Possibilities**. They exist to document wh ## Message types ### Client to Relay -| type | description | NIP | -|-------|-----------------------------------------------------|------------| -| EVENT | used to publish events | [1](01.md) | -| REQ | used to request events and subscribe to new updates | [1](01.md) | -| CLOSE | used to stop previous subscriptions | [1](01.md) | +| type | description | NIP | +|-------|-----------------------------------------------------|-------------| +| EVENT | used to publish events | [1](01.md) | +| REQ | used to request events and subscribe to new updates | [1](01.md) | +| CLOSE | used to stop previous subscriptions | [1](01.md) | +| AUTH | used to send authentication events | [42](42.md) | ### Relay to Client | type | description | NIP | From 4a5202646a86ce63a85a50b560d1a2fbce277df6 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Mon, 2 Jan 2023 17:26:41 -0300 Subject: [PATCH 6/9] use "OK" message. --- 42.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/42.md b/42.md index d5122ba..b17aa14 100644 --- a/42.md +++ b/42.md @@ -52,10 +52,10 @@ that it can use a `NOTICE` message with a standard prefix `"restricted: "` that ["NOTICE", "restricted: we can't serve DMs to unauthenticated users, does your client implement NIP-42?"] ``` -or +or it can return an `OK` message noting the reason an event was not written using the same prefix: ``` -["NOTICE", "restricted: we do not accept events from unauthenticated users, please sign up at https://example.com/"] +["OK", "b1a649ebe8...", false, "restricted: we do not accept events from unauthenticated users, please sign up at https://example.com/"] ``` ## Signed Event Verification From 50faceef0965042714e7f50a45b102f35c5231ca Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Wed, 4 Jan 2023 10:24:37 -0300 Subject: [PATCH 7/9] clarify `created_at` and auth session duration. --- 42.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/42.md b/42.md index b17aa14..4c884a9 100644 --- a/42.md +++ b/42.md @@ -43,7 +43,8 @@ WebSocket URL of the relay. `created_at` should be the current time. Example: ``` The client may send an auth message right before performing an action for which it knows authentication will be required -- for example, right -before requesting `kind: 4` chat messages --, or it may do right on connection start or at some other moment it deems best. +before requesting `kind: 4` chat messages --, or it may do right on connection start or at some other moment it deems best. The authentication +is expected to last for the duration of the WebSocket connection. Upon receiving a message from an unauthenticated user it can't fulfill without authentication, a relay may choose to notify the client. For that it can use a `NOTICE` message with a standard prefix `"restricted: "` that is readable both by humans and machines, for example: @@ -62,7 +63,7 @@ or it can return an `OK` message noting the reason an event was not written usin To verify `AUTH` messages, relays must ensure: - - that the `kind` is `22242` - - that the event was recently signed (~10 minutes, by `created_at`) - - that the `content` field matches the relay URL + - that the `kind` is `22242`; + - that the event `created_at` is close (e.g. within ~10 minutes) of the current time; + - that the `content` field matches the relay URL: - URL normalization techniques can be applied. For most cases just checking if the domain name is correct should be enough. From 6a70967f0eb6e7b00fc811e34631f8db046d6e1f Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Sat, 7 Jan 2023 19:53:42 -0300 Subject: [PATCH 8/9] add challenge from relay. --- 42.md | 37 +++++++++++++++++++++++++++---------- README.md | 1 + 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/42.md b/42.md index 4c884a9..93bb3e3 100644 --- a/42.md +++ b/42.md @@ -19,16 +19,23 @@ A relay may want to require clients to authenticate to access restricted resourc 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. -## Protocol flow +## Definitions -This NIP defines a new message, `AUTH`, which clients can send to relays when they want to authenticate. The message is of the following form: +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 is of the following form: ``` -["AUTH", ] +["AUTH", ] ``` -The signed event is an ephemeral event not meant to be published or queried, it must be of `kind: 22242` and content must be set to the -WebSocket URL of the relay. `created_at` should be the current time. Example: +And, when sent by clients, of the following form: + +``` +["AUTH", ] +``` + +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. `created_at` should be the current time. Example: ```json { @@ -36,18 +43,27 @@ WebSocket URL of the relay. `created_at` should be the current time. Example: "pubkey": "...", "created_at": 1669695536, "kind": 22242, - "tags": [], - "content": "wss://relay.example.com/", + "tags": [ + ["relay", "wss://relay.example.com/"], + ["challenge", "challengestringhere"] + ], + "content": "", "sig": "..." } ``` +## Protocol flow + +At any moment the relay may send an `AUTH` message to the client containing a challenge. After receiving that the client may decide to +authenticate itself or not. The challenge is expected to be valid for the duration of the connection or until a next challenge is sent by +the relay. + The client may send an auth message right before performing an action for which it knows authentication will be required -- for example, right before requesting `kind: 4` chat messages --, or it may do right on connection start or at some other moment it deems best. The authentication is expected to last for the duration of the WebSocket connection. Upon receiving a message from an unauthenticated user it can't fulfill without authentication, a relay may choose to notify the client. For -that it can use a `NOTICE` message with a standard prefix `"restricted: "` that is readable both by humans and machines, for example: +that it can use a `NOTICE` or `OK` message with a standard prefix `"restricted: "` that is readable both by humans and machines, for example: ``` ["NOTICE", "restricted: we can't serve DMs to unauthenticated users, does your client implement NIP-42?"] @@ -56,7 +72,7 @@ that it can use a `NOTICE` message with a standard prefix `"restricted: "` that or it can return an `OK` message noting the reason an event was not written using the same prefix: ``` -["OK", "b1a649ebe8...", false, "restricted: we do not accept events from unauthenticated users, please sign up at https://example.com/"] +["OK", , false, "restricted: we do not accept events from unauthenticated users, please sign up at https://example.com/"] ``` ## Signed Event Verification @@ -65,5 +81,6 @@ 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 `content` field matches the relay URL: + - 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. diff --git a/README.md b/README.md index f627f4d..05737b1 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ NIPs stand for **Nostr Implementation Possibilities**. They exist to document wh | NOTICE | used to send human-readable messages to clients | [1](01.md) | | EOSE | used to notify clients all stored events have been sent | [15](15.md) | | OK | used to notify clients if an EVENT was successuful | [20](20.md) | +| AUTH | used to send authentication challenges | [42](42.md) | Please update these lists when proposing NIPs introducing new event kinds. From 60741160535de8137fb7a74bd6561adef974b884 Mon Sep 17 00:00:00 2001 From: Leo Wandersleb Date: Wed, 11 Jan 2023 00:05:15 -0300 Subject: [PATCH 9/9] Update 42.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ricardo Arturo Cabral Mejía --- 42.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/42.md b/42.md index 93bb3e3..9b0c45b 100644 --- a/42.md +++ b/42.md @@ -35,7 +35,9 @@ And, when sent by clients, of the following form: ``` 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. `created_at` should be the current time. Example: +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: ```json {