Files

5.4 KiB

NuQloud Managed Recovery + Emergency Access

This document describes the managed recovery and emergency-access flow added to qortal_integration.

Goal

  • Keep decentralized auth primary.
  • Provide optional recovery support for users who opt in.
  • Ensure admins cannot read recovery data at rest.
  • Require both admin and user participation for recovery reveal.

User-facing behavior

In Personal Settings (/settings/user/connected-accounts, section NuQloud for Nextcloud):

  • User can opt in to Managed Recovery.
  • User can optionally opt in to Emergency Temporary Login Reset.
  • User chooses:
    • linked wallet/account
    • current backup password (verified against wallet backup API)
    • recovery passphrase (local secret known only to user)
  • User can later reveal backup password only with:
    • recovery passphrase, and
    • admin-issued one-time recovery code.

Admin flow is available in Administration settings -> NuQloud for Nextcloud -> Recovery Operations:

  • Check target user recovery status.
  • Issue one-time recovery code (configurable TTL).
  • Start emergency temporary-login window (configurable duration).

Security model

  • The backup password is encrypted server-side into an escrow blob:
    • cipher: aes-256-gcm
    • key derivation: pbkdf2-sha256 with per-record salt and iterations
  • Admin recovery codes are:
    • short-lived,
    • one-time use,
    • stored hashed with HMAC in app config registry.
  • Recovery reveal has lockout controls:
    • failed attempt counter,
    • temporary lock window after repeated failures.
  • Emergency reset is explicit opt-in and time-bounded.

What this is and is not

  • This is an app-level encrypted escrow model, not raw plaintext storage.
  • This is not zero-knowledge from the server runtime perspective during active recovery execution.
  • This does not currently hard-disable centralized login globally; it adds emergency controls around it.
  • Passwords app mirroring is not yet implemented in this pass.

Endpoints

All routes are under /apps/qortal_integration.

User endpoints

  • GET /api/user/recovery/status

    • auth: authenticated user
    • returns current recovery/enrollment/emergency state for the current user.
  • POST /api/user/recovery/enroll

    • auth: authenticated user
    • params:
      • walletId
      • currentBackupPassword
      • recoveryPassphrase
      • recoveryPassphraseConfirm
      • managedEnabled (1)
      • emergencyLoginEnabled (1 or empty)
    • behavior:
      • verifies wallet backup password first,
      • encrypts escrow blob,
      • persists managed recovery settings.
  • POST /api/user/recovery/disable

    • auth: authenticated user
    • clears managed recovery configuration for current user.
  • POST /api/user/recovery/reveal

    • auth: authenticated user
    • params:
      • recoveryPassphrase
      • adminCode
    • behavior:
      • validates and consumes one-time admin code,
      • decrypts escrow if passphrase is valid,
      • returns backup password once.
  • POST /api/user/recovery/emergency/complete

    • auth: authenticated user
    • marks emergency flow as completed after user replaces backup password.

Admin endpoints

  • POST /api/admin/recovery/code

    • auth: ops admin (admin or delegated MSP admin)
    • params:
      • targetUserId
      • expiresMinutes (default 15, bounded)
    • behavior:
      • issues one-time recovery code for target user,
      • returns code and expiration.
  • POST /api/admin/recovery/emergency

    • auth: ops admin
    • params:
      • targetUserId
      • durationHours (default 24, bounded)
    • behavior:
      • requires user opted into managed + emergency modes,
      • sets temporary Nextcloud password for target user,
      • sets emergency-required and emergency-expiry flags.

Stored app/user config keys

User-scope keys (IConfig::setUserValue, app qortal_integration):

  • user_recovery_managed_enabled
  • user_recovery_emergency_login_enabled
  • user_recovery_wallet_id
  • user_recovery_escrow_blob
  • user_recovery_escrow_updated_at
  • user_recovery_failed_attempts
  • user_recovery_locked_until
  • user_recovery_emergency_until
  • user_recovery_emergency_required
  • user_recovery_emergency_started_by
  • user_recovery_emergency_started_at

App-scope key:

  • recovery_admin_code_registry

Verify on devcloud

Use your normal compose file + env file pattern:

docker compose -f docker-compose.devprod.nossl.yml --env-file .env.devprod exec --user www-data nextcloud php -l /var/www/html/custom_apps/qortal_integration/lib/Controller/ApiController.php
docker compose -f docker-compose.devprod.nossl.yml --env-file .env.devprod exec --user www-data nextcloud php -l /var/www/html/custom_apps/qortal_integration/lib/Settings/PersonalSettings.php
docker compose -f docker-compose.devprod.nossl.yml --env-file .env.devprod exec --user www-data nextcloud php -l /var/www/html/custom_apps/qortal_integration/templates/personal.php

Functional smoke:

  1. User enables managed recovery in personal settings.
  2. Admin issues recovery code for that user.
  3. User reveals backup password with admin code + recovery passphrase.
  4. Admin triggers emergency reset (if user opted in).
  5. User logs in, rotates backup password, clicks emergency complete.

Known follow-up work

  • Optionally integrate with Nextcloud Passwords app if a stable API contract is confirmed for target NC versions.
  • Add hard enforcement of emergency completion in login/post-login policy flow if required.