Decentralized Identifiers (DIDs)
The W3C DID Standard
Definition: A globally unique identifier that doesn't require a central registration authority.
DID syntax:
did:method:method-specific-identifier
Examples:
did:ethr:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb
did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH
did:web:example.com
did:ion:EiDyOQbbZAa3aiRzeCkV7LOx3SERjjH93EXoIM3UoN4oWg
Components:
did:ethr:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb
↑ ↑ ↑
| | └── Unique identifier (Ethereum address)
| └──────────── Method (how to resolve)
└───────────────── Scheme (always "did")
DID Document:
{
"@context": "https://www.w3.org/ns/did/v1",
"id": "did:ethr:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"verificationMethod": [{
"id": "did:ethr:0x742d35...#keys-1",
"type": "EcdsaSecp256k1VerificationKey2019",
"controller": "did:ethr:0x742d35...",
"publicKeyHex": "04ab5d8a2f..."
}],
"authentication": ["did:ethr:0x742d35...#keys-1"],
"service": [{
"id": "did:ethr:0x742d35...#vcs",
"type": "VerifiableCredentialService",
"serviceEndpoint": "https://example.com/vc"
}]
}
Key properties:
1. Self-sovereign:
- You control your identifier
- No company can revoke it
- No permission needed to create
2. Resolvable:
- DID → DID Document (public keys, endpoints)
- Multiple resolution methods
- Works across systems
3. Cryptographically verifiable:
- Sign with private key
- Others verify with public key from DID Document
- No central authority needed
4. Persistent:
- Same identifier forever
- Can update keys/services
- Portable across platforms
DID Methods
Different methods for different use cases:
1. did:ethr (Ethereum-based)
// ERC-1056: Ethereum DID Registry
contract EthereumDIDRegistry {
mapping(address => address) public owners;
mapping(address => mapping(bytes32 => mapping(address => uint))) public delegates;
// Change owner
function changeOwner(address identity, address newOwner) public {
require(msg.sender == owners[identity] || msg.sender == identity);
owners[identity] = newOwner;
emit DIDOwnerChanged(identity, newOwner);
}
// Add delegate (e.g., for signing)
function addDelegate(
address identity,
bytes32 delegateType,
address delegate,
uint validity
) public {
require(msg.sender == owners[identity] || msg.sender == identity);
delegates[identity][delegateType][delegate] = block.timestamp + validity;
emit DIDDelegateChanged(identity, delegateType, delegate, validity);
}
}
// Usage:
// DID: did:ethr:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb
// Resolution: Query registry for owner and delegates
// Verification: Check if public key is registered delegate
2. did:key (Self-contained)
Format: did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH
└─────────────────────────────────┘
Base58-encoded public key
DID Document derived from key itself:
{
"id": "did:key:z6Mkp...",
"verificationMethod": [{
"id": "did:key:z6Mkp...#z6Mkp...",
"type": "Ed25519VerificationKey2020",
"controller": "did:key:z6Mkp...",
"publicKeyMultibase": "z6Mkp..."
}]
}
Benefits:
+ No blockchain needed
+ No registration cost
+ Instant creation
+ Works offline
Drawbacks:
- Cannot update keys (key = identity)
- Cannot add services
- Limited functionality
3. did:web (Web-based)
Format: did:web:example.com:user:alice
Resolution:
GET https://example.com/user/alice/did.json
Returns DID Document from web server
Benefits:
+ Familiar infrastructure (HTTPS)
+ Easy for companies to adopt
+ Can use existing domains
Drawbacks:
- Centralized (domain owner controls)
- Can be censored
- Subject to DNS/CA system
4. did:ion (Bitcoin-anchored, decentralized)
Microsoft's ION system:
- Uses Bitcoin for anchoring
- IPFS for data storage
- Sidetree protocol
Process:
1. Create DID locally
2. Publish DID operations to IPFS
3. Anchor IPFS hash on Bitcoin
4. Anyone can resolve by reading Bitcoin + IPFS
Benefits:
+ Highly decentralized
+ Bitcoin security
+ No tokens needed (no gas)
Drawbacks:
- Complex resolution
- Slow updates (Bitcoin block time)
- Still maturing
How DID Resolution Works
User has: did:ethr:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb
Step 1: Parse DID
method = "ethr"
identifier = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
Step 2: Select resolver based on method
if method == "ethr":
resolver = EthereumDIDResolver()
elif method == "key":
resolver = KeyDIDResolver()
elif method == "web":
resolver = WebDIDResolver()
Step 3: Resolve DID → DID Document
document = resolver.resolve(identifier)
Step 4: Extract verification methods
public_keys = document["verificationMethod"]
services = document["service"]
Step 5: Use for verification
signature = "0x1a2b3c..."
message = "I authenticate with this DID"
is_valid = verify(public_keys[0], message, signature)
Example: Authentication flow
// 1. User wants to authenticate with website
const did = "did:ethr:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb";
// 2. Website generates challenge
const challenge = "Sign this message at " + Date.now();
// 3. User signs with their private key
const signature = await wallet.signMessage(challenge);
// 4. Website resolves DID to get public key
const didDocument = await resolve(did);
const publicKey = didDocument.verificationMethod[0].publicKeyHex;
// 5. Website verifies signature
const isValid = verifySignature(challenge, signature, publicKey);
// 6. If valid, user is authenticated
if (isValid) {
// User proved control of DID
// No password needed!
authenticateUser(did);
}