[RFC] WebAuthn Credential Resolver for ENS

Credential Store for WebAuthn Public Keys

ENS has no native credential resolution primitive for WebAuthn public keys. Any ENS name can store a passkey public key as a text record today — but with no standard key convention, no resolver that understands the format, and no onchain verification path, the credential is effectively inert.

This RFC proposes a WebAuthn credential resolver for ENS — a primitive that makes passkey public keys first-class resolvable ENS records, verifiable onchain via P-256 signature verification (EIP-7951, live on mainnet since Fusaka).

ENS supports storing arbitrary data as text records — including WebAuthn public keys, with gas abstracted by embedded wallet providers for the initial setText(). What ENS doesn’t provide is a standardized way to resolve that credential or verify it.

Every app that wants to authenticate against an ENS-stored passkey has to build its own resolver logic and P-256 verifier from scratch.

This proposal eliminates that duplication: a single credential resolver contract any app can call, backed by a standalone P-256 verifier using EIP-7951.

The infrastructure exists once — apps just resolve and verify.

Example Use Cases

App ecosystem with 2LD identity

A developer builds a reputation-weighted prediction market and wants users to have a portable identity that works across their ecosystem. The app offers free 2LDs — users register via an embedded wallet in one tap, no seed phrase. The embedded wallet signs the one-time setText() that publishes the user’s WebAuthn credential (qx, qy) to ENS.

WebAuthnCredentialResolver reads that record and WebAuthnVerifier handles P-256 signature verification against the user’s device — no custom resolver logic required from the app developer.

From that point on, any app in the ecosystem resolves the ENS name and authenticates via Face ID or Touch ID — no wallet, no gas for the user, no extension. The credential lives in ENS, not the app’s database. one setup, portable everywhere.

Passwordless Login Anchored to ENS

A user registers alice.eth and signs one transaction to publish their passkey public key (qx, qy). ENS stores the credential — but without a standardized credential resolver, any app that wants to authenticate against it has to build its own resolver logic and P-256 verifier from scratch.

WebAuthnCredentialResolver and WebAuthnVerifier close that gap, extending ENS from a naming registry into an authentication primitive. Any app can resolve alice.eth, fetch the credential, issue a nonce, and verify the user’s Face ID response — no custom infrastructure, no wallet dependency in the auth flow.

Wallet only needed once — for setup. Every login after that is passkey-native.

AI agent authorization

An AI agent acting on behalf of alice.eth publishes its P-256 public key as an ENS text record. Any service the agent calls can resolve alice.eth and verify the agent’s signatures. Authorization is publicly auditable onchain. Revocation is one setText(), instantly effective everywhere. No OAuth server, no API keys, no centralized auth provider.

This is only possible with a standardized credential resolver — without one, each service would need to independently know which text record key to read and build its own P-256 verifier.

How it Works

We ship two contracts:

  • WebAuthnCredentialResolver — reads webauthn.credential from an ENS name’s text records and returns the P-256 public key (qx, qy) to the calling app
  • WebAuthnVerifier — verifies a P-256 signature against that public key using the EIP-7951 precompile (~3,000 gas)

Authentication flow in three steps:

  1. app resolves alice.eth → fetches (qx, qy) via WebAuthnCredentialResolver
  2. app issues a nonce → user taps Face/Touch ID → device returns a P-256 signature
  3. app calls WebAuthnVerifier.verify()boolean true or false

No wallet, no seed phrase, no cross-chain calls. All verification happens on L1.

The specification has already been developed in depth — proof system, cryptographic primitives, challenge freshness rules, client requirements, and resolver profiles are fully documented here: WebAuthn Resolution Specification - Eureka

Open Questions

  1. Are there existing ENS ecosystem projects pursuing passkey-native credential resolution that I should coordinate with rather than duplicate?

  2. Trust model: this proposal proves ownership of a credential published in an ENS name’s records — not ownership of the name itself. If a wallet is compromised and the webauthn.credential record is overwritten, an attacker could impersonate the user without their passkey. How should apps consuming credentials resolved this way handle this risk?

1 Like

I think this is missing rpid context and possible extra creds data required to get residential vs non residential key support. and webauthn is bound to that specific wallet/dapp domain, so we might need more than one key=> value records per rpid.

```
node = alice.eth
rpid = some.domain.com
public key = x,y
key type = p256/secp256r1 * to add other key support in future
might also need credid as hash? & extension data if PRF was used?
residential vs non-resident?
```

current pubkey(..) records in ens isn’t fit for this, there’s no extradata or key types field.
using text(key=???with rpid) is only options but we’ve to go through string value for verification..
proper node=> key+rpid hash => bytes mapping would be better fit..

1 Like

Thanks for your input, your comments address various gaps I overlooked.

On rpid context

I realize that passkeys are bound to their domain via the browser-enforced rpid, so credentials won’t transfer across origins. The current design ignores this, leaving credentials ambiguous.

We should treat rpid as a first-class field in the credential record. A flattened, parameterized key like webauthn-credential[<rpid>] could work, pending further research.

On multiple credentials per name

Because rpid scoping is required to resolve a name per client, a simple 1:1 text record is insufficient.

A bytes-mapped field would support multiple credentials indexed by rpid, avoiding string parsing overhead from text-based keys. This could fall under a proposed WebAuthnCredentialResolver, though it would likely require a new ENSIP.

On missing credential fields

Thanks for flagging. We need to account for the following credentials:

  • credentialID: required for non-resident (server-side) lookups so verifiers can disambiguate multiple credentials per name.
  • keyType: explicit flag to future-proof for emerging curves (e.g., Ed25519).
  • PRF extension data: needed when credentials use PRFs, to store context alongside the public key for correct verification flows.
  • Resident vs non-resident: WebAuthCredentialResolver supports both—resident keys live on the authenticator; non-resident flows require server-side lookup with a credentialID hint.

On pubkey() ceiling

The current resolver stores a single (x, y) key pair—suited for generic pubkeys, not WebAuthn. Forcing WebAuthn into pubkey() is a square peg in a round hole. We likely need a purpose-built schema with fixed-size fields tailored to credential data.

Tl;dr

The WebAuthn data model is more complex than flat text records can handle. Following recommendations for the project to move forward are:

  • Use parameterized text record keys webauthn-credential[<rpid>]
  • Create a custom, data-rich value schema
  • Build this into the WebAuthnCredentialResolver design

One more thing: I realize this pushes beyond current standards, and not every ENS name will adopt WebAuthn or switch to a custom resolver.

A workaround is implementing @Premm.eth’s ERC-8121: Cross-Chain Function Calls via Hooks.

Instead of embedding credentials in ENS, a hook can redirect lookups to a dedicated WebAuthn credential registry, while ENS stores only a pointer (e.g., a single text record).

1 Like