Skip to content

feat: automated implicit key delegation #5

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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions