-
Notifications
You must be signed in to change notification settings - Fork 473
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
Proposal: Solana Auth #100
Comments
@PaulFidika at metaplex we are looking at something similar, lets connect @_austbot on twitter |
@PaulFidika very cool proposal! looking forward to following along. Im thinking some of the examples in the examples list can definitely be solved using transaction request, especially around NFT ownership check, and paying with arbitrary mint for example. The receiver app could always look for your signed message on chain asking to "sign in" and then direct you to some login page, but ya then you have to pay transaction fees, the way you have proposed with the stateless approach requiring just off-chain message signing is very cool! I find the stuff about OAuth scopes interesting as well and have been exploring it especially in the context of your login being represented an NFT that represents your identity which can be (optionally) transferred to a new wallet if you want to keep the same login and potentially point to some encrypted data store or encrypted on-chain data as you mentioned |
Correct me if I’m wrong, won’t using universal links need to be hard coded by dapp developers in their websites? Most dapp developers simply add phantom then that’s it, this will cause a huge barrier of entry to any other wallet developers, we don’t want just a single wallet supported by every dapp then every new comer has to contact every dapp developer out there and ask for their wallet to be hard coded in. This is already a problem now, every site has “Connect with Phantom” only. Some of us like to use other wallets. At least for now wallets can inject code in the browser to be detected, universal links hardcoded will make support for any new wallet in the mercy of dapp developers. The reason solana uri don’t open on phantom mobile is just because they haven’t added support for it, if they did iOS would have asked if you want to use Phantom or FTX app. A single open standard that dapps and wallets will follow is a better solution, we let the choice of which wallet to use to the users not dapp developers. I do agree with other points. |
I believe Glow Wallet does this. @vpontis |
Oh yeah a transaction request could be used to prove your identity as well; just the fact that you can sign the transaction proves that you have control of the public key. You don't even need to submit the transaction; you could just show that the signature + pubkey + transaction match. However that seems kind of round-about, and I definitely wouldn't want people to have to submit transactions every time they login haha.
Yeah it's really interesting! With credit cards payment is closely tied to authentication and providing consumer-information. I don't think we really need an NFT--I was thinking more like you'd have a data-store account associated owned by your pubkey, which would have some agreed upon format (probably a JSON string encrypted using your pubkey). And various 'verifier services' could add their signatures to the various fields to attest to them having checked and validated your information, so that whoever consumes the information has greater confidence in it, in so far as they trust the validation-service. If you wanted to transfer the data-account, you could change the owner. But probably what you'd do is unencrypt the data using the old private key, then re-encrypt it using the new owner's public key, and transfer the ownership of the account in which that data lives to the new account, if you wanted to migrate to a new pubkey. |
Yes this is a legit problem; we want dapps to support more than one wallet. I think what we'd do is have an open-source library containing iOS and Android components, which says 'connect wallet' -> pops up a screen listing all wallets; each of these links will have the universal link to that app, like phantom.app/auth or sollet.app/auth. If you a project wanted to add their wallet to this library, they'd just submit a PR to have the link-list updated, and then any dapp that pulls down the updated version will have that button auto-included without doing anything. That's kind of how it works currently with the Solana-labs wallet-connect repo.
iOS has disambiguation screens? Are you sure? I thought only Android had it.
Yeah I like the idea of a general |
I like the basic idea! I think it would be helpful to more narrowly scope this proposal, removing the stuff about deep links for now, which there are various UX flows wallets are looking to achieve that are out of scope for Solana Pay itself. I also think we need to simplify the protocol. The protocol doesn't require knowledge of what is being signed. To sign data of arbitrary length, you can use something like transaction requests, where instead the server responds with an arbitrary message for signing. A wallet can predictably hash and sign the hash (to prevent signing a transaction message), and then send the signature to an endpoint. The wallet is agnostic to what is being signed or how the signature will be used as long as it's safe. |
I also work with the same problem but differently...Zero knowledge-based solution. |
Thanks! And you're absolutely right; I'll break the wallet-redirect flow stuff into a separate proposal--it belongs more in the wallet-adapter section. I mostly added that for context. It took me a while to figure out what the purpose of Solana Pay was--essentially the problem you're solving is for cases where provider-injection (i.e. a browser extension) is not available.
I see what you're saying. I wanted to include a standard for the format of the signed message (the challenge) so that dapps have a standard to follow, rather than the silly insecure things they're doing right now. For example, thePortal.to asks you to sign a message to login, however the message is always the same every time, meaning they're vulnerable to replay attacks. For example, if I were to get ahold of you signing that message even one time I could authenticate as you indefinitely and then re-arrange your entire room (not a huge security tragedy, but meh). These amateurish NFT projects just don't have a clear security framework to follow. I also really like the idea of wallets checking the origin inside of the challenge they're signing. This will prevent attacks where a malicious site authenticates as you by forwarding you someone else's signature challenge. |
I talked to my friend Victor at Glow App today (a new Solana Wallet). They have the same idea as mean--an 'authenticated connect' function. There method is this: when the dapp connects to the wallet, the wallet not only returns the pubkey, but it also returns a message and a signature. The message consists of How do you guys feel about this? It's a very simple one-step process. My concern is that because these challenge-messages are predictable anyone with access to the wallet's signature function generate these signatures indefinitely into the future. Glow App could add a nonce to the message as well, but that wouldn't make it any more secure. In my opinion, this should be a 2-step process, where the wallet requests something like a nonce from the server, and then the wallet and server both use that to construct the challenge message which will be signed. This just feels way more secure to me. Thoughts? Implementations aside, this is HUGE for the internet. I could go to a web-app that I've already connected my wallet to (my wallet is already unlocked) and then Glow app could auto-connect (eager connecting) to the dapp, get a challenge, sign the challenge, return it to the server, and then be authenticated by the server, all in the background, without a single user interaction. If the Solana Auth standard is followed as above the user will never even have to read the message their wallet is invisibly signing, and it will still be entirely secure. Like this is internet-changing, 1-billion-people-using it daily type stuff. Also, when we do ask the user for login-permission, I'm debating if a display-message should be included along with the challenge by the dapp at all; we could simply leave that up to the wallet to implement entirely. I.e., the wallet could display a message like 'Login to theportals.to? [Approve] [Cancel]'. That would simplify this standard some. |
I think this Solana Auth standard qualifies as 'zero knowledge' already--correct me if I'm wrong. The merchant needs to prove they own a product? Could you elaborate more? Message me on Telegram @PaulFidika |
I also found this EIP today as well, and it's rather similar: https://eips.ethereum.org/EIPS/eip-4361 However it includes a much larger template, and way more variables. I'm not sure why the author feels the need for all those variables. One idea he has that this proposal is lacking is a version-number; having a version-number for the standard being used would help make it more extensible in the future as well. |
Proposal -- https://twitter.com/jordaaash/status/1560386497370419200 Since message signing security will be improved with prefixing (solana-labs/solana#26915) and this will be a requirement in the Mobile Wallet Adapter spec (solana-mobile/mobile-wallet-adapter#193) I think we can just have a simple protocol for exchanging messages and signed message + signature payloads, without caring about what's in them. Next step is probably a PR to the spec for this? |
Summary:
We propose an expansion on the Solana Pay Standard to provide an authentication standard using any crypto wallet.
Background:
There are two fundamentally different flows for a dapp to interact with a user’s wallet:
- Provider Injection: the wallet is a browser extension that injects a global object, like window.solana, which our dapp can interact with. This works best for web-apps running on a desktop browser. Mobile browsers do not currently support browser extensions (except for mobile-Safari, which added its own browser-extensions on iOS 15, although there are no crypto-wallets available yet). To get around this, MetaMask and Phantom have built browsers within their mobile apps; users visit a dapp from this in-app browser, allowing for global provider injection.
- Deep-Linking: we use the Solana URL scheme
“solana:...”
to open the user’s wallet-app on their device, approve a transaction request, and then redirect back to our app or website with the response as query params. This works best for web-apps running on mobile-Safari or mobile-Chrome, within native mobile apps, and for kiosks / terminals / any app that runs on an external device. You could also remove desktop browser-extensions entirely; for example, if a dapp on my laptop needed to submit a transaction it could present me with a QR code which I could scan with my phone camera, redirect me into Phantom, and allow me to verify the transaction.Problems with ‘solana:’ URL scheme and how to fix Deep-Linking:
https://phantom.app/code?<params>
https://phantom.app/code?<params>
. As a last resort, we can use the Solana URL scheme ‘solana:’ and hope this routes the user somewhere useful. However the above wallet-specific url and linktree methods should always be preferred. That is, the 'solana:' URL scheme should only be used as a last resort.request=
query param to the standard, which has a URL-encoded URI to which the wallet will make a transaction request.How signMessage authentication currently works with Provider Injection:
Security Problems:
Solana Auth Proposal:
How to adapt signMessage to work with Deep Linking:
request=
query param to the spec allows for us to use deep links.The wallet parses the link and optionally prompts the user for permission to make the request; this should be considered the same as a wallet-connect. The label and message params can optionally be displayed, but more importantly the wallet should display the url of the server to which the request will be directed. The permission step can be skipped if the wallet has connected to this domain before.
If permitted, the wallet makes a POST request to the specified url (which will be referred to as the authentication server from here on out). The label and message params will not be used again. The JSON body should have a
{ “account”: <pubkey> }
formatExample url:
https://merchant.com/solanapay?auth=true
The authentication-server generates a nonce and a message, and then generates a challenge, which is a 3-part comma separated utf-8 string, with the format:
${origin},${nonce},${pubkey}
where origin should match the origin of the redirect URI, the nonce is a randomly generated alphanumeric string, and the pubkey is the pubkey being requested for authentication. The pubkey-portion should be considered optional in this format.
Example:
https://merchant.com,GCQLiawuDQbaaxFUAKcGpvQxfSxddZwGDp8p4Q57DfoX,17FaeoyXD2
This will be returned as a Uint8Array. The authentication server then saves the pubkey and challenge as a key-value pair, with an expiry time after which the pair will be discarded from memory. It also adds a redirect_uri and an expiry time that is an epoch time value in the future. It then returns the following (signed or unsigned)) JWT response, with this being the JSON body:
Notes:
In oauth, this is called a ‘front channel communication’. A ‘back channel communication’ would be if the wallet sent the signature directly to the authentication-server, rather than including it in the redirect uri. Back channel communications are considered more secure because the two parties share an oauth credential, and if there was some piece of malicious software on the user’s device that could observe the redirect uri, that malicious software could authenticate itself before we get the chance to. However, the main point of this authentication flow is for the authentication server to place an authentication cookie in the user’s browser, so I think front channel communication is the simplest and overall best solution.
Alternate flow with stateless authentication server:
Proposed Changes to Wallet Interfaces:
Wallets should expose a more robust method;
requestAuth({ challenge: string, display?: string } ) => resp: { signature: Uint8Array }
. This method works just like signing a message, except that the wallet verifies that the challenge is not actually a transaction, and that the origin inside of the challenge is the origin of the currently connected website before it presents it to the user to sign. This adds an extra layer of security on top of the signMessage() method.This opens up the possibility of wallets creating an 'authenticated connect' method, something Glow App is already building. The purpose is to not only get the user's pubkey, but also a proof that the user owns their pubkey as they claim. This method would be an overload of the current
window.solana.connect()
method, except that the client-app provides a server-generated nonce:window.solana.connect({ nonce: string }) => resp
with theresp
object extended to contain achallenge: string
andsignature: Uint8Array
param. The client-app will first fetch a nonce from the server, call the connect method supplying the nonce, and then return the response object to the server. The server will first examine the validity of the challenge-string, which is should be${origin},${nonce},${pubkey}
, then compare that to the pubkey and signature. If it all checks out, the server will respond with an authentication token to the client.Ideally users should be able to auto-connect and auto-authenticate in one method call, without any user interaction or permission required.
Alternate flow without authentication:
from=<pubkey>&signature=<blank>
Examples:
Why Solana wallets (like Phantom) should build this:
The above has very broad applicability; even for users who have no interest in crypto, they can still use their wallet application as their preferred authentication method for any website, app, or kiosk that supports it. This greatly expands the use cases of Phantom, for example, beyond Solana. Even people with no interest in Solana could use this system.
This is a zero-knowledge authentication system, in the sense that the user does not have to share any secret information with a remote server. In contrast, when you login using a password and username you are sending that server your password in plaintext, possibly compromising your password if the server mishandles it (such as leaving it in a log file).
Furthermore, this authentication system is non-custodial, and doesn’t require the permission and service availability of a 3rd party resource-provider (such as Google or Facebook…), for either the user or the developer implementing it into their service.
Future expansions:
Solana Auth with scopes
One of the beauties of the oAuth / OpenId Connect standard is the ability for the client (application) to request pre-verified information about the resource-owner (user), in addition to just authenticating, which can be used to expedite purchase flows and registration flows. For example, if you’re buying a product online, you can authenticate and give the application your full shipping information in one click.
oAuth calls this a ‘scope’ request. To implement this, in the JWT response containing the server’s request, the authentication-server can add a scope parameter to the body, like:
{ scope: [name, email, shipping_address, phone_number], …}
Where each scope is a request for a specific piece of information about you. This information can be stored by your wallet or stored on-chain and encrypted using your pubkey. Your wallet app can then display a message like ‘merchant.com is requesting the following information: name, email, shipping address, and phone number’, and the user can granularly choose which pieces of information it wants to expose.
Transaction requests superseed authorization requests, because the signature for a transaction can already be used to authenticate a user. However, we can combine scopes and transactions to do some interesting things. For example, a kiosk could display a QR code, which when scanned responds with the following transaction request:
This transaction could authorize the kiosk to charge us an unspecified amount within the next few minutes (after I checkout). The scope provided gives the kiosk an email address to mail the receipt to, and the name gives the restaurant owners a name to associate with my order.
In Denver there's a restaurant called Bird Call, and they have a kiosk that does exactly this with my credit card; I swipe it once and then it (1) recognizes me by name, so the restaurant employees know who to give my order to, (2) it emails me a receipt, and (3) it charges my card for however much I ordered when I checkout. I’ve seen several kiosks that now use credit cards for authentication in addition to charging for purchases, such as airport check-in kiosks.
The text was updated successfully, but these errors were encountered: