diff options
| author | Marco Argentieri <3596602+tiero@users.noreply.github.com> | 2023-02-20 20:26:13 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-02-20 16:26:13 -0300 |
| commit | b1a5ad355a8b376170471a41817d8722ba7443b1 (patch) | |
| tree | 3e8ac83e12cf82fc6b4901b02db91fa7a1f0ec46 /46.md | |
| parent | 524caa38563f49fc2aacbbe48b212fde0f24f97e (diff) | |
NIP-46: Nostr Connect 🔌 connect your Nostr app with remote signing devices (#153)
Diffstat (limited to '46.md')
| -rw-r--r-- | 46.md | 162 |
1 files changed, 162 insertions, 0 deletions
| @@ -0,0 +1,162 @@ | |||
| 1 | NIP-46 | ||
| 2 | ====== | ||
| 3 | |||
| 4 | Nostr Connect | ||
| 5 | ------------------------ | ||
| 6 | |||
| 7 | `draft` `optional` `author:tiero` `author:giowe` `author:vforvalerio87` | ||
| 8 | |||
| 9 | ## Rationale | ||
| 10 | |||
| 11 | Private keys should be exposed to as few systems - apps, operating systems, devices - as possible as each system adds to the attack surface. | ||
| 12 | |||
| 13 | Entering private keys can also be annoying and requires exposing them to even more systems such as the operating system's clipboard that might be monitored by malicious apps. | ||
| 14 | |||
| 15 | |||
| 16 | ## Terms | ||
| 17 | |||
| 18 | * **App**: Nostr app on any platform that *requires* to act on behalf of a nostr account. | ||
| 19 | * **Signer**: Nostr app that holds the private key of a nostr account and *can sign* on its behalf. | ||
| 20 | |||
| 21 | |||
| 22 | ## `TL;DR` | ||
| 23 | |||
| 24 | |||
| 25 | **App** and **Signer** sends ephemeral encrypted messages to each other using kind `24133`, using a relay of choice. | ||
| 26 | |||
| 27 | App prompts the Signer to do things such as fetching the public key or signing events. | ||
| 28 | |||
| 29 | The `content` field must be an encrypted JSONRPC-ish **request** or **response**. | ||
| 30 | |||
| 31 | ## Signer Protocol | ||
| 32 | |||
| 33 | ### Messages | ||
| 34 | |||
| 35 | #### Request | ||
| 36 | |||
| 37 | ```json | ||
| 38 | { | ||
| 39 | "id": <random_string>, | ||
| 40 | "method": <one_of_the_methods>, | ||
| 41 | "params": [<anything>, <else>] | ||
| 42 | } | ||
| 43 | ``` | ||
| 44 | |||
| 45 | #### Response | ||
| 46 | |||
| 47 | ```json | ||
| 48 | { | ||
| 49 | "id": <request_id>, | ||
| 50 | "result": <anything>, | ||
| 51 | "error": <reason> | ||
| 52 | } | ||
| 53 | ``` | ||
| 54 | |||
| 55 | ### Methods | ||
| 56 | |||
| 57 | |||
| 58 | #### Mandatory | ||
| 59 | |||
| 60 | These are mandatory methods the remote signer app MUST implement: | ||
| 61 | |||
| 62 | - **describe** | ||
| 63 | - params [] | ||
| 64 | - result `{"get_public_key": { params: [], result: anything }}` | ||
| 65 | - **get_public_key** | ||
| 66 | - params [] | ||
| 67 | - result `pubkey` | ||
| 68 | - **sign_event** | ||
| 69 | - params [`event`] | ||
| 70 | - result `signature` | ||
| 71 | |||
| 72 | #### optional | ||
| 73 | |||
| 74 | |||
| 75 | - **connect** | ||
| 76 | - params [`pubkey`] | ||
| 77 | - **disconnect** | ||
| 78 | - params [] | ||
| 79 | - **delegate** | ||
| 80 | - params [`pubkey`, `conditions query string`] | ||
| 81 | - result `nip26 delegation token` | ||
| 82 | - **get_relays** | ||
| 83 | - params [] | ||
| 84 | - result `{ [url: string]: {read: boolean, write: boolean} }` | ||
| 85 | - **nip04_encrypt** | ||
| 86 | - params [`pubkey`, `plaintext`] | ||
| 87 | - result `nip4 ciphertext` | ||
| 88 | - **nip04_decrypt** | ||
| 89 | - params [`pubkey`, `nip4 ciphertext`] | ||
| 90 | - result [`plaintext`] | ||
| 91 | |||
| 92 | |||
| 93 | NOTICE: `pubkey` and `signature` are hex-encoded strings. | ||
| 94 | |||
| 95 | |||
| 96 | ### Nostr Connect URI | ||
| 97 | |||
| 98 | **Signer** discovers **App** by scanning a QR code, clicking on a deep link or copy-pasting an URI. | ||
| 99 | |||
| 100 | The **App** generates a special URI with prefix `nostrconnect://` and base path the hex-encoded `pubkey` with the following querystring parameters **URL encoded** | ||
| 101 | |||
| 102 | - `relay` URL of the relay of choice where the **App** is connected and the **Signer** must send and listen for messages. | ||
| 103 | - `metadata` metadata JSON of the **App** | ||
| 104 | - `name` human-readable name of the **App** | ||
| 105 | - `url` (optional) URL of the website requesting the connection | ||
| 106 | - `description` (optional) description of the **App** | ||
| 107 | - `icons` (optional) array of URLs for icons of the **App**. | ||
| 108 | |||
| 109 | #### JavaScript | ||
| 110 | |||
| 111 | ```js | ||
| 112 | const uri = `nostrconnect://<pubkey>?relay=${encodeURIComponent("wss://relay.damus.io")}&metadata=${encodeURIComponent(JSON.stringify({"name": "Example"}))}` | ||
| 113 | ``` | ||
| 114 | |||
| 115 | #### Example | ||
| 116 | ```sh | ||
| 117 | nostrconnect://b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558e9d4?relay=wss%3A%2F%2Frelay.damus.io&metadata=%7B%22name%22%3A%22Example%22%7D | ||
| 118 | ``` | ||
| 119 | |||
| 120 | |||
| 121 | |||
| 122 | ## Flows | ||
| 123 | |||
| 124 | The `content` field contains encrypted message as specified by [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md). The `kind` chosen is `24133`. | ||
| 125 | |||
| 126 | ### Connect | ||
| 127 | |||
| 128 | 1. User clicks on **"Connect"** button on a website or scan it with a QR code | ||
| 129 | 2. It will show an URI to open a "nostr connect" enabled **Signer** | ||
| 130 | 3. In the URI there is a pubkey of the **App** ie. `nostrconnect://<pubkey>&relay=<relay>&metadata=<metadata>` | ||
| 131 | 4. The **Signer** will send a message to ACK the `connect` request, along with his public key | ||
| 132 | |||
| 133 | ### Disconnect (from App) | ||
| 134 | |||
| 135 | 1. User clicks on **"Disconnect"** button on the **App** | ||
| 136 | 2. The **App** will send a message to the **Signer** with a `disconnect` request | ||
| 137 | 3. The **Signer** will send a message to ACK the `disconnect` request | ||
| 138 | |||
| 139 | ### Disconnect (from Signer) | ||
| 140 | |||
| 141 | 1. User clicks on **"Disconnect"** button on the **Signer** | ||
| 142 | 2. The **Signer** will send a message to the **App** with a `disconnect` request | ||
| 143 | |||
| 144 | |||
| 145 | ### Get Public Key | ||
| 146 | |||
| 147 | 1. The **App** will send a message to the **Signer** with a `get_public_key` request | ||
| 148 | 3. The **Signer** will send back a message with the public key as a response to the `get_public_key` request | ||
| 149 | |||
| 150 | ### Sign Event | ||
| 151 | |||
| 152 | 1. The **App** will send a message to the **Signer** with a `sign_event` request along with the **event** to be signed | ||
| 153 | 2. The **Signer** will show a popup to the user to inspect the event and sign it | ||
| 154 | 3. The **Signer** will send back a message with the schnorr `signature` of the event as a response to the `sign_event` request | ||
| 155 | |||
| 156 | ### Delegate | ||
| 157 | |||
| 158 | 1. The **App** will send a message with metadata to the **Signer** with a `delegate` request along with the **conditions** query string and the **pubkey** of the **App** to be delegated. | ||
| 159 | 2. The **Signer** will show a popup to the user to delegate the **App** to sign on his behalf | ||
| 160 | 3. The **Signer** will send back a message with the signed [NIP-26 delegation token](https://github.com/nostr-protocol/nips/blob/master/26.md) or reject it | ||
| 161 | |||
| 162 | |||