All posts

Web3Auth Unified Login

12 min read · ComputeFlux Team
Economic Protocol

In one sentence. You sign in with an email address or a Google account, and your wallet is derived mathematically from it — there are no twelve words to write down, hide, and eventually lose.

Picture it like this. A hotel that cuts your room key from your reservation details instead of handing you a key to guard for the rest of your life. Vastly more convenient. It also means the hotel's key-cutting machine can produce your key whenever it likes — and this article refuses to soften that trade rather than burying it.

Why it matters. The seed phrase is the single biggest reason ordinary people bounce off crypto products. Removing it is genuinely valuable and genuinely changes the security model. This is one of the most candid articles in the series about exactly what the user is getting and giving up.

The part most readers should not skip is the threat model.


The defining UX problem of Web3 is the private key. Generate a random 256-bit number, encode it as 12 BIP-39 words, write it down, put it in a safe, never lose it, never photograph it, never type it into a website. The instructions contradict themselves: the key has to be accessible enough to sign transactions and inaccessible enough that no attacker ever finds it.

That contradiction pushes users toward custodial wallets, which defeat the purpose of self-custody, or hardware wallets, which add a $100 device to onboarding. ComputeFlux's unified login removes the private key from the user's mental model entirely. An email address is the only input; cryptographic derivation and TEE-protected storage do the rest.

This is the authentication layer under the bearer tokens Article 15 described issuing and validating — where those tokens, and the wallet they authenticate into, actually come from.

Cryptographic Derivation: HMAC-SHA256(email) → EVM Private Key → Address

The derivation function is deliberately minimal. What matters is being precise about what it derives, because the private key itself comes out of this chain — not just the address.

Start with a user's email. The system computes a seed as HMAC-SHA256(SeedSecret, email), then derives the ECDSA private key as HMAC-SHA256(SeedSecret, "eth-key:" + seed), reading the resulting 32 bytes as a secp256k1 scalar through go-ethereum's crypto.ToECDSA — the same curve Ethereum uses, not NIST P-256. The address is just PubkeyToAddress of that key's public component. SeedSecret is a high-entropy random value generated once via SetConfig and stored in the on-chain auth config, sealed inside the TEE rather than exposed in plaintext.

No BIP-32 hierarchical derivation, no seed phrase, no mnemonic encoding. And it's important to be clear about what that implies. The private key is not generated independently and then linked to an HMAC-derived address. The private key is the output of the same HMAC chain that produces the address — same email plus same SeedSecret always yields the same key, and therefore the same address.

That differs meaningfully from a design where only the address is derived. Here, anyone holding the SeedSecret and a user's email can recompute that user's private key directly.

Why HMAC-SHA256 rather than BIP-39? BIP-39 starts from a random seed, encodes it as a mnemonic phrase, and derives a tree of keypairs through BIP-32. That's the right choice when the user must control the entropy source and needs a key no third party could have generated.

ComputeFlux's model differs. The TEE is the trusted entropy source, the user authenticates through an external factor — email OTP or OAuth — and recovery runs on email re-authentication rather than seed-phrase restoration. BIP-39's mnemonics, HD paths, and derivation indices would add complexity here without buying security.

All of which rests entirely on the SeedSecret. Suppose it leaks, through a TEE side-channel attack, a code vulnerability, or an operational mistake. That isn't a de-anonymization risk. Because the private key derives deterministically from SeedSecret and email, a leaked SeedSecret lets an attacker compute every user's private key for any email they know or can guess.

So SeedSecret is among the most sensitive pieces of state in the entire system — arguably more sensitive than a traditional wallet's own key-encryption secret. It deserves the TEE-sealed handling this series describes for other high-value secrets: generated inside the enclave, never exposed in plaintext outside it.

Threat Model: What Happens If the Email Provider Is Compromised

The email provider is an external trust dependency. If an attacker compromises a user's Gmail account, they can request an OTP code, receive it in the compromised inbox, and authenticate as the user. This is not a flaw specific to ComputeFlux's design — it's a property of email-based authentication in general — but the system minimizes the blast radius.

The primary defense is that authentication only grants a session token, which authorizes API calls: reading usage data, viewing API keys, submitting governance votes. Signing a transaction takes more — decrypting the stored private key with the user's password, which is never stored server-side.

That produces a two-factor separation for the common case. The email or OAuth provider controls the session token; the user's memory controls decryption of the stored, encrypted private key. Compromise the email provider and you can impersonate the user on the platform without decrypting their key blob. Guess the password without email access and you can't get a session token at all.

Be precise about the limit, though. Since the private key is deterministically re-derivable from the server-side SeedSecret plus the email, this two-factor story defends against a database-only compromise. It does not defend against full compromise of the TEE's SeedSecret, which would let an attacker recompute private keys directly and skip the password-protected ciphertext entirely.

The remaining risk is that an attacker with a valid session token could modify the social recovery configuration — changing guardians, lowering the threshold — and use those modified guardians to approve a recovery that transfers wallet control. This is mitigated by requiring the current password to confirm any guardian change (a verifyPassword check before recovery configuration mutations); an email-only attacker doesn't know the password and can't make the change.

A deeper concern is the OAuth token itself. Logging in via Google means ComputeFlux receives a Google-issued access token proving the user authenticated, carrying its own scope and expiration. Breach the TEE and an attacker could harvest those tokens and reach whatever Google account data their scope permits.

Two things narrow that. Scope minimization: the flow requests only openid, profile, and email — no calendar, no drive, no contacts. And lifetime: the OAuth token sits in EPC memory for the milliseconds of the token exchange, then it's discarded. The long-lived credential is the gateway's own session token, not the provider's.

OTP vs OAuth: A Security Comparison

The login system offers two paths with genuinely different security properties: email OTP with a 6-digit code, and OAuth through Google, GitHub, or X.

OTP rests on the security of the user's email account. A 6-digit code has only 1,000,000 possible values, but rate limiting — five attempts per code, five-minute expiry — puts brute force out of reach. The realistic threat is interception. Anyone who can read the user's email can take the OTP, and if the email provider is subpoenaed or compromised, every code is exposed with it.

OAuth hands authentication to a third party the user already trusts with their primary identity. That party's security, typically backed by 2FA, login alerts, and a dedicated team, is almost certainly stronger than a personal inbox.

It also introduces a different risk. The OAuth provider can impersonate the user to ComputeFlux at any time, without the user knowing, because ComputeFlux verifies the token's signature chain back to that provider's trusted roots. This isn't a bug ComputeFlux can fix; it's inherent to delegated authentication. The mitigation is transparency. Every OAuth login writes a signed, append-only audit entry — provider, timestamp, IP address — visible on the user's dashboard and stored in PebbleDB's sealed storage, where an attacker who compromises the server afterward cannot alter it.

Social Recovery: Guardians, Thresholds, and the Recovery Ceremony

Social recovery lets a user regain wallet access after losing their primary authentication factor. In ComputeFlux's on-chain auth contract, each user has a RecoveryConfig naming a set of guardians and a threshold.

The flow is simple and fully on-chain. RequestRecovery opens a RecoveryRequest carrying the proposed new key-share root and an expiry a fixed number of blocks out. Guardians call ApproveRecovery to add their approval. Once len(Approvals) >= Threshold, anyone can call CompleteRecovery to update the account's stored share root to the requested one.

There's no separate signature ceremony beyond those ApproveRecovery calls, and no sign of a Shamir-secret-sharing step at this layer. If key material is split into shares at all, it happens outside this contract, which tracks only guardians, the approval count, and a share-root commitment.

Guardian selection is the hardest problem here — a guardian must be simultaneously trustworthy, available, and secure, and the set of people satisfying all three for a typical user is small, often zero. Diversifying guardian types (a trusted contact, a separate device the user controls, an institutional option) reduces correlated failure, though the specifics of what guardian types the product surfaces are a client-side/product decision layered on top of the generic on-chain guardian-address list.

Threshold setting is the error-prone parameter in any social-recovery scheme. Set it too low and a small coalition of guardians can seize the wallet on their own, defeating the multi-party model. Set it too high and one unavailable guardian blocks recovery entirely. The contract stores whatever Threshold value SetRecovery receives. What bounds a deployment enforces on that value, if any, is worth confirming against the current product configuration rather than assuming an on-chain invariant — the contract defines ErrInvalidThreshold but doesn't appear to wire it into a validation check in SetRecovery today.

The recovery request carries a fixed expiry measured in blocks from submission, so it doesn't stay open indefinitely. Guardians have a bounded window to approve before it lapses. Whether a mandatory waiting period gates a fully-approved request, and how long it runs, is another detail to verify against the deployment rather than assume. What the contract layer does guarantee: completion requires reaching the configured threshold, and the entire approval trail is recorded on-chain and auditable.

AES-GCM Key Wrapping and the Password Strength Problem

The user's private key is encrypted at rest with AES-256-GCM. The wrapping key comes from hashing the client-supplied passwordHash through a single round of SHA-256 — sha256Hash(passwordHash). No PBKDF2, no scrypt, no Argon2 stretching in this path, and no per-user salt at this layer beyond whatever the client-side hashing already contributes.

GCM still delivers authenticated encryption. Any modification to the ciphertext trips the authentication tag, so an attacker with write access to PebbleDB can't corrupt the encrypted key into a chosen plaintext. But the wrapping step adds none of the deliberate computational cost that PBKDF2, scrypt, and Argon2 exist to provide.

That leaves client-side password hashing as the only thing between a leaked ciphertext blob and an offline brute-force attack, because a single SHA-256 call costs an attacker essentially nothing per guess. Extract the blob from PebbleDB, work out how the client derives passwordHash from the raw password, and the attack is bounded by the password's entropy alone — not by any slow-hashing step in this code.

Two further caveats belong here. For accounts with no password set yet, ComputeFlux falls back to a server-derived convenience key: aes-256-gcm-server, keyed from the server's JWTSecret plus the email. That's a weaker threat model, since anyone who can reach the server-side key-derivation logic decrypts that copy with no user secret at all.

And as established above, the private key remains deterministically re-derivable from SeedSecret plus email. None of this at-rest encryption protects against a SeedSecret compromise. It protects the stored ciphertext from someone holding database access but not the secret.


Key Takeaways

  • Your wallet is computed, not generated. Email plus one server-held secret produces the same key every time. There is no randomness to back up, which is exactly why there's no phrase to lose.
  • That one secret is the crown jewel, and this article says so plainly. Anyone who obtains it can compute the private key of any user whose email address they know — not merely identify them, compute their key. Most write-ups of this pattern describe only the address derivation and leave that implication unstated.
  • Two factors with an explicitly stated ceiling. Email controls your session; a password controls decryption of the stored key. That defends against a database leak. It does not defend against compromise of the master secret, and the article doesn't pretend otherwise.
  • Delegated login is safer in practice and introduces a new party. A big provider's account security beats a personal inbox — but that provider could impersonate you at any time, by design, unfixably. The mitigation is a tamper-evident login trail you can inspect.
  • Several places where the code is weaker than the design intends are named outright: no deliberate slow-hashing on the stored key, a fallback that encrypts with a server-side key when no password is set, and a recovery threshold check that exists in the code but isn't actually wired up.

Authentication and wallet identity are, ultimately, in service of participating in the protocol — including having a say in how it changes.

That closes Part 3. The network can now take requests, route them, translate them, bill them, and let people in the door. Part 4 turns to the uncomfortable question every permissionless system eventually faces: who's in charge, and who checks the people in charge?

Next — Article 17: Governance — Gov Track Proposals: how the addresses derived here get used to propose and vote on changes to ComputeFlux itself.