-
Notifications
You must be signed in to change notification settings - Fork 3
Description
Feature: Auto-Renew Browser Key Delegation (24 h rolling)
Background
• Every "write" (post / comment / vote / moderate) is signed by a browser-generated Ed25519 key pair.
• That key gains authority through a delegation message signed by the user's wallet and cached in localStorage (KeyDelegation.STORAGE_KEY).
• Delegation is valid for 24 h (DEFAULT_EXPIRY_HOURS = 24).
• After expiry, MessageSigning.signMessage() returns null, the UI shows "Key Delegation Expired" and users must manually re-delegate.
We can smooth this by silently rolling the delegation before it expires.
Goals
- Zero-Friction Renewal – Users who stay logged-in never hit an expiry wall.
- Security Preservation – Each renewal generates a fresh key pair & delegation signed by the wallet (can't skip wallet signature).
- Timely Broadcast – Renewal happens well before expiry (configurable threshold), so queued actions remain signable.
Proposed Flow
┌───────────── first delegation (user clicks "Delegate Key") ─────────────┐
│ │
│ localStorage { sig, browserPubKey, expiryT } │
└──────────────────────────────────────────────────────────────────────────┘
│
every N minutes ▼
(or single timeout) check time-remaining
│ < THRESHOLD?
│
┌────────┴────────┐
│ yes (< 2 h) │
└────────┬────────┘
▼
generate fresh keypair
▼
craft delegation message
▼
wallet.signMessage() ← single popup
▼
storeDelegation(newInfo) + update state
▼
schedule next renewal based on new expiry
Implementation Plan
-
Constants
RENEW_THRESHOLD_MS = 2 * 60 * 60 * 1000// 2 h
RENEW_CHECK_INTERVAL = 15 * 60 * 1000// 15 min -
AuthContext Enhancements
- Add a
useEffectthat runs on mount & whenever delegation changes. - Inside, compute
timeRemaining = delegationTimeRemaining().
• IftimeRemaining <= RENEW_THRESHOLD_MS→await delegateKey('silent').
• Else:setTimeoutfor(timeRemaining - RENEW_THRESHOLD_MS)to trigger renewal. - Keep a fallback
setInterval(RENEW_CHECK_INTERVAL)to cover system-sleep edge cases.
- Add a
-
delegateKey(silent = false)
Parameter controls UX:
•silent === true→ fewer toasts, skip "recommend delegation" banners.
• On wallet rejection: show non-blocking toast but keep old delegation until expiry. -
UI Feedback
• Add small badge in profile dropdown: "Key refreshes in x h m".
• If wallet rejects auto-renew, show toast once and retry on next interval. -
MessageSigning Hot-Swap
No change required: it always queriesKeyDelegation.retrieveDelegation(); oncestoreDelegation()is called the new keys are used automatically. -
Edge Cases
• If the user has been offline and delegation already expired → fallback to current "Key Delegation Required" flow.
• If wallet connection lost → abort renewal and re-prompt when they reconnect.
Acceptance Criteria
- Delegation renews automatically ≥ 1 h before expiry (default 2 h threshold).
- No "Key Delegation Expired" toast appears for continuously logged-in users.
- Only one wallet signature popup appears per renewal.
localStoragecontains latest delegation with rolling 24 h expiry.- Manual Delegate Key button still works and resets the 24 h window.
- Unit test fakes
Date.now()to within 30 min of expiry → auto-renew logic callsdelegateKey().
Code Pointers
| File | Relevance |
|---|---|
src/lib/identity/signatures/key-delegation.ts |
Holds expiry constant & storage helpers. |
src/contexts/AuthContext.tsx |
Delegation flow (delegateKey, isDelegationValid, delegationTimeRemaining). |
src/lib/identity/signatures/message-signing.ts |
Reads delegation when signing messages. |