From 0038ff3a34647cf1aa458a6291dc90de849ba4bb Mon Sep 17 00:00:00 2001 From: abrichr Date: Fri, 24 Jul 2026 16:42:13 -0400 Subject: [PATCH 1/4] document privileged MFA in trust center --- pages/security.js | 30 +++++++++++++++++++++++++++++- tests/securityMfa.test.js | 16 ++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/securityMfa.test.js diff --git a/pages/security.js b/pages/security.js index f7be58f..1765620 100644 --- a/pages/security.js +++ b/pages/security.js @@ -73,7 +73,7 @@ const summary = [ area: 'Identity, access & tenancy', anchor: 'access', status: 'partial', - note: 'Org RBAC + row-level tenant isolation today; SSO / SAML / SCIM not yet.', + note: 'Org RBAC, row-level tenant isolation, and TOTP step-up protect privileged access; SSO / SAML / SCIM is not included.', }, { area: 'Release integrity', @@ -943,6 +943,34 @@ export default function SecurityPage() { isolated per organization rather than shared across tenants.

+

+ Authenticator-app two-factor authentication is + available to every account and enforced for + privileged access. The platform-admin console + always requires both the server-side administrator + allowlist and a current two-factor session. The + default policy also requires two-factor + authentication for organization owners and admins. + When a signed-in session needs this additional + assurance, Cloud asks for one current 6-digit code + and returns the user to the protected page after it + is accepted. +

+

+ The signed-in account menu keeps identity and + organization context visible. It provides Security + & 2FA, organization switching when an account + belongs to more than one workspace, and an explicit + sign-out action. The{' '} + + account-security guide + {' '} + covers enrollment, step-up, and account or workspace + switching. +

What is not{' '} yet available:{' '} diff --git a/tests/securityMfa.test.js b/tests/securityMfa.test.js new file mode 100644 index 0000000..f0bfb8a --- /dev/null +++ b/tests/securityMfa.test.js @@ -0,0 +1,16 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import path from 'node:path' +import test from 'node:test' +import { fileURLToPath } from 'node:url' + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') +const security = fs.readFileSync(path.join(root, 'pages/security.js'), 'utf8') + +test('trust center describes the privileged MFA and account-menu contract', () => { + assert.match(security, /two-factor authentication is[\s\S]*enforced for[\s\S]*privileged access/i) + assert.match(security, /platform-admin console[\s\S]*server-side administrator[\s\S]*current two-factor session/i) + assert.match(security, /one current 6-digit code[\s\S]*returns the user to the protected page/i) + assert.match(security, /organization switching[\s\S]*explicit[\s\S]*sign-out action/i) + assert.match(security, /docs\.openadapt\.ai\/guides\/account-security/) +}) From bc39a9eebc4a19d8d0e955a6df80a0d560bcd8f5 Mon Sep 17 00:00:00 2001 From: abrichr Date: Fri, 24 Jul 2026 16:43:54 -0400 Subject: [PATCH 2/4] scope MFA claim to platform admin --- pages/security.js | 6 ++---- tests/securityMfa.test.js | 5 +++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pages/security.js b/pages/security.js index 1765620..b77a54e 100644 --- a/pages/security.js +++ b/pages/security.js @@ -73,7 +73,7 @@ const summary = [ area: 'Identity, access & tenancy', anchor: 'access', status: 'partial', - note: 'Org RBAC, row-level tenant isolation, and TOTP step-up protect privileged access; SSO / SAML / SCIM is not included.', + note: 'Org RBAC, row-level tenant isolation, and TOTP step-up protect platform administration; SSO / SAML / SCIM is not included.', }, { area: 'Release integrity', @@ -946,11 +946,9 @@ export default function SecurityPage() {

Authenticator-app two-factor authentication is available to every account and enforced for - privileged access. The platform-admin console + platform administration. The platform-admin console always requires both the server-side administrator allowlist and a current two-factor session. The - default policy also requires two-factor - authentication for organization owners and admins. When a signed-in session needs this additional assurance, Cloud asks for one current 6-digit code and returns the user to the protected page after it diff --git a/tests/securityMfa.test.js b/tests/securityMfa.test.js index f0bfb8a..5cd3389 100644 --- a/tests/securityMfa.test.js +++ b/tests/securityMfa.test.js @@ -7,10 +7,11 @@ import { fileURLToPath } from 'node:url' const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') const security = fs.readFileSync(path.join(root, 'pages/security.js'), 'utf8') -test('trust center describes the privileged MFA and account-menu contract', () => { - assert.match(security, /two-factor authentication is[\s\S]*enforced for[\s\S]*privileged access/i) +test('trust center describes the platform-admin MFA and account-menu contract', () => { + assert.match(security, /two-factor authentication is[\s\S]*enforced for[\s\S]*platform administration/i) assert.match(security, /platform-admin console[\s\S]*server-side administrator[\s\S]*current two-factor session/i) assert.match(security, /one current 6-digit code[\s\S]*returns the user to the protected page/i) assert.match(security, /organization switching[\s\S]*explicit[\s\S]*sign-out action/i) assert.match(security, /docs\.openadapt\.ai\/guides\/account-security/) + assert.doesNotMatch(security, /default policy also requires two-factor[\s\S]*organization owners/i) }) From 6a826b325cdd96ebd2ebf0013fb25bbac63a021c Mon Sep 17 00:00:00 2001 From: abrichr Date: Fri, 24 Jul 2026 16:47:59 -0400 Subject: [PATCH 3/4] document bounded MFA recovery --- pages/security.js | 14 +++++++++++--- tests/securityMfa.test.js | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pages/security.js b/pages/security.js index b77a54e..ce98128 100644 --- a/pages/security.js +++ b/pages/security.js @@ -948,12 +948,20 @@ export default function SecurityPage() { available to every account and enforced for platform administration. The platform-admin console always requires both the server-side administrator - allowlist and a current two-factor session. The + allowlist and a current two-factor session. When a signed-in session needs this additional assurance, Cloud asks for one current 6-digit code and returns the user to the protected page after it is accepted.

+

+ Enrollment also produces one-time recovery codes. + If an authenticator is lost, a recovery code removes + the inaccessible factors and returns the signed-in + user to Security & 2FA to enroll a replacement. + Recovery never grants the protected session by + itself. +

The signed-in account menu keeps identity and organization context visible. It provides Security @@ -966,8 +974,8 @@ export default function SecurityPage() { > account-security guide {' '} - covers enrollment, step-up, and account or workspace - switching. + covers enrollment, step-up, recovery, and account or + workspace switching.

What is not{' '} diff --git a/tests/securityMfa.test.js b/tests/securityMfa.test.js index 5cd3389..28dd35a 100644 --- a/tests/securityMfa.test.js +++ b/tests/securityMfa.test.js @@ -11,6 +11,7 @@ test('trust center describes the platform-admin MFA and account-menu contract', assert.match(security, /two-factor authentication is[\s\S]*enforced for[\s\S]*platform administration/i) assert.match(security, /platform-admin console[\s\S]*server-side administrator[\s\S]*current two-factor session/i) assert.match(security, /one current 6-digit code[\s\S]*returns the user to the protected page/i) + assert.match(security, /recovery code[\s\S]*returns[\s\S]*signed-in[\s\S]*user to Security[\s\S]*never grants the protected session/i) assert.match(security, /organization switching[\s\S]*explicit[\s\S]*sign-out action/i) assert.match(security, /docs\.openadapt\.ai\/guides\/account-security/) assert.doesNotMatch(security, /default policy also requires two-factor[\s\S]*organization owners/i) From f5f2646ceecb37f9aaf7d511086894e06e914a1f Mon Sep 17 00:00:00 2001 From: abrichr Date: Fri, 24 Jul 2026 17:08:47 -0400 Subject: [PATCH 4/4] docs(security): clarify MFA recovery session reset --- pages/security.js | 9 +++++---- tests/securityMfa.test.js | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pages/security.js b/pages/security.js index ce98128..409230c 100644 --- a/pages/security.js +++ b/pages/security.js @@ -957,10 +957,11 @@ export default function SecurityPage() {

Enrollment also produces one-time recovery codes. If an authenticator is lost, a recovery code removes - the inaccessible factors and returns the signed-in - user to Security & 2FA to enroll a replacement. - Recovery never grants the protected session by - itself. + the inaccessible factors. Factor removal invalidates + active sessions, so Cloud explicitly returns through + sign-in and then to Security & 2FA to enroll a + replacement. Recovery never grants the protected + session by itself.

The signed-in account menu keeps identity and diff --git a/tests/securityMfa.test.js b/tests/securityMfa.test.js index 28dd35a..733cb22 100644 --- a/tests/securityMfa.test.js +++ b/tests/securityMfa.test.js @@ -11,7 +11,7 @@ test('trust center describes the platform-admin MFA and account-menu contract', assert.match(security, /two-factor authentication is[\s\S]*enforced for[\s\S]*platform administration/i) assert.match(security, /platform-admin console[\s\S]*server-side administrator[\s\S]*current two-factor session/i) assert.match(security, /one current 6-digit code[\s\S]*returns the user to the protected page/i) - assert.match(security, /recovery code[\s\S]*returns[\s\S]*signed-in[\s\S]*user to Security[\s\S]*never grants the protected session/i) + assert.match(security, /recovery code[\s\S]*invalidates[\s\S]*returns through[\s\S]*sign-in[\s\S]*Security[\s\S]*never grants[\s\S]*protected[\s\S]*session/i) assert.match(security, /organization switching[\s\S]*explicit[\s\S]*sign-out action/i) assert.match(security, /docs\.openadapt\.ai\/guides\/account-security/) assert.doesNotMatch(security, /default policy also requires two-factor[\s\S]*organization owners/i)