Summary
This SRFC proposes an application standard for authenticating users of dapps, specifically those using Programmable Wallets (a.k.a Smart Wallets or Smart Contract Wallets), through the use of off-chain delegates. This standard addresses the challenge of authenticating users who interact with an app using a Programmable Wallet, which does not have a private key for signing messages. By enabling Programmable Wallets to appoint a delegate with a private key to sign messages on their behalf, a secure and verifiable way to authenticate users is achieved.
Introduction
A lot of dapps use the authentication process called off-chain message signing. The mechanism takes advantage of the fact that user keypair can be used to cryptographically sign any arbitrary messages such that anyone can verify that a message is signed by the party controlling the private key for the corresponding public key. The algorithm for this process is as follows:
- The app produces a unique message and gives it to the user (wallet) to sign.
- The user signs it and returns the signature back to the app.
- The app verifies that the signature is indeed valid and is for the message it provided originally.
- If true, the app issues an auth token that the client can use for future interactions with the app’s API.
The Problem of Programmable Wallets
Not every Solana address has a corresponding private key. A good example is Programmable Wallets - accounts owned by a Solana program that is typically controlled by one or many regular (keypair) wallets and interacts with the chain following the rules of the program.
For example, a multisig program owns a vault account shared by multiple owners of the multisig, tracks the status of voting on proposals, and allows executing the proposals once they are approved by the quorum. The vault account has a public key but lacks the private key; the program programmatically “signs” on its behalf.
How can we authenticate a user who uses a Programmable Wallet address to interact with a dapp?
Proposed Solution
Introducing Off-chain Delegates.
So Programmable Wallet accounts cannot sign a message off-chain as they lack a private key. However, they can appoint a delegate - a regular account that has a private key - authorized to sign messages on behalf of the Programmable Wallet account. This delegation can be fully registered on-chain, making it verifiable by anyone.
Let’s take a look at how the sign-in flow would work when modified to support Programmable Wallets:
- The app produces a unique message and gives it to the user (Programmable Wallet) to sign.
- The wallet knows which address controls the Programmable Wallet account and signs the message with the delegate keypair. It then returns the signature along with information about the Programmable Wallet account and delegate addresses to the app.
- The app verifies that the signature is produced by the delegate.
- The app verifies that there’s a delegation record (DelegateToken account) for the given delegate created by the account on-chain and that it has not expired.
- If true, the app issues an auth token that the client can use for future interactions with the app’s API.
Implementation on the Programmable Wallet programs’ side can vary between Programmable Wallets. Each program can decide and implement the mechanism of creating delegate records in whatever way they want. They just need to make a CPI (cross-program invocation) into the Off-chain Delegate Program that manages those records and sign that with the PDA seeds of the Programmable Wallet account they control.
Drawbacks
This mechanism introduces a slight overhead compared to off-chain signing for regular wallets. The overhead is the DelegateToken account that must be created and stored on-chain for each delegate. The current reference implementation, Off-chain Delegate Program, uses 77 bytes, which is about 0.0014268 SOL per account. The rent can be reclaimed when the delegate is removed, but it’s still worth highlighting.
Another problem is additional logic that needs to be implemented on the dapp side. Unfortunately, I don’t see how the mechanism can be implemented entirely in the wallet apps/extensions without explicit support from the dapps. If someone has ideas, please feel free to comment.
Conclusion
The proposed SRFC standardizes the process of off-chain authentication for users with Programmable Wallets through the use of off-chain delegates. Although it introduces some overhead due to the on-chain storage of DelegateToken accounts, this mechanism provides a secure and verifiable way to authenticate users interacting with an app using a Programmable Wallet.
Implementation: Off-chain Delegate Program