-
Notifications
You must be signed in to change notification settings - Fork 43
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
[Contributor Proposal] [Do not merge] Account Abstraction - (Nextjs, WebAuthn, Safe) #223
Conversation
|
We'd also need to implement some way for them to control their funds as if a non-web3 user comes in without a wallet and we create one for them (before the cards are rolled out) then they'd have no way to access the funds unless we provide them a UI as we are now technically their wallet provider, otherwise a way for them to securely swap and off-ramp. |
the build doesn't suppose to be failing, does it, @Keyrxng |
I believe that some environment variables are missing according to the build
|
The build is failing because CI is expecting the build to come from the So my apologies if it wasn't clear before now but this is more of a reference PR in regards to account abstraction than an actual implementation PR. A few things to be decided first before an impl PR is allowed I think?
This recent EIP proposes baking AA into EOAs, it's quite a hot topic on CT right now. It's only in review (draft > review > last call > final) so it's pretty far out but should be kept in mind I think. |
It's interesting research. I'll be back on my computer early next week to be able to hopefully spend time on a deeper dive because I am not well read on all the latest in account abstraction. Looking at the video: I had a far more lightweight implementation in mind. The key point would be leveraging the webauthn browser API.
I think all these clicks, modals (and in this case, redirects with more inputs) that are prevalent in this industry is bad UX. This should all be handled behind the scenes and as seamlessly as possible. Further UX optimizations:
|
Yes, great initiative and im in favor sounds great some points:
|
if you can add generic js tests is plus |
great stuff, I'll carry it through to completion over the weekend taking these comments into account |
How it works
Implemented using information from cookie based auth and MFA (webauthn) credential.
Not implemented. We could rebuild the account pk using the cookie auth and browser-stored
Both newly visiting EOA's and smart accounts are populated into the
I was using a tweaked version of the gas faucet to fund the few accounts that I saw through to deployment but I've been thinking about this.
We could just use the faucet seeing as that is a worker, just setup a KV for it and track which newly created accounts both SMAs and EOAs have been funded and expose access to the KV through calls to the worker so other plugins/modules can use it
So will the user entry point for wallet and account creation always be GitHub via slash commands? Considering what @molecula451 said about it being user choice between the two smart account versions, how would they choose that at a bot level via slash command? I think it makes sense to either choose only one and have a dedicated section in docs or onboarding explaining things or create a new section for the claims portal. A visit to If only one is being chosen it makes sense to use the Multi-Owner-Modular account because of it's extensibility with plugins etc it would likely work very well with whatever plans you have for ubiquity cards, I assume they go hand-in-hand with the long term vision being preferably everyone has at least an abstracted account, if not also the card too? But it would also make things easier to register at bot level, as we'd be able to setup, fund, debit and deploy all without leaving github. QA
aa-webauthn.mp4std-eoa.mp4 |
This is nifty but it's a bit hard to tell watching from my phone screen some of the details.
|
I'm either grabbing or creating the account because we only have one entry point The 2nd step (the 3rd step in the video) in the new creation process pulls info from the credential to use in creating the account and just makes sure nothing went wrong in creating the credential like a timeout of the OS request or some other cross-platform issue. The If we chose to store a flag in With Auth0/Supabase we can be sure to always return the correct credentials and allows us to handle multiple credentials/smart accounts if that is a feature to be offered (does that fall inline with how cards will work maybe?) My suggestion in that a visit to
When creating the credentials we need to fill in identifying information from us as the host and them as the user and some other settings. publicKey: {
challenge,
user: {
id: new Uint8Array(0),
name: "",
displayName: "",
}, As recommended, gated behind auth0 we have multiple layers of security and more entropy for creating the new account with so long as the info we are using from that step is secured properly. I've tried to make it as secure as possible while still being deterministic.
This statement from the docs is in regards to a user being identifiable with the easily accessed credential information through the credentialID (which is used as entropy for the pk), so a better way to handle it is to gate it behind an additional auth step.
I forgot to add this implies that they first setup their credential via "/" |
Cloudflare login uses my iCloud so I figured there is some OS level abstractions we can take advantage of for our identities instead of logging into GitHub as an extra step. The cloudflare UX prompts an OS modal with a single button to login. When I press it, it does faceid and I log in right away. It's really seamless and seems to have all the strengths with none of the weaknesses. If for some reason that's not viable for non Apple devices, an alternative suggestion is to generate a unique device fingerprint/signature (theres techniques that use webgl) to deterministically generate a dedicated signing key per device. Then we could in theory allow multiple claim addresses per user although that would require a forked version of permit2. But this would be great for private key security because the user doesn't ever need to export or save it elsewhere. The device should be able to deterministically generate it every time. |
To clarify, I just test at the Cloudflare login again and I'm specifically referring to the two factor auth. This doesn't require an iCloud ID but it's shared in my "keychain" Which iCloud syncs across all devices. |
Which is just another MFA auth step like GitHub login, but I hear what you are saying.
The pk could be generate deterministically with only the information from the passkey although you'd want to add in some secure extra entropy not attached to the key. And we could remove any other auth step like Github or Auth0. I avoided it because it seemed insecure to me at first but I can push this ahead with just WebAuthn if that's acceptable, which by the sounds of your comment it is and enhancing pk generation/security can be hashed out later? pun intended |
Your keychain is part of your iCloud ID. I'm not sure how it works with Android so I can't comment but it's my understanding that they go hand-in-hand
Maybe I misundestand. You are logging into Cloudflare using your keychain. I tried "login with apple", is that the same as what you are seeing/doing? |
I wanted to include a video but I'm unsure how to censor the key it displays. To be honest I'm not even sure if that key is considered sensitive data. I can also 2FA with my hardware wallets for other accounts. Basically there are APIs to inject keys into the browser with elegant OS level integrations that we should be taking advantage of. I'm not so concerned with entropy because the raw inputs aren't "exportable" by the user. I don't see how it can be leaked if we don't provide that option in the first place. We could consider browser fingerprinting so that it's all seamless but still not sure that this additional entropy is necessary. However that would be a really nice architecture for a user to be able to automatically add their generated (browser fingerprint based) device keys to a gnosis safe where all their assets are. They can conveniently "2fa" with another device they own. Key rotation shouldn't be necessary if we don't save the key anywhere (it can be deterministically generated every time) In this case perhaps it makes sense to add that additional entropy via a login (maybe GitHub or something better?) so that a malicious actor won't use our same algorithm to make a fake page to generate the key and save it on their database (I understand now why you wanted to make additional entropy, it's so that their browsers can't be easily phished with a fake page?) Another seamless way to handle this without a login: what if we use a posix timestamp as a salt and then save the generated key in localStorage? At this point, they would need a way to export the key which is not desired. However the benefit is that even with the same algorithm, a phisher won't be able to generate the same key. Pros and cons... I wish there was a way to not necessitate exporting/backups. One last idea is using a password as a salt, combined with a browser fingerprint. Autofill can make it seamless for the user to authenticate. That way the user just needs to remember the password and the device they used. We can encourage users to have minimum two keys connected to their claims so that if they lose their phone etc they can still manage their account from the other? We could also consider the GitHub user as a super user for their account so they can always manage their keys even if all their devices are unavailable. |
We could black box the key generation algorithm by distributing it as a compiled wasm module. |
do you mean a literal Gnosis Safe or are you using that interchangeably with just an abstracted smart account? I see Gnosis have rolled out points for transacting with your own Safe which is cool, maybe swap out Alchemy as the AA provider then and go with Safe?
Sort of, the passkeys are tied to the domain that they get registered to so it's already sort of 'unphishable' in that sense. But if the whole AA system is based off of just the user-accessible passkey info, I believe all that would be needed on the attacker's part is to manage to hack their iCloud account. Or if a user was MITMd their info would be revealed after a successful passkey request, an attacker would likely be able to use this and the algorithm to reproduce the pk or just access the website with the user's account logged in via browser ext for example (with github too, if the user has an icloud passkey saved they are fucked so maybe that's redundant anyway?). Will access to their card(s) use the same credentials/auth flow for moving funds etc? It seems shaky to me at least to not include additional entropy somewhere that either only UBQ can produce or that we can tie to a user regardless of device, location, browser, etc. and confidently reproduce it.
I would not save my wallet private key anywhere on a browser personally and I don't think it would be a huge selling point of a novel AA system, but idk. If you meant the passkey "key" (lol) then with entropy it's not totally unsafe I guess. But what happens when they get a new laptop or clear their localStorage, bye bye smart account unless we always first attempt to retrieve a set of credentials and if
I like the sound of this actually
For me browser is the natural claiming point, I don't dev on my mobile and mobile web3 sucks ass. As part of the account creation path, it should be mandatory to create a passkey from your desktop browser and another from your mobile, then we are totally hands-off. You can likely imagine the scenes with a huge devpool base with user errors with an optional 2nd key.
So I'd suggest making it mandatory with disclaimers and warnings that the onus is on them after account creation regarding account access. Although I'm not sure of how the UBQ cards are coming together but depending on how those cards are "owned", does it make sense to have AA be foundational in how the cards are constructed or vice-versa? I imagine the cards will be Fort Knox and their smart accounts should be too, but will this early account abstraction system being described here work well with cards?
So no-one really knows the private key for the wallet, it's just produced and only ever produced on ubiquity domains via the wasm module? (credentials can be shared across domains) That's exactly like being a custodial wallet provider like a CeX. "not your keys, not your coins" springs to mind. With both the cards and AA coming into play, it might be an idea to define exactly what sort of "wallet provider" Ubiquity is going to be as that is certainly the direction it's all heading with the need for UI's for handling funds etc.
|
That's not at all what I'm proposing. The keys are in the users browser. They should never be transmitted off of the device. We don't have any access to their keys. The cards have almost nothing to do with blockchain right now. The current plan is to transfer ubiquity dollars to a "card minter contract" and then we mint a new card up to $1000 and give it to the user. It's our bank account. In order to reimburse it, we take the ubiquity dollars and redeem them for USD.
This is great!
The idea is that each key technically weakens the users security. But if they keep adding keys to their account to be able to claim their rewards then it's more convenient for them. I imagine that the signing key can be for signing but the funds should really be stored elsewhere. So in this case maybe a modified permit2 contract makes sense for any user to be able to claim any permit. This means that we can use any signer to claim (and transfer) to their registered wallet on GitHub. The thing is, when I personally receive rewards, I like to claim them instantly. I imagine others may also do the same because it ensures that you can't get rugged on your payment if it's already in your wallet. |
5f3d382
to
40aadb2
Compare
Started anew and took it in the new direction discussed.
I don't see why using the faucet to fund (with kv upgrade) isn't a suitable approach? Or a relayer setup to process claims from the portal domain and only if Soon as an account is registered (before any tasks are completed because no address yet) let worker deploy user Safe are working on implementing adding passkeys to existing Safes through their SDK so I think it might be an idea to focus on a more secure pk algorithm and multi-passkey to account features once it's rolled out via the SDK? Changes
QA:
For the sake of QA I manually funded a previously created passkey address so I knew the address for funding but I've left out any sort of funding logic from this new approachwebauthn-safe-aa.mp4 |
@Keyrxng This PR touches many (if not all) of the project files. So the changes are too big for a single PR. We could divide it into issues like:
|
@rndquu I agree with you at least for the most part we know what's expected.
Would it be better to wait for #226 before migrating to a new framework I know @EresDev proposed it in the PR, thoughts on that? |
TLDR;
I'm also ok with Next
Yes Regarding the https://www.keyrxng.xyz/blog/let's-buidl-webauthn-account-abstraction article (which is awesome by the way). Security considerations:
Most of the params which form a mnemonic's PK (i.e. seed) ( Regarding AA solutions. I don't really like any of them since (at least nowadays) they are:
I would wait for some time for a free battle-tested AA solution and in the meantime stick to a simple approach with https://github.com/ubiquity/faucet. Another point is that right now (with the current permit generation schema) we need to know permit spender ahead of time (i.e. on permit generation) while with webauthn we can generate user's PK only on claiming which would force us to change the whole permit generation flow. My point is that webauthn (and AA to some extent) requires the feature of consolidated permits (when we store permits in a DB and generate them only on user demand, example flow). So the flow could be:
This way we get "web3-free gasless permit claims". |
@rndquu a lot of great points here ty. WebAuthn standard requires the challenge parameter to be generated on the server side. I initially planned to use a Next.js/React stack and leverage the server accordingly, exposing only the credential mid-request from the client. Our goal is a frictionless, no-signin, no-MFA, no-backend approach, which means circumventing some best practices. We’re exploring what’s possible. I considered a worker that handles everything from pk alg to transaction handling, with strong authentication and encryption if the Next.js/React approach isn’t viable. Most parameters forming a mnemonic's PK (name, displayName, auth_account_created_at) can be brute-forced. The docs explain that displayName and name are arbitrary and createdAt has limited entropy. I omitted some details in my blog due to uncertainty about sharing them. Required user entity values (name, displayName, id) are arbitrary and could be defined by the user at registration and not their GH info. Supabase auth UUID and OAuth identity UUID are user-specific and hard to brute-force. GitHub login acts as an auth checkpoint, providing UUIDs to secure the pk further. Salts add security assuming the user maintains security on their end:
An organizational salt integrated into the algorithm adds another layer of security. Proper CSP implementation and minimal browser storage can help prevent malicious JS from calling Balancing security and friction is key. Hacking this system requires compromising three high-entropy data points, keychain/biometric security and/or a successful MITM attack. A Next.js-based server-side handling of pk and transactions exposes only the credential ID and wallet public key to the client. User flow:
For a hacker to claim the permit, they’d need to bypass all security measures and even then they'd only be able to claim it for the user. We’ll use https://github.com/ubiquity/faucet for simplicity. Focus on securely building, transmitting, and using the PK, then fund the address with the faucet. Future enhancements can include smart accounts and sponsored transactions. Multiple static UIs are good for quickly iterating but the end product would be far better served as one full-stack app that encapsulates registering, claiming and moving funds. The credential is scoped only to the portal, we don't allow users to "export" and it's a better UX to have one place that consolidates all they need as opposed to needing to hop between domains for certain actions. I think:
Auth:
This consolidates the signer in one codebase, simplifying the pk module. Had GPT re-write to try shorten it |
Agreed so lets not jump the gun. Instead, I recommend we create multiple dedicated repos. |
Perhaps it makes sense to start simple with a new UI like So the flow for
The next step could be to add faucet support for newly created addresses so that new contributors could claim in the "gasless" way. |
I have a POC video in the first comment but yeah that's exactly what I think should happen.
But for the first iteration, simply generate the EOA and fund it immediately (it's the simplest approach, the loss is nothing to be concerned about considering we are funding < 0.01 xDAI) before storing it in the DB for them to be able to go and use I made remarks about the module not supporting handling OAuth which makes sense in the end but inline with both of your suggestions regarding multiple UIs for iterating I will bake in OAuth handling as a set of functions which should be expected to be deprecated quickly. Mainly because the module needs any env vars passed in as params to set everything up and as you mentioned @rndquu because there are a few steps we should be handling on the server. I will also add that I am sure the credential created on My thinking so far is build
This reads like you mean so that they are able to claim more than once in a gasless way, or am I misreading? You mean so that they are able to claim their first permit gasless? |
I think that the faucet approach allows us to support more networks, but if we can just leverage the sponsored transactions on gnosis then we have less infrastructure to maintain which is more attractive to me. I would still prefer to use sponsored transactions if possible. If you are concerned about the domain name, perhaps you can just do a page instead of a different subdomain. wallet.ubq.fi/new |
Related to #219
Changes
NextJS
QA:
passkey
localStorage
passkey
QA comment with video