-
Notifications
You must be signed in to change notification settings - Fork 588
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add nip-42: authentication #141
Changes from 8 commits
82aafbe
a04da3f
df28376
b9467cb
c80be21
4a52026
50facee
6a70967
6074116
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,86 @@ | ||||||||||||||||||||||
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. | ||||||||||||||||||||||
|
||||||||||||||||||||||
## Definitions | ||||||||||||||||||||||
|
||||||||||||||||||||||
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", <challenge-string>] | ||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
And, when sent by clients, of the following form: | ||||||||||||||||||||||
|
||||||||||||||||||||||
``` | ||||||||||||||||||||||
["AUTH", <signed-event-json>] | ||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
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: | ||||||||||||||||||||||
Giszmo marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
|
||||||||||||||||||||||
```json | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
"id": "...", | ||||||||||||||||||||||
"pubkey": "...", | ||||||||||||||||||||||
"created_at": 1669695536, | ||||||||||||||||||||||
"kind": 22242, | ||||||||||||||||||||||
"tags": [ | ||||||||||||||||||||||
["relay", "wss://relay.example.com/"], | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, otherwise one malicious relay can reuse your auth event with it to login on a different relay. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this a reply to? My suggestion to drop the commitment to a relay name? If your relay doesn't re-use challenge nonces, it will associate each nonce with only one websocket connection ever. That websocket connection implicitly commits to the relay already, so naming the relay is not necessary. |
||||||||||||||||||||||
["challenge", "challengestringhere"] | ||||||||||||||||||||||
], | ||||||||||||||||||||||
"content": "", | ||||||||||||||||||||||
"sig": "..." | ||||||||||||||||||||||
} | ||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A proposal for what @Semisol suggested.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with this. This shouldn't reset the authentication of the socket though, that could be done through There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nevermind, that makes it simpler and I get it now There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
## 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 | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
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` 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?"] | ||||||||||||||||||||||
``` | ||||||||||||||||||||||
|
||||||||||||||||||||||
or it can return an `OK` message noting the reason an event was not written using the same prefix: | ||||||||||||||||||||||
|
||||||||||||||||||||||
``` | ||||||||||||||||||||||
["OK", <event-id>, false, "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 `created_at` is close (e.g. within ~10 minutes) of the current time; | ||||||||||||||||||||||
- 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. | ||||||||||||||||||||||
Giszmo marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need a way to client initiate it. An AUTH command with no arguments could make the relay send this and normal flow follows
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can add that later if necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I would implement the challenge upon a socket being established. If the client somehow lost the nonce, it could always re-connect.
The nip is vague about when the relay might challenge indeed. Also a client might want to switch authentication, so while authed as Alice it might want to switch to auth as Bob now, so both for un-authed and authed clients there is a need to request a new nonce. Semisol's empty AUTH message triggering this has my support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree and it's so simple to implement we should try to have it from the start.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is unnecessary bloat. How does that work on the relay side? The relay is emitting events for identity A at a given websocket, then that websocket changes to identity B, now the relay has to shove that inside the code it might have for handling the subscriptions, maybe cancel a subscription that was valid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The client should just open a new websocket if it wants a new identity -- which is also an edge case I see no use for and we shouldn't be complicating this for 99.9% of the cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is because it is irrelevant. All clients would just set up a listener like
when("AUTH") => send("AUTH")
and be done with it. Most relays will send theAUTH
challenge when a connection is established, but why specify that if it changes nothing on the code side (the client has to wait anyway)?Also, if you think the client sending an empty
AUTH
request fixes it, consider that the relay may still take 5 minutes to reply to that with a challenge -- or may never reply, so really nothing is changed.