- Added a new method to reset the onboarding state, allowing users to re-initiate the onboarding process. - Integrated backup creation functionality, enabling users to create encrypted backups of their node identity. - Updated API endpoints to handle onboarding reset and backup creation requests. - Enhanced UI components to support the new onboarding reset and backup features, including error handling and user feedback. - Introduced new dependencies for cryptographic operations and data encoding.
190 lines
7.5 KiB
Markdown
190 lines
7.5 KiB
Markdown
# DID Onboarding Flow: Assessment & Implementation Plan
|
||
|
||
## Executive Summary
|
||
|
||
The current onboarding DID flow is **partially implemented** and has several significant gaps compared to the W3C DID protocol and Web5 expectations. The core `did:key` format is **correct**, but the user-facing flow includes mock/fake behavior and the backup/verify steps don't actually use the DID infrastructure.
|
||
|
||
---
|
||
|
||
## What We Have (Current State)
|
||
|
||
### ✅ Correct Implementation
|
||
|
||
| Component | Status | Notes |
|
||
|-----------|--------|-------|
|
||
| **did:key format** | ✅ Correct | Ed25519 multicodec `0xed 0x01`, base58btc encoding, `z` prefix |
|
||
| **Key generation** | ✅ Correct | Ed25519 via `ed25519_dalek`, persisted at `/var/lib/archipelago/identity/` |
|
||
| **node.did RPC** | ✅ Correct | Returns `{ did, pubkey }` from server state |
|
||
| **Identity persistence** | ✅ Correct | Key survives reboots, 0o600 permissions on Unix |
|
||
| **Sign/verify primitives** | ✅ Present | `NodeIdentity::sign()`, `NodeIdentity::verify()` exist in Rust |
|
||
|
||
### ⚠️ Partial / Misleading Implementation
|
||
|
||
| Component | Status | Issue |
|
||
|-----------|--------|-------|
|
||
| **OnboardingDid.vue** | ⚠️ Misleading copy | Says "Generate DID" but we *fetch* from server; key is created at first boot, not during onboarding |
|
||
| **OnboardingVerify.vue** | ❌ Fake | Uses `generateMockSignature()` – random chars, no backend call. Doesn't prove DID control |
|
||
| **OnboardingBackup.vue** | ❌ Non-functional | Backup is mock JSON with `{ did, kid }`; no encrypted key material; **restore is impossible** |
|
||
| **kid usage** | ⚠️ Non-standard | We store `pubkey` as `kid`; proper did:key uses fragment like `#key-1` or `did:key:z...#key-1` |
|
||
|
||
### ❌ Missing
|
||
|
||
| Component | Status |
|
||
|-----------|--------|
|
||
| **node.sign RPC** | Not exposed – backend can sign but no API |
|
||
| **Challenge-sign flow** | No backend support for proof-of-control |
|
||
| **Encrypted backup** | No real backup with key material or recovery path |
|
||
| **DID Document endpoint** | Not exposed (optional for did:key – can be derived client-side) |
|
||
| **keyAgreement / X25519** | Not derived – full DID Document would need Ed25519→X25519 for encryption |
|
||
|
||
---
|
||
|
||
## DID Protocol Requirements (W3C / Web5)
|
||
|
||
### did:key Method (W3C CCG)
|
||
|
||
1. **Format**: `did:key:z<base58btc(multicodec + raw-public-key-bytes)>` ✅ We do this
|
||
2. **DID Document**: Can be derived from the DID string; no registry. Libraries like `@digitalcredentials/did-method-key` expand it.
|
||
3. **Verification methods**: `verificationMethod`, `authentication`, `assertionMethod`, `keyAgreement` (X25519 derived), `capabilityDelegation`, `capabilityInvocation`
|
||
4. **Key ID (kid)**: Typically `{did}#key-1` or similar fragment
|
||
|
||
### Proof of Control
|
||
|
||
To prove control of a DID, you must **sign a challenge** with the private key. The verifier checks the signature against the public key in the DID. Our OnboardingVerify step claims to do this but **does not**.
|
||
|
||
### Backup / Recovery
|
||
|
||
A proper identity backup for recovery would:
|
||
|
||
- Include the private key (or encrypted key material)
|
||
- Be encrypted with a user passphrase
|
||
- Allow restore on a new device
|
||
|
||
Our backup has none of this – it's display-only.
|
||
|
||
---
|
||
|
||
## Recommended Implementation Plan
|
||
|
||
### Phase 1: Fix Verify Step (Proof of Control)
|
||
|
||
**Goal**: Replace the fake "Sign Challenge" with a real cryptographic proof.
|
||
|
||
1. **Backend**: Add `node.signChallenge` RPC
|
||
- Input: `{ challenge: string }` (nonce from frontend)
|
||
- Output: `{ signature: string }` (hex-encoded Ed25519 signature)
|
||
- Uses `NodeIdentity::sign()` with `challenge.as_bytes()`
|
||
|
||
2. **Frontend (OnboardingVerify.vue)**:
|
||
- Generate a random nonce (e.g. 32 bytes, base64)
|
||
- Call `node.signChallenge({ challenge })`
|
||
- Verify signature locally using the pubkey from `node.did` (optional – or trust server)
|
||
- Display the real signature; remove `generateMockSignature()`
|
||
|
||
**Effort**: ~2–4 hours
|
||
|
||
---
|
||
|
||
### Phase 2: Improve UX and Terminology
|
||
|
||
**Goal**: Align copy and flow with actual behavior.
|
||
|
||
1. **OnboardingDid.vue**:
|
||
- Change "Generate DID" → "Get your node's identity" or "Retrieve DID"
|
||
- Clarify that the DID is created when the node first starts (not on button click)
|
||
- Optionally auto-fetch on mount if identity exists (no button needed for returning state)
|
||
|
||
2. **kid / Key ID**:
|
||
- Use `#key-1` or full `{did}#key-1` in backup and state
|
||
- Or follow [did:key key IDs](https://www.w3.org/TR/did-core/#relative-did-urls)
|
||
|
||
**Effort**: ~1–2 hours
|
||
|
||
---
|
||
|
||
### Phase 3: Real Backup (Encrypted Export)
|
||
|
||
**Goal**: Backup that can actually be used for recovery.
|
||
|
||
**Design choice**: The private key lives on the **server**. Two options:
|
||
|
||
- **Option A (simpler)**: Backup is a signed, encrypted blob containing the key material. Restore requires:
|
||
- Upload backup file
|
||
- Enter passphrase
|
||
- Server imports key and replaces current identity (or restores to same node)
|
||
|
||
- **Option B (more self-sovereign)**: User can export key to their own wallet. Higher complexity and key-handling risk.
|
||
|
||
**Recommended: Option A**
|
||
|
||
1. **Backend**: Add `node.createBackup` RPC
|
||
- Input: `{ passphrase: string }`
|
||
- Encrypt the raw key bytes (e.g. XChaCha20-Poly1305 or AES-256-GCM) with a key derived from passphrase (Argon2)
|
||
- Return JSON: `{ version, did, backupBlob (base64), salt, ... }` or trigger download
|
||
|
||
2. **Backend**: Add `node.restoreBackup` RPC (for restore flow)
|
||
- Input: `{ backupBlob, passphrase }`
|
||
- Decrypt, validate, write to identity dir
|
||
- Restart or reload identity
|
||
|
||
3. **Frontend (OnboardingBackup.vue)**:
|
||
- Call `node.createBackup` instead of building mock JSON locally
|
||
- Download the real backup file
|
||
|
||
4. **Restore flow**: Add a restore path (e.g. from login or onboarding options) that accepts backup file + passphrase and calls `node.restoreBackup`
|
||
|
||
**Effort**: ~1–2 days (crypto, testing, edge cases)
|
||
|
||
---
|
||
|
||
### Phase 4: DID Document & Web5 Interop (Optional)
|
||
|
||
**Goal**: Full compatibility with Web5 resolvers and DWN.
|
||
|
||
1. **DID Document endpoint**: `GET /.well-known/did.json` or `/did/{did}`
|
||
- Resolve did:key to a full DID Document
|
||
- Include `verificationMethod`, `authentication`, `keyAgreement` (X25519 from Ed25519)
|
||
- Reference: [did:key expansion](https://github.com/digitalbazaar/did-method-key)
|
||
|
||
2. **X25519 derivation**: Add `curve25519-dalek` or equivalent; derive X25519 pubkey from Ed25519 for `keyAgreement`
|
||
|
||
3. **Web5/DWN**: Ensure `web5-dwn` and `did-wallet` use our node DID correctly for resolution and operations
|
||
|
||
**Effort**: ~2–3 days
|
||
|
||
---
|
||
|
||
### Phase 5: DID as Authentication (Future)
|
||
|
||
**Goal**: Use DID + proof instead of (or in addition to) password.
|
||
|
||
- DID Auth / SIOP flow: prove control of DID via challenge-response
|
||
- Could reduce or replace password for API access
|
||
- Larger design and security review required
|
||
|
||
**Effort**: TBD
|
||
|
||
---
|
||
|
||
## Priority Recommendation
|
||
|
||
| Priority | Phase | Reason |
|
||
|----------|-------|--------|
|
||
| **P0** | Phase 1 (Verify) | Removes fake crypto; proves DID control |
|
||
| **P1** | Phase 2 (UX) | Quick wins; honest representation of flow |
|
||
| **P2** | Phase 3 (Backup) | Makes backup/restore actually useful |
|
||
| **P3** | Phase 4 (DID Doc) | Needed for full Web5 interop |
|
||
| **P4** | Phase 5 (DID Auth) | Longer-term identity architecture |
|
||
|
||
---
|
||
|
||
## Quick Reference: Current vs. Target
|
||
|
||
| Step | Current | Target |
|
||
|------|---------|--------|
|
||
| DID fetch | `node.did` ✅ | Same, better UX |
|
||
| Prove control | Fake random "signature" ❌ | Real `node.signChallenge` |
|
||
| Backup | Mock JSON, no key ❌ | Encrypted key material + restore |
|
||
| kid | Raw pubkey | `#key-1` or standard fragment |
|
||
| Restore | Not possible | `node.restoreBackup` |
|