Domain Wallet Authentication
The Domain Wallet Authentication describes a method for linking a crypto domain with authentication methods or providers by adding an authenticator: JSON/URL field to the metadata of a crypto domain NFT. The standard also describes a method for application developers and web3 login modal providers to enable users to login with their domain name.
No reviewsSpecification
Abstract
The Domain Wallet Authentication describes a method for linking a crypto domain with authentication methods or providers by adding an authenticator: JSON/URL field to the metadata of a crypto domain NFT. The standard also describes a method for application developers and web3 login modal providers to enable users to login with their domain name.
The goal of this specification is to define a chain-agnostic identity standard for domain-based authentication.
Terminology
- Crypto Domain: A domain name that is associated with a blockchain address, typically stored in a blockchain NFT.
- Crypto Domain NFT: A non-fungible token that represents ownership of a crypto domain.
- Wallet Provider: A service that provides wallet functionality, such as signing transactions or messages.
- Text record: A record that stores text-based information, such as an authenticator URL or JSON object.
- Domain lookup/query: The process of retrieving information associated with a domain name, such as an address or text record.
- Authenticator: A URL or JSON object that provides authentication details or additional information related to a domain name.
- Authenticator Flow provider: Any service that will store Authentication Flows linked to Crypto Domains.
- Authentication Flow: Configuration for the requesting application to find and connect to the wallet that it wants to authenticate with.
Motivation
Current blockchain authentication methods primarily rely on connecting wallets via providers like Metamask. However, this requires users to remember both their wallet provider and their wallet address. As more wallets, signers, and chains come online, this problem will only get worse.
Crypto domains provide a human-readable, user-friendly way to represent wallet addresses. By enabling authentication directly with crypto domains, this standard aims to improve usability and adoption of web3 logins.
Additionally, standardizing the way domain NFT metadata specifies its supported authentication mechanisms allows any compatible domain NFT to abstract out authentication methods and key management. This abstraction allows both login modals and dApps to easily integrate domain-based logins.
Specification
Storage Format
Any system capable of resolving text records can be used for the name in this system.
However, we have chosen to focus this specification on Crypto domain NFTs.
Crypto domain NFTs that are compatible with this authentication standard MUST include an authenticator text record entry with the following properties:
authenticator(string, required): An URL that dereferences to a JSON object containing configuration information. In particular, information about how to authenticate the domain's subject. e.g.
https://www.authprovider.com/auth/{}
The application will craft the final URL to get the configuration, where "{}" will be substituted for the user's whole crypto domain name, so for "chrisc.eth" the final URL would be http://www.authprovider.com/auth/chrisc.eth
The actual syntax used to store this authenticator text record will vary depending on that of the particular Crypto Domain system used.
For example, on ENS, a [ENSIP-5][] record should be created, with a key of authenticator.
Any relying party can then query the domain for the authenticator text record via the [ENSIP-5][] getter interface, i.e. text(bytes32 node, string key) where node is the ENS domain being queried and key is the string "authenticator".
Example of a request to retrieve the "authenticator" ENS text record:
import { normalize } from "viem/ens";
import { publicClient } from "./client";
const userDomain = "chrisc.eth";
const authenticatorRecord = await publicClient.getEnsText({
name: normalize(userDomain),
key: "authenticator",
});
// ensText will be 'https://www.authprovider.com/auth/{}'
const authenticationFlowsURL = authenticatorRecord.replace("{}", userDomain);
// authenticationFlowsURL will be 'https://www.authprovider.com/auth/chrisc.eth'
Authentication Flows Retrieval
Having the authentication flows URL, the application can perform an HTTP GET request to it and obtain their configuration. Application will filter the authentication flows that are not supported and then try to execute in order, passing on the ones that cannot be fulfilled until it finds one that can complete successfully or needs action from the user.
An authentication flow may be unsupported due to the platform it is running mismatching the one it requires, e.g. "browser", "mobile", etc. Or because it requires an specific wallet but it is not installed, e.g. requires "io.metamask" but there is no browser extension wallet. When an authentication flow requires user action, such as scanning a QR code, or authorizing the connection from the wallet, the application must wait for the user to complete or cancel the action.
Authentication Flows Definition
The Authentication flows definition JSON MUST conform to the following [Draft 7 JSON Schema][]:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "The wallet address requested by the user. This can be from any chain and can be an externally owned account or smart wallet"
},
"chain": {
"type": "string",
"description": "The blockchain id as specified in CAIP-2. This is only required when the wallet is a smart account on a specific chain. If not provided, it is assumed the wallet is an EOA that is not on any chain."
},
"authFlows": {
"type": "array",
"description": "List of authentication flows supported for different platforms and connections. At least one value must be provided.",
"items": {
"type": "object",
"properties": {
"platform": {
"type": "string",
"description": "The platform type, e.g., 'browser' or 'mobile'. If not specified, it should support all platforms"
},
"connection": {
"type": "string",
"description": "The method of connection, e.g., 'extension', 'wc' (wallet connect), or 'mwp' (mobile wallet protocol)."
},
"URI": {
"type": "string",
"description": "The Uniform Resource Identifier used to initiate the authentication process. This can be a URL with an optional domain placeholder, a universal link, a wallet rdns following EIP-6963 or the string 'injected'. E.g., 'io.metamask', 'https://domainwallet.io/wallet', etc. In case this value is not provided it will default according to the application criteria, e.g. looking for an 'injected' wallet in browser"
}
},
"required": ["connection"]
}
}
},
"required": ["address", "authFlows"]
}
Possible values for each field are:
platform:browsermobile- undefined
connection:extension, whenwc, to trigger a connection with the wallet using [Wallet Connect][]mwp, to discover and communicate with the wallet usign [Mobile Wallet Protocol][]
Those values are not exhaustive, which means they can be extended as new options arise or become popular.
For example, a new platform such as wearable or a new communication protocol could be defined.
This last case will likely happen when smart account wallets or embedded wallets gain enough traction to reach consensus on a standard and become interoperable.
To further clarify how authentication flows should be used, let's consider the following example:
{
"address": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
"authFlows": [
{
"platform": "browser",
"connection": "extension",
"URI": "com.wallet.rdns"
},
{
"platform": "mobile",
"connection": "mwp",
"URI": "https://universal-link.wallet.com/"
},
{
"connection": "wc",
"URI": "http://www.wallet.com/auth"
}
]
}
To resolve that configuration:
- Application will iterate the authentication flows, automatically discarding the ones that are not supported until one can be completed or requires user action.
- First authentication flow indicates that it has to run on
browser, where there has to be anextensionwallet installed with the resource identifiercom.wallet.rdns. When this authentication flow is possible, application will try to trigger the wallet and request a connection using the standard mechanism to communicate with browser extensions wallets. - Second authentication flow indicates that it has to run on
mobile, and using [Mobile Wallet Protocol][] (indicated with the keymwp) application has to find another app that responds to the universal linkhttps://universal-link.wallet.com/. - Last authentication flow does not impose a platform to run on because it does not specify any, so it will be valid everywhere. It tries to set up a [Wallet Connect][] connection to the URI
http://www.wallet.com/auth. When this authentication flow is triggered, the URI will be opened in a new browser window passingaddress,domainandwcUrias query params with the resolved address, the domain name and [Wallet Connect][] URI respectively. As this flow requires user action (connecting the wallet at the URI) this step will never be skipped as long as the application supports [Wallet Connect][].
Standard Authentication Flows
To further clarify different authentication flow configurations we can consider the following cases.
Use a wallet extension installed at the browser
{
"platform": "browser",
"connection": "extension",
"URI": "io.metamask"
}
When using this configuration, the application MUST check available [EIP-6963][] wallets via its signaling channel and trigger the one that matches the URI field with its rdns. In these cases, the application should not ignore and replace the URI field with any other wallet found in the browser, as it could lead to a security issue where the user connects a wallet that is not the one they want to authenticate with. Therefore, this configuration will only be valid if the browser has that wallet installed (in this case, Metamask) and be skipped when it is not.
Use the wallet extension installed at the browser, without requiring a specific one
{
"platform": "browser",
"connection": "extension",
"URI": "injected"
}
As this authentication flow calls for the injected extension, without specifying which one, it should use the default wallet provider at the browser.
For EVM chains, this would be window.ethereum.
Having connection set to extension, the URI field is by default injected, so some users might want to skip this field and save some bytes.
However, this configuration is not recommended as having multiple wallets in the browser generates a race condition, and can result in false positives where the wallet used does not have the account linked to the domain.
We recommend using [EIP-6963][] by specifying a wallet rnds in the URI field if possible, to avoid this issue.
Wallet connect to any wallet, letting the user decide upon connection
{
"connection": "wc"
}
Using this configuration, the application should provide the QR code and [Wallet Connect][] URI for the user to connect with his wallet from any place. Such configuration can be useful when the user has their wallet in their mobile device or on another platform, such as a web wallet or desktop application that can receive the [EIP-1328] URI and present it to the end-user as a "[Wallet Connect]"-style QR code or otherwise get informed consent from the user to make the relay connection; this has the added benefit of allowing a manual choice among multiple wallets at connection time.
This configuration should never be skipped as it does not impose any platform and requires the user action of connecting from the wallet making this a great last case in the authFlows array and behaving as the default when no other authentication flow is useful.
The only requirement for this configuration to work is that the application supports [ERC-1328]-conformant relay connections such as [Wallet Connect][].
Self-hosting Authentication Flows
In case the user does not trust any authentication flow provider to store their info, they can easily host it themselves using a public serverless function and saving its URL in the ENS authenticator record.
The requirements for the serverless functions are:
- Being accessible from the internet.
- Being able to return the authentication flows JSON object when requested.
- Provide the user with a way to update the authentication flows in case they want to change them.
- Be able to handle the user's domain name as a parameter to return the correct authentication flows when that changes it.
The user can use any service that provides serverless functions, such as Vercel, AWS Lambda, Google Cloud Functions, etc. When this is done for personal reasons, the user should be aware that the serverless function will be public and anyone can access it. Therefore, it is recommended to use a service that provides, at least, rate limiting to avoid abuse.
Direct Authentication Flows Resolution
There are a few cases where the authenticator record can be set to the authentication flows directly.
- The user wants a truly decentralized solution and does not care about the gas cost of storing lots of data in blockchain; then they can write a stringified version of their authentication flows definition in the
authenticatortext record. - Application will resolve only its issued names, therefore the authentication flows or their location are likely already known. In such cases the application can use its own backend to resolve
authenticatorinstead of ENS or another Crypto Domain system NFT system. Responding with the stringified version of the saves the user of the extra request as it does not provide any useful information.
Login With Name Flow

Web3 applications and login modal providers can implement the Login With Name flow following the sequence pictured in the diagram above:
- Users starts the Login With Name process.
- Application requests name and user enters a name linked with the address of the wallet they control and want to authenticate with.
- Application uses its configured name resolvers to resolves the name authentication flows under the
authenticatortext record. If the authenticator text record is a URL, client application sends an HTTP GET request to it in order to obtain users authentication flows. This is not needed if the authenticator text record already has a stringified JSON in it. - Application filters invalid authentication flows and initiates the one that can be completed which could be by opening a new window in the user's browser to the
[Content truncated — view full spec at source]
Discussion (0 threads)
Loading discussions...