diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 0127649586..41e75d5147 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -138,12 +138,18 @@ When email/password signin and signup are disabled (`NEXT_PUBLIC_DISABLE_EMAIL_P - The URL carries `#embedded=true` (embedded signing widgets must not bounce to the IdP). - The URL carries `?direct=1` and the break-glass allowlist is configured. -**Break-glass** (`NEXT_PRIVATE_BREAK_GLASS_EMAILS`, comma-separated admin emails): with password signin disabled suite-wide, allowlisted admins keep a manual password escape hatch via `/signin?direct=1` for use when the OIDC provider is unreachable. The allowlist is enforced server-side in `POST /api/auth/email-password/authorize` (non-allowlisted emails still receive `SigninDisabled`), so the credential-stuffing surface stays limited to the admin emails. Regular users have no password path. +**Break-glass** (`NEXT_PRIVATE_BREAK_GLASS_EMAILS`, comma-separated admin emails): with password signin disabled suite-wide, allowlisted admins keep a manual password escape hatch via `/signin?direct=1` for use when the OIDC provider is unreachable. The allowlist is enforced server-side in `POST /api/auth/email-password/authorize` (the gate sits after the rate limiter and CSRF/captcha checks, and rejects with the same `INVALID_CREDENTIALS` error as a wrong password). Regular users have no password path. -Security notes: the `SigninDisabled` vs `InvalidCredentials` distinction means a prober can infer allowlist membership; this is accepted as rate-limited, low-signal (admin emails are rarely secret). The break-glass also overrides the suite-wide `NEXT_PUBLIC_DISABLE_SIGNIN` master switch for allowlisted admins by design, so a login lockout incident still leaves admins a recovery path. The forgot-password flow stays disabled in this mode; the break-glass form hides its "Forgot your password?" link to avoid a dead end. +Security notes: every credential-check rejection path on `/authorize` pays an identical bcrypt cost (early-rejected paths compare the submitted password against a precomputed dummy hash) and returns identical status and error body, so allowlist membership and account existence cannot be probed from error codes or response timing. Pre-auth rejections (rate limit, CSRF, captcha) and distinct failures (service accounts, 2FA, unverified email) are uniform per path and reveal no allowlist information. The break-glass also overrides the suite-wide `NEXT_PUBLIC_DISABLE_SIGNIN` master switch for allowlisted admins by design, so a login lockout incident still leaves admins a recovery path. The forgot-password flow stays disabled in this mode; the break-glass form hides its "Forgot your password?" link to avoid a dead end. Deep links are preserved end to end: unauthenticated access to authenticated routes redirects to `/signin?returnTo=`, and `returnTo` is validated (`isValidReturnTo`) and carried through the OIDC round-trip back to the original page. +### 4.4. Embedded Signing Auth Constraint + +Recipient signing is token-gated by design: `/sign/$token` and `/d/$token` (and direct-link templates under the embed surface) authorize the recipient via the URL token, with no login required. This matches the ecosystem's non-intrusive UX rule (CRM/Desk users must never be forced into Sign accounts) and is the intended pattern for embedding - comparable to DocuSign-style embedded signing URLs. + +**Constraint under redirect-only OIDC:** when a document or template's access auth is set to `ACCOUNT` and the signing page is opened in an embedded context (`#embedded=true`), the widget cannot offer an OIDC login: the IdP login page is not framed into the widget (and must not be - the IdP blocks/should block framing), the password path is disabled suite-wide, and no popup OIDC flow exists. The widget falls back to passkey sign-in only (subject to the embedding page's `publickey-credentials-get` Permissions Policy). Until a popup-based OIDC flow is deliberately built, embedded deployments must use token-based access auth (or open the signing link in a normal tab when account auth is required). This is a documented product constraint, not a bug. + --- ## 5. Crove OS 2-Tier Hybrid Architecture & Data Sync