- Updated the GDPR retention and purging cycle for candidate applications, clarifying the eligibility for purge and retention rules for supporting documents. - Revised the archiving process for terminated leases, specifying retention periods for tenant and guarantor documents. - Introduced a new ADR for sharing a single landlord vault across applications, tenants, and leases, ensuring consistent encryption and access management. - Enhanced the document generation process to ensure confidential data remains encrypted and only processed in the browser. - Improved the candidate application access flow, allowing for more flexibility in managing tenant information and supporting documents. - Updated the QR code styling for public application links to enhance visual branding and usability. - Added a new specification for simplifying the candidate application process, focusing on clarity and user experience.
9.5 KiB
Encrypt candidate applications outside the server
Status: accepted
For issue #146, ADR-0036 supersedes this decision's application-only vault, passkey and reset scope.
Candidate identity, contact, professional, resource, guarantor and supporting-document content is encrypted in the candidate's browser before upload. Every file's bytes, filename, business document type and thumbnail are ciphertext. Clear application metadata is limited to technical application, apartment and landlord identifiers; lifecycle state, technical revision and timestamps; purge deadline; encrypted-object count and sizes; and cryptographic-format version. The access capability is stored only as a hash, key envelopes remain encrypted, and source IP is transient rate-limit data rather than application metadata. The candidate's private resume link carries two independent high-entropy secrets in its URL fragment, which the browser omits from the initial request: the client forwards only the access capability in an authorization header, while the encryption secret never leaves the browser. Sensitive routes use Cache-Control: no-store and Referrer-Policy: no-referrer; fragments, capabilities, authorization headers, personal data, decrypted content and sensitive payloads are excluded from logs and telemetry. The complete link lets the candidate resume the same draft, while the landlord's private key material remains outside the server; OIDC authorizes landlord retrieval and commands but does not enable decryption. Neither the server nor the landlord can reconstruct a lost complete link, so its loss requires a new application and the abandoned draft follows its normal inactivity expiry. The authenticated landlord separately unlocks submitted applications in the browser, where display, completeness and sorting are derived.
Candidate-application passkeys are not required for OIDC authentication or unrelated features. A landlord without one is asked to register it only when creating their first public application link, after a dialog explains the encryption and irrecoverability consequences; no link is created if they cancel or registration fails. Each landlord may then register multiple LMNP Organizer WebAuthn credentials, distinct from any passkey used at the OIDC provider. A credential whose PRF extension is confirmed at registration derives a local wrapping key for a separate envelope of the same landlord private key. Each wrapped plaintext also carries its own random vault-management capability; the server stores only the corresponding passkey's hash, and each management request must prove possession of the capability belonging to the named credential after local unwrap. The server stores the credential identifier, a landlord-chosen label, creation and last-use timestamps, and the wrapped envelope, never the PRF output, capability plaintext or private key. Independent or synchronized passkeys may therefore unlock the same applications from compatible Windows, Android and macOS combinations, but the application cannot guarantee that two credentials use independent synchronized managers and only recommends a second device or manager. Runtime PRF failure prevents link creation and presents compatibility guidance without falling back to a PIN or server-held key; unrelated features remain available. One credential is sufficient. Giving a new passkey access to existing data requires OIDC authentication plus proof from the already unlocked current credential; revoking one requires OIDC authentication plus proof from another named registered credential. Adding a credential performs an existing-passkey ceremony to unwrap the private-key bytes only long enough to create the new envelope, then imports the operational private key as non-extractable and discards the bytes. OIDC alone cannot recreate lost decryption capability, and loss of every registered passkey makes the ciphertext irrecoverable. Ordinary revocation always requires another registered passkey, so the final credential is intentionally removed only by the explicit irreversible reset, which closes all links, revokes the credentials and makes every remaining application immediately purge-eligible.
This design protects candidate ciphertext already stored when an attacker gains full control of the host, but only until the landlord next unlocks it through the web client served by that compromised host. The minimal identity and contact facts deliberately copied into the current plaintext tenant model on selection remain readable until issue #146. Such an attacker can modify the client to capture a PRF result or plaintext, substitute the public key used by future candidates, delete ciphertext, or alter clear metadata; authenticated encryption detects content tampering but does not preserve availability. Protecting data during use against that attacker would require an independently trusted signed client or dedicated hardware and is outside this decision.
The unlocked private CryptoKey is non-extractable and never written by the application to localStorage, sessionStorage or IndexedDB. Structured cloning through a high-entropy BroadcastChannel name bound to the authenticated HTTP session shares its opaque handle only with LMNP Organizer tabs that have verified that session in the same browser profile and origin, avoiding repeated passkey ceremonies without persistence. Anonymous tabs never receive the channel name. Logout or OIDC expiry asks every cooperating tab to discard its reference and removes the session-bound channel name; once a key has been cloned into a tab, the browser cannot cryptographically revoke that tab's access, and closing every tab or restarting the browser is the hard end of this ephemeral unlock.
An irreversible vault reset requires OIDC plus a reinforced typed confirmation, not a working application passkey. Its start transaction closes every public application link before persisting an in-progress marker; that marker blocks ordinary landlord and candidate commands while an interrupted reset is resumed. Completion invalidates every private capability, moves every remaining application’s purge deadline to the reset start, removes the vault and its passkey envelopes, and records completion atomically. It deliberately preserves application ciphertext and file artifacts for the coordinated purge in issue #39. Replaying the completed operation is a no-op, so a later activation creates a new vault for future applications without allowing a delayed retry to remove it. The initiating tab discards its key before the request and broadcasts that discard without ending the OIDC session.
The initial suite is versioned and uses only native Web Crypto primitives. Every structured or binary application object uses AES-256-GCM with a fresh random 96-bit nonce and a 128-bit authentication tag. Authenticated additional data binds the suite version, landlord identifier, application identifier, object identifier and expected technical revision, so a ciphertext cannot be moved silently into another context; optimistic revision checks reject ordinary stale writes. This does not detect a fully compromised host rolling back both a valid previous ciphertext and its matching metadata. The candidate's random 256-bit application key is transported under the landlord public key with RSA-OAEP 3072 and SHA-256. For each credential, HKDF-SHA-256 derives an AES-256-GCM wrapping key from its WebAuthn PRF result and versioned random salt; that envelope's plaintext also contains a separate random vault-management capability, while the server stores only the hash associated with that credential. Every passkey-management request must present the capability extracted by a successful local unwrap of the named authorizing credential; OIDC and a caller-supplied credential identifier alone are insufficient. A fresh nonce protects each landlord RSA private-key envelope. Nonce reuse is forbidden, application random values come from crypto.getRandomValues, and no custom cryptographic algorithm or dependency is introduced.
Because the server cannot inspect encrypted uploads for malware, candidate evidence is restricted to PDF, JPEG and PNG with browser-side binary-signature validation before encryption and after decryption. Archives, Office documents, SVG, HTML and executables are rejected rather than rendered as active content. A source file is limited to 25 MB and one application to 250 MB of stored ciphertext; client-side source checks are backed by server-side encrypted-object and aggregate limits that account for versioned cryptographic overhead.
This decision refines ADR-0029 for candidate applications: the OIDC-authenticated landlord remains the only application user allowed to retrieve and operate on portfolio ciphertext, while public and private candidate capability links grant no portfolio access. Revoking a capability prevents future server access but cannot erase ciphertext and keys already copied by its holder; this is disclosed without attempting browser DRM. Encryption supplements rather than replaces authorization, retention and the coordinated purge owned by issue #39.
Selection may transfer only landlord-confirmed structured identity and contact facts into the current tenant model; it never copies application evidence automatically. The encrypted application copies remain governed by their 90-day purge. Selective promotion of still-necessary ciphertext and end-to-end encryption of tenant and lease records are deliberately deferred to issue #146 rather than weakening this boundary through the current plaintext tenant attachments.