Skip to content

feat: automated implicit key delegation #5

@danisharora099

Description

@danisharora099

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

  1. Zero-Friction Renewal – Users who stay logged-in never hit an expiry wall.
  2. Security Preservation – Each renewal generates a fresh key pair & delegation signed by the wallet (can't skip wallet signature).
  3. 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

    1. Add a useEffect that runs on mount & whenever delegation changes.
    2. Inside, compute timeRemaining = delegationTimeRemaining().
      • If timeRemaining <= RENEW_THRESHOLD_MSawait delegateKey('silent').
      • Else: setTimeout for (timeRemaining - RENEW_THRESHOLD_MS) to trigger renewal.
    3. Keep a fallback setInterval(RENEW_CHECK_INTERVAL) to cover system-sleep edge cases.
  • 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 queries KeyDelegation.retrieveDelegation(); once storeDelegation() 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.
  • localStorage contains 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 calls delegateKey().

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions