Nostr Connect (NIP46) is a remote key access protocol.
If you need continuous access to users' Nostr keys, you have these options:
- store private keys in your app (please don't do that!)
- use
window.nostr
(NIP07), which is only available for web apps with a browser extension installed - use Amber or Keystache or other platform-specific solution
- use Nostr Connect - a remote key access protocol that works on most platforms and is gaining wide adoption
This guide is for you if you want to implement Nostr Connect.
If you are looking for a more general login with Nostr guide, check out nostrlogin.org
Before we start, to test your Nostr Connect client you should get yourself a server first - try nsec.app or nsecbunker.com or Gossip nostr client.
Now, if you're building a web app, the best place to start would be nostr-login or window.nostr.js.
These are drop-in scripts that provide the UI for users to log in with Nostr Connect, while exposing a window.nostr
object for your app to work with.
It super easy!
Just call signEvent
and other methods on the window.nostr
object and these libs will handle the rest.
Because there's actually a lot to handle...
If you just want your own Nostr Connect UI you should probably use NDK or nostr-tools@v2 to avoid implementing the full nip46 protocol yourself.
However, even with one of these libraries, the flow will be quite complex:
- ask the user for their
bunker URL
ornip05
address - show a Loading state to the user when they click Login, establishing a connection will take a while
- if
nip05
address is entered, fetch the nostr.json info to get remote user pubkey and thebunker relays
(that'squeryBunkerProfile
withnostr-tools
, withNDK
you just supply thenip05
to theNDKNip46Signer
) - if bunker URL is entered, parse it properly to get the
remote user pubkey
,bunker relays
and an optionalsecret
token (example #1, #2) (that'sparseBunkerInput
withnostr-tool
, withNDK
you have to do that yourself) - make sure you connect to the specified bunker relays (
NDK
) - generate the Local keypair, it will be used to sign nip46 requests to the
remote user pubkey
- create a nip46 client and supply the
local keypair privkey
and other params to it, that'sBunkerSigner
withnostr-tools
orNDKNip46Signer
withNDK
- subscribe to
auth_url
messages, that'sonauth
callback withnostr-tools
orauthUrl
event withNDKNip46Signer
- these are needed for users to confirm the actions you're requesting from their keys - send a
connect
request, that'sBunkerSigner.connect
withnostr-tools
orNDKNip46Signer.blockUntilReady
withNDK
- if
auth_url
message arrives, you are supposed to open a popup browser tab with the provided URL - show users a Please confirm button and only open the popup when they click it, opening without direct user action will get your popups blocked in many browsers
- the popup tabs are supposed to be closed automatically when user finishes interacting with them, you don't have to do that yourself
- when
connect
is finished, your connection is established and you're ready to make other methods calls - save the
local keypair
and other params in persistent storage, reuse them next time your app starts
Wow, we made it!
Well hopefully we didn't miss anything and it actually worked.
Here is what happens next:
- you send
sign_event
or other requests using the nip46 client auth_url
messages might arrive if key storage app needs a confirmation from the user- you should show a non-modal notification to the user saying Please confirm
- when user clicks, open the URL provided by
auth_url
message, opening it without a click will get your popup blocked and users irritated - the popup will be auto-closed, no need to watch it
Cool, we're accessing those keys now!
Sign up differs from login in that there is no remote user pubkey
yet.
Instead, we will use the remote signer pubkey to send a create_account
request to the nip46 server.
Here is how it will look with nostr-tools
or NDK
:
- ask the user for their preferred username, and show them a list of nip46 providers - the full username will be
name@provider.com
- you might fetch the list of providers from the Nostr network, that's
fetchBunkerProviders
withnostr-tools
, but keep in mind - anyone can publish a provider description, and you don't want to send your users to some scammy service, so... - so maybe you start with a static list of good providers, and only fetch from network later, when you add some Web of Trust signals for your users to evaluate the providers
- now that user has entered their preferred username and selected a provider - make a nip05 request to check if the username is available
- if the name is available, let users click Sign up, show them the Loading state
- fetch the
remote signer pubkey
andbunker relays
of the selected provider from their nostr.json?name=_ endpoint - connect to the
bunker relays
(NDK
) - with
nostr-tools
you will callcreateAccount
and supply the provider info and theonauth
callback - with
NDK
you'll have to create aNDKNip46Signer
withremote signer pubkey
and then create thecreate_account
request yourself and send it with theirrpc
object - whenever
auth_url
message arrives, follow the same logic as above with the Login flow create_account
will return the newly createdremote_user_pubkey
- with
createAccount
ofnostr-tools
, you will get a ready-to-useBunkerSigner
object (when they fix an issue) - with
NDK
you'll have to create a newNDKNip46Signer
yourself with the samelocal keypair
and the newremote user pubkey
- and don't forget to save the
local keypair
andremote user pubkey
andbunker relays
to the persistent storage to reuse later
Now that was really hard, even with a help of some libraries. Hopefully, we get wider and more polished support for create_account
method, for now - it is what it is.
This is really out of scope of a Getting started guide (just use libraries), but here are some hints:
- when subscribing to the replies from the signer, add the
since
filter with something like now - 10 seconds threshold, many strfry relays don't actually delete the ephemeral events and will send you old replies - make sure the first parameter for
connect
method is theremote user pubkey
, notlocal keypair pubkey
- make sure you support passing the
secret
as a second parameter toconnect
method, some providers only allow connections with asecret
- all parameters and all return values of the nip46 method calls are
strings
, which means you'll have to stringify the event forsignEvent
, etc - make sure you handle relay disconnects properly - reconnect, and re-subscribe to nip46 replies
- make sure you ignore duplicates of
auth_url
messages, only the firstauth_url
per method call should be handled - make sure you ignore duplicate replies, only handle the first one
- make sure you test with relays dedicated to nip46, general purpose public relays may block ephemeral events or may impose rate limits if you make lots of method calls
Some hints here too:
- when subscribing to the replies from the signer, add the
since
filter with something like now - 10 seconds threshold, many strfry relays don't actually delete the ephemeral events and will send you old replies - make sure you only allow confirmation of new connections without a
secret
usingauth_url
pages, do not show connection requests without asecret
in other parts of your app, this is a security hole - make sure you handle relay disconnects properly - reconnect, resubscribe
- LOTS MORE, TBD.
Submit an issue for this repo, we will do our best to help. And please submit PRs if you have something to contribute.