mirror of
https://github.com/nostr-protocol/nips.git
synced 2024-12-22 16:35:52 -05:00
Merge pull request #831 from nostr-protocol/nostr-connect
rewrite NIP-46
This commit is contained in:
commit
09f8244e6f
192
46.md
192
46.md
|
@ -2,161 +2,97 @@ NIP-46
|
||||||
======
|
======
|
||||||
|
|
||||||
Nostr Connect
|
Nostr Connect
|
||||||
------------------------
|
-------------
|
||||||
|
|
||||||
`draft` `optional`
|
`draft` `optional`
|
||||||
|
|
||||||
## Rationale
|
This NIP describes a method for 2-way communication between a **remote signer** and a normal Nostr client. The remote signer could be, for example, a hardware device dedicated to signing Nostr events, while the client is a normal Nostr client.
|
||||||
|
|
||||||
Private keys should be exposed to as few systems - apps, operating systems, devices - as possible as each system adds to the attack surface.
|
## Signer Discovery
|
||||||
|
|
||||||
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.
|
The client always starts by generating a random key which is used to communicate with the signer, then it one of the methods below is used to allow the client to know what is the signer public key for the session and which relays to use.
|
||||||
|
|
||||||
|
### Started by the signer (nsecBunker)
|
||||||
|
|
||||||
## Terms
|
The remote signer generates a connection token in the form
|
||||||
|
|
||||||
* **App**: Nostr app on any platform that *requires* to act on behalf of a nostr account.
|
```
|
||||||
* **Signer**: Nostr app that holds the private key of a nostr account and *can sign* on its behalf.
|
<npub1...>#<optional-secret>?relay=wss://...&relay=wss://...
|
||||||
|
```
|
||||||
|
|
||||||
|
The user copies that token and pastes it in the client UI somehow. Then the client can send events of kind `24133` to the specified relays and wait for responses from the remote signer.
|
||||||
|
|
||||||
## `TL;DR`
|
### Started by the client
|
||||||
|
|
||||||
|
The client generates a QR code in the following form (URL-encoded):
|
||||||
|
|
||||||
**App** and **Signer** sends ephemeral encrypted messages to each other using kind `24133`, using a relay of choice.
|
```
|
||||||
|
nostrconnect://<client-key-hex>?relay=wss://...&metadata={"name":"...", "url": "...", "description": "..."}
|
||||||
|
```
|
||||||
|
|
||||||
App prompts the Signer to do things such as fetching the public key or signing events.
|
The signer scans the QR code and sends a `connect` message to the client in the specified relays.
|
||||||
|
|
||||||
The `content` field must be an encrypted JSONRPC-ish **request** or **response**.
|
## Event payloads
|
||||||
|
|
||||||
## Signer Protocol
|
Event payloads are [NIP-04](04.md)-encrypted JSON blobs that look like JSONRPC messages (their format is specified inside the `.content` of the event formats nelow).
|
||||||
|
|
||||||
### Messages
|
Events sent by the client to the remote signer have the following format:
|
||||||
|
|
||||||
#### Request
|
```js
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
{
|
||||||
"id": <random_string>,
|
"pubkey": "<client-key-hex>"
|
||||||
"method": <one_of_the_methods>,
|
"kind": 24133,
|
||||||
"params": [<anything>, <else>]
|
"tags": [
|
||||||
|
["p", "<signer-key-hex>"]
|
||||||
|
],
|
||||||
|
"content": "nip04_encrypted_json({id: <random-string>, method: <see-below>, params: [array_of_strings]})",
|
||||||
|
...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Response
|
And the events the remote signer sends to the client have the following format:
|
||||||
|
|
||||||
```json
|
```js
|
||||||
{
|
"pubkey": "<signer-key-hex>"
|
||||||
"id": <request_id>,
|
"kind": 24133,
|
||||||
"result": <anything>,
|
"tags": [
|
||||||
"error": <reason>
|
["p", "<client-key-hex>"]
|
||||||
}
|
],
|
||||||
|
"content": "nip04_encrypted_json({id: <request-id>, result: <string>, error: <reason-string>})",
|
||||||
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The signer key will always be the key of the user who controls the signer device.
|
||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
|
|
||||||
|
|
||||||
#### Mandatory
|
|
||||||
|
|
||||||
These are mandatory methods the remote signer app MUST implement:
|
|
||||||
|
|
||||||
- **describe**
|
|
||||||
- params []
|
|
||||||
- result `["describe", "get_public_key", "sign_event", "connect", "disconnect", "delegate", ...]`
|
|
||||||
- **get_public_key**
|
|
||||||
- params []
|
|
||||||
- result `pubkey`
|
|
||||||
- **sign_event**
|
|
||||||
- params [`event`]
|
|
||||||
- result `event_with_signature`
|
|
||||||
|
|
||||||
#### optional
|
|
||||||
|
|
||||||
|
|
||||||
- **connect**
|
- **connect**
|
||||||
- params [`pubkey`]
|
- params: [`pubkey`, `secret`]
|
||||||
- **disconnect**
|
- result: `"ack"`
|
||||||
- params []
|
- **get_public_key**
|
||||||
- **delegate**
|
- params: []
|
||||||
- params [`delegatee`, `{ kind: number, since: number, until: number }`]
|
- result: `pubkey-hex`
|
||||||
- result `{ from: string, to: string, cond: string, sig: string }`
|
- **sign_event**
|
||||||
|
- params: [`event`]
|
||||||
|
- result: `json_string(event_with_pubkey_id_and_signature)`
|
||||||
- **get_relays**
|
- **get_relays**
|
||||||
- params []
|
- params: []
|
||||||
- result `{ [url: string]: {read: boolean, write: boolean} }`
|
- result: `json_string({[url: string]: {read: boolean, write: boolean}})`
|
||||||
- **nip04_encrypt**
|
- **nip04_encrypt**
|
||||||
- params [`pubkey`, `plaintext`]
|
- params: [`third-party-pubkey`, `plaintext`]
|
||||||
- result `nip4 ciphertext`
|
- result: `nip04-ciphertext`
|
||||||
- **nip04_decrypt**
|
- **nip04_decrypt**
|
||||||
- params [`pubkey`, `nip4 ciphertext`]
|
- params: [`third-party-pubkey`, `nip04-ciphertext`]
|
||||||
- result [`plaintext`]
|
- result: `plaintext`
|
||||||
|
- **nip44_get_key**
|
||||||
|
- params: [`third-party-pubkey`]
|
||||||
NOTICE: `pubkey` and `signature` are hex-encoded strings.
|
- result: `nip44-conversation-key`
|
||||||
|
- **nip44_encrypt**
|
||||||
|
- params: [`third-party-pubkey`, `plaintext`]
|
||||||
### Nostr Connect URI
|
- result: `nip44-ciphertext`
|
||||||
|
- **nip44_decrypt**
|
||||||
**Signer** discovers **App** by scanning a QR code, clicking on a deep link or copy-pasting an URI.
|
- params: [`third-party-pubkey`, `nip44-ciphertext`]
|
||||||
|
- result: `plaintext`
|
||||||
The **App** generates a special URI with prefix `nostrconnect://` and base path the hex-encoded `pubkey` with the following querystring parameters **URL encoded**
|
- **ping**
|
||||||
|
- params: []
|
||||||
- `relay` URL of the relay of choice where the **App** is connected and the **Signer** must send and listen for messages.
|
- result: `"pong"`
|
||||||
- `metadata` metadata JSON of the **App**
|
|
||||||
- `name` human-readable name of the **App**
|
|
||||||
- `url` (optional) URL of the website requesting the connection
|
|
||||||
- `description` (optional) description of the **App**
|
|
||||||
- `icons` (optional) array of URLs for icons of the **App**.
|
|
||||||
|
|
||||||
#### JavaScript
|
|
||||||
|
|
||||||
```js
|
|
||||||
const uri = `nostrconnect://<pubkey>?relay=${encodeURIComponent("wss://relay.damus.io")}&metadata=${encodeURIComponent(JSON.stringify({"name": "Example"}))}`
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
```sh
|
|
||||||
nostrconnect://b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558e9d4?relay=wss%3A%2F%2Frelay.damus.io&metadata=%7B%22name%22%3A%22Example%22%7D
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Flows
|
|
||||||
|
|
||||||
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`.
|
|
||||||
|
|
||||||
### Connect
|
|
||||||
|
|
||||||
1. User clicks on **"Connect"** button on a website or scan it with a QR code
|
|
||||||
2. It will show an URI to open a "nostr connect" enabled **Signer**
|
|
||||||
3. In the URI there is a pubkey of the **App** ie. `nostrconnect://<pubkey>&relay=<relay>&metadata=<metadata>`
|
|
||||||
4. The **Signer** will send a message to ACK the `connect` request, along with his public key
|
|
||||||
|
|
||||||
### Disconnect (from App)
|
|
||||||
|
|
||||||
1. User clicks on **"Disconnect"** button on the **App**
|
|
||||||
2. The **App** will send a message to the **Signer** with a `disconnect` request
|
|
||||||
3. The **Signer** will send a message to ACK the `disconnect` request
|
|
||||||
|
|
||||||
### Disconnect (from Signer)
|
|
||||||
|
|
||||||
1. User clicks on **"Disconnect"** button on the **Signer**
|
|
||||||
2. The **Signer** will send a message to the **App** with a `disconnect` request
|
|
||||||
|
|
||||||
|
|
||||||
### Get Public Key
|
|
||||||
|
|
||||||
1. The **App** will send a message to the **Signer** with a `get_public_key` request
|
|
||||||
3. The **Signer** will send back a message with the public key as a response to the `get_public_key` request
|
|
||||||
|
|
||||||
### Sign Event
|
|
||||||
|
|
||||||
1. The **App** will send a message to the **Signer** with a `sign_event` request along with the **event** to be signed
|
|
||||||
2. The **Signer** will show a popup to the user to inspect the event and sign it
|
|
||||||
3. The **Signer** will send back a message with the event including the `id` and the schnorr `signature` as a response to the `sign_event` request
|
|
||||||
|
|
||||||
### Delegate
|
|
||||||
|
|
||||||
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.
|
|
||||||
2. The **Signer** will show a popup to the user to delegate the **App** to sign on his behalf
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user