nostr-protocol / nips

Nostr Implementation Possibilities
2.33k stars 564 forks source link

NIP-XX: Passwordless Authentication #558

Open cryptoquick opened 1 year ago

cryptoquick commented 1 year ago

I'd like to follow the process outlined in #545.

NIP-XX

Passwordless Authentication

Problem

We're launching an platform for unique digital assets, it's not a fancy dapp or nothin, just uses Next.js and a MongoDB. I think our users are fine with this, but a number of users have requested not to use email auth. The thing is, we'd still like to communicate with them, and we were hoping Nostr could be a viable alternative as both a sort of email address alternative, and also as a way to generate passwords specific to each site using your Nostr key. We intend to build this into the BitMask extension, along with NIP-07 authentication, though ideally other browser extension adopt this also, and maybe even mobile clients with Nostr-enabled webviews.

Potential Solution

The server will have a private key which will serve as its means to authenticate, and also to communicate with users via encrypted DMs.

A shared secret could be generated using ECDH derived from the website's NIP-05 public key and a private key available only to the user.

The 32-byte hex-encoded public key could then be provided as the username, and the shared secret would be used as the password within the Basic Authorization header.

When the server sees this on the request header, it computes an ECDH shared secret using the user's provided public key as the username, which should match the shared secret provided as the password. This then does not need to be persisted within a database, which helps against hacks, is entirely serverless, and also obviates the entire business model used by Silicon Valley tech startups like Auth0 and Okta.

A convention would be that "admin" would be the username used by websites, though perhaps this could be specified through some other way, either via meta tags, or a dictionary of popular administrative usernames could be utilized, though that might result in unexpected behavior in some cases, such as a user registering as "admin" or "webmaster" and if they wind up in the NIP-05 JSON somehow, that could result in mistaken identity. But this is a relatively contrived scenario. Perhaps instead, a character like "@" could be used to indicate the webmaster username, and so first the key "@" is looked up, and let's say the value is "admin", and then the key for the NIP-05 username "admin" is used as the canonical site key.

This same information could also be used to end-to-end encrypt data at-rest in a way that is relatively opaque to cloud hosts, especially if using something like object storage from a different cloud provider.

In case the private key on this centralized server is ever compromised, its NIP-05 identifier could be updated once it's secured again, and users will authenticate for their old data using new passwords generated from the new NIP-05. This will then update all communications. Perhaps in Nostr email clients, they could request the NIP-05 id to validate the sender.

Also, I'm unsure if ECDH can be done using the x-only public keys used by Nostr as defined in NIP-01. If this is the case, when the server goes to validate a shared secret password, both a lower and upper s value will need to be generated, and shared secrets created from both. If one matches, the password is considered valid.

Browser extensions that support NIP-07 could also support this NIP to authenticate with sites that have a NIP-05 identifier available.

Additionally, to reduce the number of calls, perhaps a meta tag could be embedded within the HTML page, such as this: <meta name="nostr-nip-05" content="admin:deadbeefdeadbeefdeadbeefdeadbeef">

Another potential solution could be something like NIP-42, but that's more similar in concept to a JWT, which is good for API requests, but way fancier than we need.

Draft

No draft yet, but perhaps I could request a NIP number and a suggested kind? I'll draft a NIP tomorrow if this is well-received by the community. Any suggestions are welcome, this would be my first NIP.

cmdruid commented 1 year ago

I do this with crypto-sessions and ECDH is a pretty sensible way to go about it. My suggestions:

cryptoquick commented 1 year ago

@cmdruid For the first point, is NIP-05's .well-known/nostr.json file sufficient? I'm not sure what problem key rotation solves.

As for the suggestion for hashing shared secrets, I agree, that's a good idea. This can actually solve a number of problems. An Argon2id digest could be made from a floor(timestamp in seconds / 30), in addition to the shared secret, creating essentially a time-based HMAC that rotates every 30 seconds, which prevents replay attacks. A hash for this 30 second interval and also the next could be made for verification to account for potential overlap. The time isn't shared, just synchronized by clocks on both client and server, similar to TOTP.

Would that make sense to do? I have a feeling we're targeting several different Silicon Valley business models with just this one NIP. Tech billionaires hate that!

cmdruid commented 1 year ago

@cmdruid For the first point, is NIP-05's .well-known/nostr.json file sufficient? I'm not sure what problem key rotation solves.

If your key gets compromised, or you want to rotate it for forward secrecy, then array of keys is the way to go. New sessions would use the latest key, but existing sessions can still validate (until the key expires).

Would that make sense to do? I have a feeling we're targeting several different Silicon Valley business models with just this one NIP. Tech billionaires hate that!

Replay attacks aren't a big problem if your API is solid. But you could simply wrap every POST request in a nostr note. You get a free timestamp, plus all the security advantages, plus nip7 support, and its easy to throw in nip4 encryption as well.

ghost commented 1 year ago

Replay attacks aren't a big problem if your API is solid. But you could simply wrap every POST request in a nostr note. You get a free timestamp, plus all the security advantages, plus nip7 support, and its easy to throw in nip4 encryption as well.

I wouldn't use NIP-4 as the encryption uses AES-256-CBC (imo not the best) and there are plans to upgrade it (seeing from the conversation, XChaCha will be picked). Since the encrypted posts don't really need to be read by other nostr clients, why not use a stronger cipher/mode? NIP-4 says it SHOULD be AES-256-CBC encrypted, but imo they just serve as guidelines and not as actual rules.

I implemented an encrypted 1 on 1 chat app using the EncryptedDirectMessage kind, but I used ChaCha20Poly1305 and not AES.

Just know that NIP-4 also has metadata issues, so keep that in mind

Semisol commented 1 year ago

There is an HTTP auth NIP. Use that to make a request to get a long lived token.

cmdruid commented 1 year ago

Since the encrypted posts don't really need to be read by other nostr clients, why not use a stronger cipher/mode?

It's really up to whether you have access to the private key. NIP-7 extensions currently support NIP4 encryption. Any big changes to encryption means existing extensions have to update, or you onboard people to a new extension.

ghost commented 1 year ago

Since the encrypted posts don't really need to be read by other nostr clients, why not use a stronger cipher/mode?

It's really up to whether you have access to the private key. NIP-7 extensions currently support NIP4 encryption. Any big changes to encryption means existing extensions have to update, or you onboard people to a new extension.

I mean, if the network doesn't want interoperability with other Nostr clients what's there to stop them?

earonesty commented 1 year ago

this is a very simple problem to solve without a lot of moving parts or complexity, imo, no "server" needed:

we need a standard protocol handler:

nkey:// for signing stuff with your nostr identity, again with the "associated favorite signing app"

for the right to post

you can use nkey://delegate?uri=app-uri&...params... to sign app delegations in an oauth-style flow ( i can detail this, but its not hard to figure out). this allows the app to "post as you" using NIP26 delegations (no access is given to the main privkey)

then you can have one app custody the main key, and no other app needs the main key

you can also revoke delegations as needed.

if your app doesn't need delegation, and just wants to log someone in:

you can use nkey://auth?uri=app-uri&...params... to sign a nonce, prove ownership, and provide the npub

without a protocol handler, we're always reduced to weird flows and insecure custody. with a handler, you can have anything custody a key... from a hardware system to an indexdb - you choose how secure you want it!

earonesty commented 1 year ago

here: https://github.com/nostr-protocol/nips/issues/581