-
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
Conversation
Who else is implementing chat stuff so I can tag them here? @ArcadeCityMayor |
Im for this nip 100%. I believe that a simple method to auth (prove ownership of keys) was missing from the protocol. I think this solves that as well as lends the ability for a relay to restrict access to events. Of course the relay still has access, but it's the clients choice to store where they see fit. This gives me more hope for Seer :) |
Concept ACK |
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.
concept ACK, preferably do not merge until a working relay and client is released
Sorry to be a late entrant to the conversation. What is the rationale behind the combination of (~10 minute time window, URL of relay in content), versus a traditional challenge-response? Challenge-response requires current possession of the key, whereas an unlimited number of these authentication messages could be pre-generated with temporary possession of a key (admittedly less likely than simply exfiltrating the key itself). |
Challenge-response is harder to implement and there are no benefits whatsoever, or at least I am not aware of any. What are the benefits? "temporary possession of a key", as you noticed yourself, is not a real thing that exists. |
Challenge + Relay URL solves that problem. |
That would be a malicious client. |
|
Sorry, misclicked! |
I don't follow. Do you think relays should be simple and clients complex or vice-versa? I'm just curious, because regardless of your answer, I don't think keeping track challenges issued by the relay to each client is less complex than keeping track of used challenges in the last 10 minutes, or am I wrong? To keep track of used events one can just use a global like var challenges = []
// cleanup stuff every 3 minutes
setInterval(() => {
challenges = challenges.slice(challenges.findIndex(([d]) => d < Date.now() - 60 * 10 * 1000))
}, 60 * 3 * 1000)
// then when a client sends an AUTH message
let challenge = authEvent.tags.find(([t, v]) => t === 'challenge')[1]
if (challenges.find(([_, c]) => c === challenge)) throw new Error('bad auth: reused challenge')
challenges.push([Date.now(), challenge]) Now to keep track of challenges issued to each client it would be something like var challenges = {}
// when client connects
let challenge = somehowGenerateRandomString()
challenges[ws] = challenge
// then when client sends an AUTH message
let challenge = authEvent.tags.find(([t, v]) => t === 'challenge')[1]
let expected = challenges[ws]
if (!expected || challenge !== expected) throw new Error('bad auth: reused challenge')
// then when client disconnects
ws.on('close', () => {
delete challenges[ws]
}) |
What if your relay is multi-process? What happened to the idea of keeping the challenge scoped to the websocket connection? There's no need to remember "used challenges". The relay just needs to keep track of one challenge per connection. For instance, my python relay is issuing challenges in the form of '<client_ip_address>:<random 4 bytes>'. (I could easily switch to sending a hashed challenge with secret) |
@fiatjaf this wouldn't work on Nostream since new connections are handled by a different process and the memory isn't shared. The challenges would have to be cached using Redis or on a primary process and it could lead to race conditions if locks are not used. |
You mean each connection has a different process? Don't you already use Redis in this case? With Redis this is much easier. Just save the challenge on Redis and make it expire after 10 minutes. |
Then you store the used challenges on Redis and that is super easy.
This is for the version of this NIP that doesn't require the relay to send a challenge to the client. |
Nostream would need to use a lock to make sure only one authentication attempt can use the challenge (which is now a shared resource) so it will slow down considerably. If locks are not used then a race condition is theoretically possible where more than 1 client can auth if timed correctly. |
... each socket has its own challenge and auth state. there is no one challenge that is shared across all clients |
You're getting it wrong. Relay A could connect to relay B, get a challenge, send that to you, and you sign it, which then allows relay A to impersonate you on relay B |
True, and that would be a malicious relay... So, keep the relay domain in the tags, as the current proposal states. |
I'm responding to Fiatjaf's suggestion on caching challenges so they are not re-used, it introduces a "double-spend" problem that needs solving. If the challenge is per socket we don't need to check any cache, cause every connection has it's own challenge. |
I'm going to merge this if no one objects until tomorrow. (Except @Semisol's objections that we must have a way for the client to initiate the process, which I don't think is reasonable as I think I've explained before and it can always be added if it proves necessary.) |
It's just a handler for the AUTH command that starts the authentication flow. It's simple so why not have it in the initial spec? |
I'll support it. It's good for testing as well. |
"Why not?" is the source of all protocol bloat. |
This NIP could be expanded to let relays reject creating events from a pubkey you didn't authenticate with. Currently nothing prevents me from taking someone else's signed event from one relay and putting it onto another relay. |
@alexgleason I wouldn't put this restriction on for several reasons:
In general, if you speak out publicly, you should expect and assume others will hear your voice. In this case, the voice just takes the shape of this Event object. |
They can already do this if they want. It's a relay policy. |
Readable: https://github.com/nostr-protocol/nips/blob/auth/42.md