archy/docs/DID_ONBOARDING_ASSESSMENT.md
Dorian 62d6c13764 Implement onboarding reset functionality and enhance backup features
- 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.
2026-03-02 08:34:13 +00:00

190 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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**: ~24 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**: ~12 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**: ~12 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**: ~23 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` |