upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--46.md169
1 files changed, 81 insertions, 88 deletions
diff --git a/46.md b/46.md
index c356a9c..60850aa 100644
--- a/46.md
+++ b/46.md
@@ -6,7 +6,7 @@ Nostr Remote Signing
6 6
7## Changes 7## Changes
8 8
9`remote-signer-key` is introduced, passed in bunker url, clients must differentiate between `remote-signer-pubkey` and `user-pubkey`, must call `get_public_key` after connect. 9`remote-signer-key` is introduced, passed in bunker url, clients must differentiate between `remote-signer-pubkey` and `user-pubkey`, must call `get_public_key` after connect, nip05 login is removed, create_account moved to another NIP.
10 10
11## Rationale 11## Rationale
12 12
@@ -25,6 +25,14 @@ This NIP describes a method for 2-way communication between a remote signer and
25 25
26All pubkeys specified in this NIP are in hex format. 26All pubkeys specified in this NIP are in hex format.
27 27
28## Overview
29
301. _client_ generates `client-keypair`. This keypair doesn't need to be communicated to _user_ since it's largely disposable. _client_ might choose to store it locally and they should delete it on logout;
312. A connection is established (see below), _remote-signer_ learns `client-pubkey`, _client_ learns `remote-signer-pubkey`.
323. _client_ uses `client-keypair` to send requests to _remote-signer_ by `p`-tagging and encrypting to `remote-signer-pubkey`;
334. _remote-signer_ responds to _client_ by `p`-tagging and encrypting to the `client-pubkey`.
345. _client_ requests `get_public_key` to learn `user-pubkey`.
35
28## Initiating a connection 36## Initiating a connection
29 37
30There are two ways to initiate a connection: 38There are two ways to initiate a connection:
@@ -37,66 +45,16 @@ _remote-signer_ provides connection token in the form:
37bunker://<remote-signer-pubkey>?relay=<wss://relay-to-connect-on>&relay=<wss://another-relay-to-connect-on>&secret=<optional-secret-value> 45bunker://<remote-signer-pubkey>?relay=<wss://relay-to-connect-on>&relay=<wss://another-relay-to-connect-on>&secret=<optional-secret-value>
38``` 46```
39 47
40_user_ pastes this token on _client_, which then uses the details to connect to _remote-signer_ via the specified relays. Optional secret can be used for single successfully established connection only, _remote-signer_ SHOULD ignore new attempts to establish connection with old optional secret. 48_user_ passes this token to _client_, which then sends `connect` request to _remote-signer_ via the specified relays. Optional secret can be used for single successfully established connection only, _remote-signer_ SHOULD ignore new attempts to establish connection with old secret.
41 49
42### Direct connection initiated by the client 50### Direct connection initiated by the _client_
43 51
44In this case, basically the opposite direction of the first case, _client_ provides a connection token (or encodes the token in a QR code) and _remote-signer_ initiates a connection via the specified relays. 52_client_ provides a connection token in the form:
45 53
46``` 54```
47nostrconnect://<client-pubkey>?relay=<wss://relay-to-connect-on>&metadata=<json metadata in the form: {"name":"...", "url": "...", "description": "..."}> 55nostrconnect://<client-pubkey>?relay=<wss://relay-to-connect-on>&metadata=<json metadata: {"name":"...", "url": "...", "description": "...", "perms": "..."}>&secret=<required-secret-value>
48``` 56```
49 57_user_ passes this token to _remote-signer_, which then sends `connect` *response* event to the `client-pubkey` via the specified relays. Client discovers `remote-signer-pubkey` from connect response author. `secret` value MUST be provided to avoid connection spoofing, _client_ MUST validate the `secret` returned by `connect` response.
50## The flow
51
521. _client_ generates `client-keypair`. This keypair doesn't need to be communicated to _user_ since it's largely disposable. _client_ might choose to store it locally and they should delete it on logout;
532. _client_ gets `remote-signer-pubkey` (either via a `bunker://` connection string or a NIP-05 login-flow; shown below);
543. _client_ use `client-keypair` to send requests to _remote-signer_ by `p`-tagging and encrypting to `remote-signer-pubkey`;
554. _remote-signer_ responds to _client_ by `p`-tagging and encrypting to the `client-pubkey`.
56
57### Example flow for signing an event
58
59- `remote-signer-pubkey` is `fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52`
60- `user-pubkey` is also `fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52`
61- `client-pubkey` is `eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86`
62
63#### Signature request
64
65```js
66{
67 "kind": 24133,
68 "pubkey": "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86",
69 "content": nip04({
70 "id": <random_string>,
71 "method": "sign_event",
72 "params": [json_stringified(<{
73 content: "Hello, I'm signing remotely",
74 kind: 1,
75 tags: [],
76 created_at: 1714078911
77 }>)]
78 }),
79 "tags": [["p", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52"]], // p-tags the remote-signer-pubkey
80}
81```
82
83#### Response event
84
85```js
86{
87 "kind": 24133,
88 "pubkey": "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52",
89 "content": nip04({
90 "id": <random_string>,
91 "result": json_stringified(<signed-event>)
92 }),
93 "tags": [["p", "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86"]], // p-tags the client-pubkey
94}
95```
96
97#### Diagram
98
99![signing-example](https://i.nostr.build/P3gW.png)
100 58
101## Request Events `kind: 24133` 59## Request Events `kind: 24133`
102 60
@@ -125,11 +83,11 @@ The `content` field is a JSON-RPC-like message that is [NIP-04](04.md) encrypted
125 83
126### Methods/Commands 84### Methods/Commands
127 85
128Each of the following are methods that the client sends to the remote signer. 86Each of the following are methods that the _client_ sends to the _remote-signer_.
129 87
130| Command | Params | Result | 88| Command | Params | Result |
131| ------------------------ | ------------------------------------------------- | ---------------------------------------------------------------------- | 89| ------------------------ | ------------------------------------------------- | ---------------------------------------------------------------------- |
132| `connect` | `[<user_pubkey>, <optional_secret>, <optional_requested_permissions>]` | "ack" | 90| `connect` | `[<remote-signer-pubkey>, <optional_secret>, <optional_requested_permissions>]` | "ack" OR `<required-secret-value>` |
133| `sign_event` | `[<{kind, content, tags, created_at}>]` | `json_stringified(<signed_event>)` | 91| `sign_event` | `[<{kind, content, tags, created_at}>]` | `json_stringified(<signed_event>)` |
134| `ping` | `[]` | "pong" | 92| `ping` | `[]` | "pong" |
135| `get_relays` | `[]` | `json_stringified({<relay_url>: {read: <boolean>, write: <boolean>}})` | 93| `get_relays` | `[]` | `json_stringified({<relay_url>: {read: <boolean>, write: <boolean>}})` |
@@ -138,11 +96,10 @@ Each of the following are methods that the client sends to the remote signer.
138| `nip04_decrypt` | `[<third_party_pubkey>, <nip04_ciphertext_to_decrypt>]` | `<plaintext>` | 96| `nip04_decrypt` | `[<third_party_pubkey>, <nip04_ciphertext_to_decrypt>]` | `<plaintext>` |
139| `nip44_encrypt` | `[<third_party_pubkey>, <plaintext_to_encrypt>]` | `<nip44_ciphertext>` | 97| `nip44_encrypt` | `[<third_party_pubkey>, <plaintext_to_encrypt>]` | `<nip44_ciphertext>` |
140| `nip44_decrypt` | `[<third_party_pubkey>, <nip44_ciphertext_to_decrypt>]` | `<plaintext>` | 98| `nip44_decrypt` | `[<third_party_pubkey>, <nip44_ciphertext_to_decrypt>]` | `<plaintext>` |
141| `create_account` | `[<username>, <domain>, <optional_email>, <optional_requested_permissions>]` | `<newly_created_user_pubkey>` |
142 99
143### Requested permissions 100### Requested permissions
144 101
145The `connect` method may be provided with `optional_requested_permissions` for user convenience. The permissions are a comma-separated list of `method[:params]`, i.e. `nip04_encrypt,sign_event:4` meaning permissions to call `nip04_encrypt` and to call `sign_event` with `kind:4`. Optional parameter for `sign_event` is the kind number, parameters for other methods are to be defined later. 102The `connect` method may be provided with `optional_requested_permissions` for user convenience. The permissions are a comma-separated list of `method[:params]`, i.e. `nip04_encrypt,sign_event:4` meaning permissions to call `nip04_encrypt` and to call `sign_event` with `kind:4`. Optional parameter for `sign_event` is the kind number, parameters for other methods are to be defined later. Same permission format may be used for `perms` field of `metadata` in `nostrconnect://` string.
146 103
147## Response Events `kind:24133` 104## Response Events `kind:24133`
148 105
@@ -171,54 +128,90 @@ The `content` field is a JSON-RPC-like message that is [NIP-04](04.md) encrypted
171- `results` is a string of the result of the call (this can be either a string or a JSON stringified object) 128- `results` is a string of the result of the call (this can be either a string or a JSON stringified object)
172- `error`, _optionally_, it is an error in string form, if any. Its presence indicates an error with the request. 129- `error`, _optionally_, it is an error in string form, if any. Its presence indicates an error with the request.
173 130
174### Auth Challenges 131## Example flow for signing an event
132
133- `remote-signer-pubkey` is `fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52`
134- `user-pubkey` is also `fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52`
135- `client-pubkey` is `eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86`
175 136
176An Auth Challenge is a response that a remote signer can send back when it needs the user to authenticate via other means. This is currently used in the OAuth-like flow enabled by signers like [Nsecbunker](https://github.com/kind-0/nsecbunkerd/). The response `content` object will take the following form: 137### Signature request
177 138
178```json 139```js
179{ 140{
180 "id": <request_id>, 141 "kind": 24133,
181 "result": "auth_url", 142 "pubkey": "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86",
182 "error": <URL_to_display_to_end_user> 143 "content": nip04({
144 "id": <random_string>,
145 "method": "sign_event",
146 "params": [json_stringified(<{
147 content: "Hello, I'm signing remotely",
148 kind: 1,
149 tags: [],
150 created_at: 1714078911
151 }>)]
152 }),
153 "tags": [["p", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52"]], // p-tags the remote-signer-pubkey
183} 154}
184``` 155```
185 156
186Clients should display (in a popup or new tab) the URL from the `error` field and then subscribe/listen for another response from the remote signer (reusing the same request ID). This event will be sent once the user authenticates in the other window (or will never arrive if the user doesn't authenticate). It's also possible to add a `redirect_uri` url parameter to the auth_url, which is helpful in situations when a client cannot open a new window or tab to display the auth challenge. 157### Response event
187
188#### Example event signing request with auth challenge
189 158
190![signing-example-with-auth-challenge](https://i.nostr.build/W3aj.png) 159```js
160{
161 "kind": 24133,
162 "pubkey": "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52",
163 "content": nip04({
164 "id": <random_string>,
165 "result": json_stringified(<signed-event>)
166 }),
167 "tags": [["p", "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86"]], // p-tags the client-pubkey
168}
169```
191 170
192## Appendix 171### Diagram
193 172
194### NIP-05 Login Flow 173![signing-example](https://i.nostr.build/P3gW.png)
195 174
196Clients might choose to present a more familiar login flow, so users can type a NIP-05 address instead of a `bunker://` string.
197 175
198When the user types a NIP-05 the client: 176## Auth Challenges
199 177
200- Queries the `/.well-known/nostr.json` file from the domain for the NIP-05 address provided to get the user's pubkey (this is the `user-pubkey`) 178An Auth Challenge is a response that a _remote-signer_ can send back when it needs the _user_ to authenticate via other means. The response `content` object will take the following form:
201- In the same `/.well-known/nostr.json` file, queries for the `nip46` key to get the relays that the remote signer will be listening on.
202- Now the client has enough information to send commands to the remote signer on behalf of the user.
203 179
204### OAuth-like Flow 180```json
181{
182 "id": <request_id>,
183 "result": "auth_url",
184 "error": <URL_to_display_to_end_user>
185}
186```
205 187
206#### Remote signer discovery via NIP-89 188_client_ should display (in a popup or new tab) the URL from the `error` field and then subscribe/listen for another response from the _remote-signer_ (reusing the same request ID). This event will be sent once the user authenticates in the other window (or will never arrive if the user doesn't authenticate).
207 189
208In this last case, most often used to facilitate an OAuth-like signin flow, the client first looks for remote signers that have announced themselves via NIP-89 application handler events. 190### Example event signing request with auth challenge
209 191
210First the client will query for `kind: 31990` events that have a `k` tag of `24133`. 192![signing-example-with-auth-challenge](https://i.nostr.build/W3aj.png)
211 193
212These are generally shown to a user, and once the user selects which remote signer to use and provides the `user-pubkey` they want to use (via npub, pubkey, or nip-05 value), the client can initiate a connection. Note that it's on the user to select the _remote-signer_ that is actually managing the `user-keypair` that they would like to use in this case. If the `user-pubkey` is managed on another _remote-signer_ the connection will fail. 194## Appendix
213 195
214In addition, it's important that clients validate that the pubkey of the announced _remote-signer_ matches the pubkey of the `_` entry in the `/.well-known/nostr.json` file of the remote signer's announced domain. 196### Announcing _remote-signer_ metadata
215 197
216Clients that allow users to create new accounts should also consider validating the availability of a given username in the namespace of remote signer's domain by checking the `/.well-known/nostr.json` file for existing usernames. Clients can then show users feedback in the UI before sending a `create_account` event to the remote signer and receiving an error in return. Ideally, remote signers would also respond with understandable error messages if a client tries to create an account with an existing username. 198_remote-signer_ MAY publish it's metadata by using [NIP-05](05.md) and [NIP-89](89.md). With NIP-05, a request to `<remote-signer>/.well-known/nostr.json?name=_` MAY return this:
199```
200{
201 "names":{
202 "_": <remote-signer-app-pubkey>,
203 },
204 "nip46": {
205 "relays": ["wss://relay1","wss://relay2"...],
206 "nostrconnect_url": "https://remote-signer-domain.com/<nostrconnect>"
207 }
208}
209```
217 210
218#### Example Oauth-like flow to create a new user account with Nsecbunker 211The `<remote-signer-app-pubkey>` MAY be used to verify the domain from _remote-signer_'s NIP-89 event (see below). `relays` SHOULD be used to construct a more precise `nostrconnect://` string for the specific `remote-signer`. `nostrconnect_url` template MAY be used to redirect users to _remote-signer_'s connection flow by replacing `<nostrconnect>` placeholder with an actual `nostrconnect://` string.
219 212
220Coming soon... 213### Remote signer discovery via NIP-89
221 214
222## References 215_remote-signer_ MAY publish a NIP-89 `kind: 31990` event with `k` tag of `24133`, which MAY also include one or more `relay` tags and MAY include `nostrconnect_url` tag. The semantics of `relay` and `nostrconnect_url` tags are the same as in the section above.
223 216
224- [NIP-04 - Encryption](04.md) 217_client_ MAY improve UX by discovering _remote-signers_ using their `kind: 31990` events. _client_ MAY then pre-generate `nostrconnect://` strings for the _remote-signers_, and SHOULD in that case verify that `kind: 31990` event's author is mentioned in signer's `nostr.json?name=_` file as `<remote-signer-app-pubkey>`.