Files

1401 lines
67 KiB
JavaScript

import assert from 'node:assert/strict'
import { createServer, request as httpRequest } from 'node:http'
import test from 'node:test'
import {
createBrowserAuthentication,
AUTH_CSRF_COOKIE,
AUTH_RETRY_COOKIE,
DSH_SESSION_COOKIE,
MANAGEMENT_CSRF_COOKIE,
MANAGEMENT_SESSION_COOKIE,
TOTP_LOGIN_COOKIE,
} from '../lib/browser-auth.mjs'
import { safeReturnPath } from '../lib/proxy.mjs'
async function listen(server) {
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(0, '127.0.0.1', resolve)
})
return server.address().port
}
async function close(server) {
server.closeAllConnections?.()
await new Promise(resolve => server.close(resolve))
}
function request(port, { path = '/', method = 'GET', headers = {}, body } = {}) {
return new Promise((resolve, reject) => {
const outgoing = httpRequest({ hostname: '127.0.0.1', port, path, method, headers }, response => {
const chunks = []
response.on('data', chunk => chunks.push(chunk))
response.on('end', () => resolve({
status: response.statusCode,
headers: response.headers,
body: Buffer.concat(chunks).toString('utf8'),
}))
})
outgoing.once('error', reject)
outgoing.end(body)
})
}
function assertInlineScriptsCompile(html) {
const scripts = [...html.matchAll(/<script>([\s\S]*?)<\/script>/g)].map(match => match[1])
assert.ok(scripts.length > 0, 'expected at least one inline script')
for (const script of scripts) assert.doesNotThrow(() => new Function(script))
}
function fixture(initialState = 'never-initialized', options = {}) {
let state = initialState
const sessions = new Map()
const managementSessions = new Map()
const managementSources = new Map()
const handoffs = new Map()
const pending = new Map()
const transitionNonces = new Map([['transition-valid', 'nonce-valid']])
const continuations = new Map([['continuation-valid', null]])
const calls = []
const access = {
request: async (method, path, body) => {
calls.push({ method, path, body })
if (path === '/v1/status') return {
state,
authenticationContext: 'dsh-authentication-context',
account: state === 'initialized'
? {
managementAdditionalCredential: { enabled: options.managementAdditionalEnabled ?? true },
totp: { enabled: options.totpEnabled === true },
managementAccess: options.managementAccess
?? { mode: 'compat', isolatedEntry: null, dshPublicOrigin: null },
}
: null,
}
if (path === '/v1/authentication-retry/status') {
return options.totpRetry ?? { kind: null, retryAfterSeconds: 0 }
}
if (path === '/v1/dsh/initialize') {
state = 'initialized'
sessions.set('dshs_created', body.origin)
return { session: { token: 'dshs_created', csrfToken: 'dshc_created' } }
}
if (path === '/v1/dsh/reset-authentication') {
if (body.setupKey !== 'dshak_valid') throw Object.assign(new Error('authentication reset key is invalid or expired'), {
code: 'AUTHENTICATION_RESET_KEY_INVALID', statusCode: 401,
})
state = 'initialized'
sessions.set('dshs_reset', body.origin)
return { session: { token: 'dshs_reset', csrfToken: 'dshc_reset' } }
}
if (path === '/v1/dsh/login') {
if (body.password !== 'correct password') throw Object.assign(new Error('username or password is incorrect'), {
code: 'AUTHENTICATION_FAILED', statusCode: 401,
})
if (options.totpEnabled === true) {
return { totpRequired: true, challenge: { token: 'dshtl_login', expiresAt: new Date(Date.now() + 300_000).toISOString() } }
}
sessions.set('dshs_logged_in', body.origin)
return { session: { token: 'dshs_logged_in', csrfToken: 'dshc_logged_in' } }
}
if (path === '/v1/dsh/totp/complete') {
if (body.challengeToken !== 'dshtl_login') throw Object.assign(new Error('login challenge invalid'), {
code: 'TOTP_LOGIN_INVALID', statusCode: 401,
})
if (body.code !== '123456') throw Object.assign(new Error('authentication code is incorrect'), {
code: 'TOTP_INVALID', statusCode: 401,
})
sessions.set('dshs_totp', body.origin)
return { session: { token: 'dshs_totp', csrfToken: 'dshc_totp' } }
}
if (path === '/v1/sessions/validate') return {
authenticated: body.kind === 'dsh'
? sessions.get(body.token) === body.origin
: managementSessions.get(body.token) === body.origin
&& sessions.has(managementSources.get(body.token)),
}
if (path === '/v1/sessions/logout') {
if (sessions.delete(body.token)) {
for (const [token, source] of managementSources) {
if (source !== body.token) continue
managementSources.delete(token)
managementSessions.delete(token)
}
}
managementSessions.delete(body.token)
managementSources.delete(body.token)
return { authenticated: false }
}
if (path === '/v1/dsh/browser-logout') {
let managementRevoked = 0
for (const [token, source] of managementSources) {
if (source !== body.dshToken) continue
managementSources.delete(token)
managementSessions.delete(token)
managementRevoked += 1
}
if (typeof body.managementToken === 'string' && managementSessions.delete(body.managementToken)) {
managementSources.delete(body.managementToken)
managementRevoked += 1
}
const dshRevoked = body.scope === 'all' && sessions.delete(body.dshToken)
return { authenticated: body.scope !== 'all', dshRevoked, managementRevoked }
}
if (path === '/v1/dsh/capabilities') {
if (sessions.get(body.dshToken) !== body.origin) {
throw Object.assign(new Error('session invalid'), { statusCode: 401 })
}
return { capability: { token: 'dshcap_plugin', expiresAt: new Date(Date.now() + 5_000).toISOString() } }
}
if (path === '/v1/management/handoffs') {
if (sessions.get(body.dshToken) !== body.dshOrigin) throw Object.assign(new Error('session invalid'), { statusCode: 401 })
handoffs.set('dshh_created', { targetOrigin: body.targetOrigin, dshToken: body.dshToken })
return { handoff: { token: 'dshh_created' } }
}
if (path === '/v1/management/handoffs/consume') {
const handoff = handoffs.get(body.token)
if (handoff?.targetOrigin !== body.origin) throw Object.assign(new Error('handoff invalid'), { statusCode: 401 })
handoffs.delete(body.token)
if (options.managementPending === true) {
pending.set('dshmp_exchanged', handoff.dshToken)
return { pending: { token: 'dshmp_exchanged' } }
}
managementSessions.set('dshms_exchanged', body.origin)
managementSources.set('dshms_exchanged', handoff.dshToken)
return { session: { token: 'dshms_exchanged', csrfToken: 'dshc_management' } }
}
if (path === '/v1/management/pending/complete') {
if (body.authenticationContext !== 'dsh-authentication-context') {
throw Object.assign(new Error('authentication context is stale'), {
code: 'AUTHENTICATION_CONTEXT_STALE', statusCode: 409,
})
}
const source = pending.get(body.pendingToken)
if (source === undefined) throw Object.assign(new Error('management login is invalid or expired'), {
code: 'PENDING_LOGIN_INVALID', statusCode: 401,
})
if (body.password !== 'correct management password') throw Object.assign(new Error('username or password is incorrect'), {
code: 'AUTHENTICATION_FAILED', statusCode: 401,
})
pending.delete(body.pendingToken)
managementSessions.set('dshms_completed', body.origin)
managementSources.set('dshms_completed', source)
return { session: { token: 'dshms_completed', csrfToken: 'dshc_management_completed' } }
}
if (path === '/v1/management/transitions/probe') {
if (transitionNonces.get(body.transitionId) !== body.nonce) {
throw Object.assign(new Error('transition probe is invalid'), {
code: 'TRANSITION_PROBE_INVALID', statusCode: 401,
})
}
transitionNonces.delete(body.transitionId)
return { proof: 'proof-valid' }
}
if (path === '/v1/management/continuations/consume') {
if (!continuations.has(body.token)) {
throw Object.assign(new Error('continuation is invalid'), {
code: 'CONTINUATION_INVALID', statusCode: 401,
})
}
continuations.delete(body.token)
managementSessions.set('dshms_continued', body.origin)
return { session: { token: 'dshms_continued', csrfToken: 'dshc_continued' } }
}
throw new Error(`unexpected request ${method} ${path}`)
},
}
const authentication = createBrowserAuthentication({ access, safeReturnPath })
const server = createServer((incoming, response) => {
const url = new URL(incoming.url, 'http://gateway.internal')
void authentication.handle(incoming, response, url.pathname, url.searchParams).then(handled => {
if (!handled) { response.writeHead(404); response.end() }
})
})
return { access, authentication, calls, server, sessions, managementSessions, managementSources, state: () => state }
}
test('Management transition probe exposes a proof only to its exact source Origin and consumes the nonce', async () => {
const current = fixture('initialized')
const port = await listen(current.server)
try {
const sourceOrigin = 'https://dsh.example'
const path = '/_dsh_platform/transition/probe?transitionId=transition-valid&nonce=nonce-valid'
const first = await request(port, {
path,
headers: { host: `127.0.0.1:${port}`, origin: sourceOrigin, accept: 'application/json' },
})
assert.equal(first.status, 200)
assert.equal(first.headers['access-control-allow-origin'], sourceOrigin)
assert.equal(first.headers['access-control-allow-credentials'], undefined)
assert.equal(first.headers.vary, 'Origin')
assert.deepEqual(JSON.parse(first.body), { proof: 'proof-valid' })
const call = current.calls.find(value => value.path === '/v1/management/transitions/probe')
assert.equal(call.body.sourceOrigin, sourceOrigin)
assert.equal(call.body.candidateOrigin, `http://127.0.0.1:${port}`)
const replay = await request(port, {
path,
headers: { host: `127.0.0.1:${port}`, origin: sourceOrigin, accept: 'application/json' },
})
assert.equal(replay.status, 401)
assert.equal(replay.headers['access-control-allow-origin'], sourceOrigin)
} finally { await close(current.server) }
})
test('same-origin Management transition probes use the request origin when browsers omit Origin on GET', async () => {
const current = fixture('initialized')
const port = await listen(current.server)
try {
const response = await request(port, {
path: '/_dsh_platform/transition/probe?transitionId=transition-valid&nonce=nonce-valid',
headers: { host: `127.0.0.1:${port}`, accept: 'application/json' },
})
assert.equal(response.status, 200)
assert.deepEqual(JSON.parse(response.body), { proof: 'proof-valid' })
const call = current.calls.find(value => value.path === '/v1/management/transitions/probe')
assert.equal(call.body.sourceOrigin, `http://127.0.0.1:${port}`)
assert.equal(call.body.candidateOrigin, `http://127.0.0.1:${port}`)
} finally { await close(current.server) }
})
test('Management continuation requires a top-level navigation and creates one session once', async () => {
const current = fixture('initialized')
const port = await listen(current.server)
try {
const path = '/_dsh_platform/transition/continue?token=continuation-valid'
const forbidden = await request(port, {
path,
headers: { host: `127.0.0.1:${port}`, 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty' },
})
assert.equal(forbidden.status, 403)
const consumed = await request(port, {
path,
headers: { host: `127.0.0.1:${port}`, 'sec-fetch-mode': 'navigate', 'sec-fetch-dest': 'document' },
})
assert.equal(consumed.status, 200)
assert.match(consumed.body, /location\.replace\("\/_dsh_platform\/console\/"\)/)
assert.match(consumed.headers['set-cookie'][0], new RegExp(`^${MANAGEMENT_SESSION_COOKIE}=dshms_continued`))
assert.match(consumed.headers['set-cookie'][0], /Path=\/_dsh_platform\//)
assert.match(consumed.headers['set-cookie'][0], /SameSite=Lax/)
const replay = await request(port, {
path,
headers: { host: `127.0.0.1:${port}`, 'sec-fetch-mode': 'navigate', 'sec-fetch-dest': 'document' },
})
assert.equal(replay.status, 401)
} finally { await close(current.server) }
})
test('isolated Management continuation creates root-path cookies and enters from its own Origin', async () => {
const current = fixture('initialized')
const isolated = createBrowserAuthentication({
access: current.access,
safeReturnPath,
paths: { authPrefix: '/auth/', accessPrefix: '/access/', transitionPrefix: '/transition/', consolePath: '/' },
})
const server = createServer((incoming, response) => {
const url = new URL(incoming.url, 'http://gateway.internal')
void isolated.handle(incoming, response, url.pathname, url.searchParams).then(handled => {
if (!handled) { response.writeHead(404); response.end() }
})
})
const port = await listen(server)
try {
const consumed = await request(port, {
path: '/transition/continue?token=continuation-valid',
headers: { host: `127.0.0.1:${port}`, 'sec-fetch-mode': 'navigate', 'sec-fetch-dest': 'document' },
})
assert.equal(consumed.status, 200)
assert.match(consumed.body, /location\.replace\("\/"\)/)
assert.match(consumed.headers['set-cookie'][0], new RegExp(`^${MANAGEMENT_SESSION_COOKIE}=dshms_continued`))
assert.match(consumed.headers['set-cookie'][0], /Path=\//)
assert.match(consumed.headers['set-cookie'][0], /SameSite=Lax/)
assert.doesNotMatch(consumed.headers['set-cookie'][0], /Path=\/_dsh_platform\//)
} finally { await close(server) }
})
test('renders state-driven initialization and recovery pages without exposing account material', async () => {
for (const [state, expected] of [
['classification-pending', /Preparing authentication/],
['never-initialized', /autocomplete="new-password"/],
['initialized', /autocomplete="current-password"/],
['migration-required', /Administrator migration required/],
['recovery-required', /Administrator recovery required/],
]) {
const current = fixture(state)
const port = await listen(current.server)
try {
const response = await request(port, {
path: '/_dsh_platform/auth/?return=https://evil.example/',
headers: { host: `127.0.0.1:${port}`, 'accept-language': 'en' },
})
assert.equal(response.status, 200)
assert.match(response.body, expected)
assert.match(response.headers['cache-control'], /no-store/)
assert.match(response.headers['x-dsh-csrf'], /^dsha_/)
assert.match(response.headers['set-cookie'][1], new RegExp(`^${AUTH_RETRY_COOKIE}=dshr_`))
assert.match(response.headers['content-security-policy'], /frame-ancestors 'none'/)
assert.doesNotMatch(response.body, /https:\/\/evil\.example/)
if (state === 'classification-pending') {
assert.match(response.body, /setTimeout\(\(\)=>location\.reload\(\),1000\)/)
assert.match(response.body, /<section class="waiting"><h1>Preparing authentication<\/h1>/)
assert.match(response.body, /\.waiting\{text-align:center\}/)
assert.doesNotMatch(response.body, /<form/)
} else if (state !== 'recovery-required') {
assertInlineScriptsCompile(response.body)
assert.match(response.body, /<form method="post" action="\/_dsh_platform\/auth\/(?:session(?:\?return=[^"]+)?|reset)" novalidate>/)
assert.doesNotMatch(response.body, /elements\.username\.addEventListener\('input'/)
if (state === 'never-initialized' || state === 'migration-required') {
assert.match(response.body, /class="field-error" data-field-error="username"[^>]*hidden/)
assert.match(response.body, /class="field-error" data-field-error="password"[^>]*hidden/)
assert.match(response.body, /name="confirmPassword" type="password" autocomplete="new-password" required/)
assert.match(response.body, /class="field-error" data-field-error="confirmPassword"[^>]*hidden/)
assert.match(response.body, /The new password confirmation does not match\./)
assert.match(response.body, /\.field-error\{color:#ff7777;font-size:12px;font-weight:400;line-height:1\.5\}/)
assert.match(response.body, /form\.elements\.password\.addEventListener\('input'/)
assert.match(response.body, /form\.elements\.confirmPassword\.addEventListener\('input'/)
assert.match(response.body, /if\(!validatePasswordConfirmation\(true\)\)return/)
} else {
assert.doesNotMatch(response.body, /class="field-error"|invalidUsername|invalidPassword|validateField/)
assert.doesNotMatch(response.body, /minlength="8"|pattern="/)
assert.doesNotMatch(response.body, /name="confirmPassword"|passwordMismatch|validatePasswordConfirmation/)
}
if (state === 'never-initialized') {
assert.doesNotMatch(response.body, /name="setupKey"/)
assert.doesNotMatch(response.body, /<h1>/)
}
} else {
assert.match(response.body, /href="\/_dsh_platform\/auth\/reset\?return=/)
assert.doesNotMatch(response.body, /name="setupKey"/)
}
} finally { await close(current.server) }
}
})
test('a DSH session exchanges once for a separate Management session', async () => {
const current = fixture('initialized')
const port = await listen(current.server)
try {
const origin = `http://127.0.0.1:${port}`
current.sessions.set('dshs_existing', origin)
const response = { writeHead(status, headers) { this.status = status; this.headers = headers }, end() {} }
await current.authentication.enterManagement({
headers: { host: `127.0.0.1:${port}`, cookie: `${DSH_SESSION_COOKIE}=dshs_existing` },
}, response)
assert.equal(response.status, 303)
assert.match(response.headers.location, /management\/handoff\?token=dshh_created$/)
const exchanged = await request(port, {
path: response.headers.location,
headers: { host: `127.0.0.1:${port}`, 'sec-fetch-mode': 'navigate', 'sec-fetch-dest': 'document' },
})
assert.equal(exchanged.status, 200)
assert.match(exchanged.body, /location\.replace\("\/_dsh_platform\/console\/"\)/)
assert.match(exchanged.headers['set-cookie'][0], new RegExp(`^${MANAGEMENT_SESSION_COOKIE}=dshms_exchanged`))
assert.match(exchanged.headers['set-cookie'][0], /Max-Age=86400/)
assert.match(exchanged.headers['set-cookie'][1], /Max-Age=86400/)
const replay = await request(port, {
path: response.headers.location,
headers: { host: `127.0.0.1:${port}`, 'sec-fetch-mode': 'navigate', 'sec-fetch-dest': 'document' },
})
assert.equal(replay.status, 401)
} finally { await close(current.server) }
})
test('a Management handoff preserves the fixed Authentication settings destination', async () => {
const current = fixture('initialized')
const port = await listen(current.server)
try {
const origin = `http://127.0.0.1:${port}`
current.sessions.set('dshs_existing', origin)
const response = { writeHead(status, headers) { this.status = status; this.headers = headers }, end() {} }
await current.authentication.enterManagement({
headers: { host: `127.0.0.1:${port}`, cookie: `${DSH_SESSION_COOKIE}=dshs_existing` },
}, response, new URLSearchParams('tab=auth-settings'))
assert.equal(response.status, 303)
assert.match(response.headers.location, /management\/handoff\?token=dshh_created&tab=auth-settings$/)
const exchanged = await request(port, {
path: response.headers.location,
headers: { host: `127.0.0.1:${port}`, 'sec-fetch-mode': 'navigate', 'sec-fetch-dest': 'document' },
})
assert.equal(exchanged.status, 200)
assert.match(exchanged.body, /location\.replace\("\/_dsh_platform\/console\/#auth-settings"\)/)
} finally { await close(current.server) }
})
test('initialization requires same-origin JSON and a matching login CSRF token', async () => {
const current = fixture()
const port = await listen(current.server)
try {
const page = await request(port, {
path: '/_dsh_platform/auth/', headers: { host: `127.0.0.1:${port}` },
})
assertInlineScriptsCompile(page.body)
assert.match(page.body, /retryUntil=Date\.now\(\)\+seconds\*1000/)
assert.match(page.body, /Math\.ceil\(\(retryUntil-Date\.now\(\)\)\/1000\)/)
assert.match(page.body, /button\.disabled=true/)
assert.match(page.body, /error\.hidden=true;button\.disabled=false/)
assert.match(page.body, /if\(response\.ok\)\{navigating=true;location\.replace/)
assert.match(page.body, /finally\{if\(!navigating&&retryUntil<=Date\.now\(\)\)button\.disabled=false;if\(!navigating\)button\.textContent=/)
const csrfCookie = page.headers['set-cookie'][0].split(';')[0]
const retryCookie = page.headers['set-cookie'][1].split(';')[0]
const browserCookies = `${csrfCookie}; ${retryCookie}`
const csrf = csrfCookie.split('=')[1]
const rejected = await request(port, {
path: '/_dsh_platform/auth/session', method: 'POST',
headers: {
host: `127.0.0.1:${port}`, origin: 'https://evil.example',
cookie: browserCookies, 'content-type': 'application/json', 'x-dsh-csrf': csrf,
},
body: JSON.stringify({ username: 'admin', password: 'correct password' }),
})
assert.equal(rejected.status, 403)
const initialized = await request(port, {
path: '/_dsh_platform/auth/session', method: 'POST',
headers: {
host: `127.0.0.1:${port}`, origin: `http://127.0.0.1:${port}`,
cookie: browserCookies, 'content-type': 'application/json', 'x-dsh-csrf': csrf,
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'user-agent': 'Session Test Browser',
},
body: JSON.stringify({ username: 'admin', password: 'correct password' }),
})
assert.equal(initialized.status, 201)
assert.equal(current.state(), 'initialized')
assert.match(initialized.headers['set-cookie'][0], new RegExp(`^${DSH_SESSION_COOKIE}=dshs_created`))
assert.match(initialized.headers['set-cookie'][0], /Max-Age=2592000/)
assert.match(initialized.headers['set-cookie'][1], /Max-Age=2592000/)
const initialization = current.calls.find(call => call.path === '/v1/dsh/initialize')
assert.equal(initialization.body.client.userAgent, 'Session Test Browser')
assert.equal(initialization.body.client.ip, '127.0.0.1')
assert.match(initialization.body.authenticationSource, /^browser:[A-Za-z0-9_-]+$/)
} finally { await close(current.server) }
})
test('parallel login pages share a stable context and stale submissions never become credential failures', async () => {
const current = fixture('initialized')
const port = await listen(current.server)
try {
const host = `127.0.0.1:${port}`
const origin = `http://${host}`
const first = await request(port, { path: '/_dsh_platform/auth/', headers: { host } })
const firstCsrfCookie = first.headers['set-cookie'][0].split(';')[0]
const firstRetryCookie = first.headers['set-cookie'][1].split(';')[0]
const firstCsrf = firstCsrfCookie.split('=')[1]
const second = await request(port, {
path: '/_dsh_platform/auth/',
headers: { host, cookie: `${firstCsrfCookie}; ${firstRetryCookie}` },
})
const secondCsrfCookie = second.headers['set-cookie'][0].split(';')[0]
assert.equal(secondCsrfCookie, firstCsrfCookie)
const stale = await request(port, {
path: '/_dsh_platform/auth/session', method: 'POST',
headers: {
host, origin, cookie: `${AUTH_CSRF_COOKIE}=dsha_newer; ${firstRetryCookie}`,
'content-type': 'application/json', 'x-dsh-csrf': firstCsrf,
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
},
body: JSON.stringify({ username: 'admin', password: 'correct password' }),
})
assert.equal(stale.status, 409)
assert.equal(JSON.parse(stale.body).code, 'AUTHENTICATION_CONTEXT_STALE')
assert.equal(current.calls.some(call => call.path === '/v1/dsh/login'), false)
assert.match(first.body, /AUTHENTICATION_CONTEXT_STALE/)
assert.match(first.body, /auth\/context/)
const refreshed = await request(port, {
path: '/_dsh_platform/auth/context',
headers: {
host, cookie: `${AUTH_CSRF_COOKIE}=dsha_newer; ${firstRetryCookie}`,
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
},
})
assert.equal(refreshed.status, 200)
assert.equal(JSON.parse(refreshed.body).csrfToken, 'dsha_newer')
const recovered = await request(port, {
path: '/_dsh_platform/auth/session', method: 'POST',
headers: {
host, origin, cookie: `${AUTH_CSRF_COOKIE}=dsha_newer; ${firstRetryCookie}`,
'content-type': 'application/json', 'x-dsh-csrf': 'dsha_newer',
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
},
body: JSON.stringify({ username: 'admin', password: 'correct password' }),
})
assert.equal(recovered.status, 200)
assert.equal(current.calls.filter(call => call.path === '/v1/dsh/login').length, 1)
} finally { await close(current.server) }
})
test('TOTP retry countdown refreshes the current browser source from Access Manager', async () => {
const current = fixture('initialized', {
totpEnabled: true,
totpRetry: { kind: 'retry', retryAfterSeconds: 6 },
})
const port = await listen(current.server)
try {
const host = `127.0.0.1:${port}`
const page = await request(port, { path: '/_dsh_platform/auth/', headers: { host } })
const cookies = page.headers['set-cookie'].map(value => value.split(';')[0]).join('; ')
assert.match(page.body, /refreshTotpRetry/)
const response = await request(port, {
path: '/_dsh_platform/auth/context',
headers: { host, cookie: cookies, 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors' },
})
assert.equal(response.status, 200)
assert.deepEqual(JSON.parse(response.body).totpRetry, { kind: 'retry', retryAfterSeconds: 6 })
const statusCall = current.calls.find(call => call.path === '/v1/authentication-retry/status')
assert.equal(statusCall.method, 'POST')
assert.equal(statusCall.body.credential, 'totp')
assert.match(statusCall.body.source, /^browser:[A-Za-z0-9_-]+$/)
} finally { await close(current.server) }
})
test('successful DSH and Management logins preserve parallel login-page context', async () => {
const current = fixture('initialized', { managementAdditionalEnabled: false })
const port = await listen(current.server)
try {
const host = `127.0.0.1:${port}`
const origin = `http://${host}`
const page = await request(port, { path: '/_dsh_platform/auth/', headers: { host } })
const csrfCookie = page.headers['set-cookie'][0].split(';')[0]
const retryCookie = page.headers['set-cookie'][1].split(';')[0]
const csrf = csrfCookie.split('=')[1]
const login = (path = '/_dsh_platform/auth/session') => request(port, {
path, method: 'POST',
headers: {
host, origin, cookie: `${csrfCookie}; ${retryCookie}`,
'content-type': 'application/json', 'x-dsh-csrf': csrf,
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
},
body: JSON.stringify({ username: 'admin', password: 'correct password' }),
})
const first = await login()
assert.equal(first.status, 200)
assert.equal(first.headers['set-cookie'].some(cookie => cookie.startsWith(`${AUTH_CSRF_COOKIE}=;`)), false)
const second = await login('/_dsh_platform/auth/session?return=%2F_dsh_platform%2Fauth%2Fmanagement%2Fstart')
assert.equal(second.status, 200)
const dshCookie = second.headers['set-cookie'][0].split(';')[0]
const next = JSON.parse(second.body).next
const handoff = await request(port, {
path: next,
headers: { host, cookie: `${dshCookie}; ${csrfCookie}; ${retryCookie}` },
})
assert.equal(handoff.status, 200)
assert.equal(handoff.headers['set-cookie'].some(cookie => cookie.startsWith(`${AUTH_CSRF_COOKIE}=;`)), false)
const afterManagement = await login()
assert.equal(afterManagement.status, 200)
const incorrect = await request(port, {
path: '/_dsh_platform/auth/session', method: 'POST',
headers: {
host, origin, cookie: `${csrfCookie}; ${retryCookie}`,
'content-type': 'application/json', 'x-dsh-csrf': csrf,
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
},
body: JSON.stringify({ username: 'admin', password: 'wrong password' }),
})
assert.equal(incorrect.status, 401)
assert.equal(JSON.parse(incorrect.body).code, 'AUTHENTICATION_FAILED')
assert.equal(current.calls.filter(call => call.path === '/v1/dsh/login').length, 4)
} finally { await close(current.server) }
})
test('TOTP completes on the existing login page and replaces its one-time challenge with a DSH session', async () => {
const current = fixture('initialized', { totpEnabled: true })
const port = await listen(current.server)
try {
const host = `127.0.0.1:${port}`
const origin = `http://${host}`
const page = await request(port, {
path: '/_dsh_platform/auth/', headers: { host, 'accept-language': 'zh-CN' },
})
assert.equal(page.status, 200)
assert.match(page.body, /动态验证码/)
assert.doesNotMatch(page.body, /name="totpCode"[^>]+pattern=/)
assert.match(page.body, /if\(error\.textContent===initialMessage\)sync\(\)/)
const csrfCookie = page.headers['set-cookie'][0].split(';')[0]
const retryCookie = page.headers['set-cookie'][1].split(';')[0]
const csrf = csrfCookie.split('=')[1]
const headers = {
host, origin, cookie: `${csrfCookie}; ${retryCookie}`,
'content-type': 'application/json', 'x-dsh-csrf': csrf,
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
}
const password = await request(port, {
path: '/_dsh_platform/auth/session', method: 'POST', headers,
body: JSON.stringify({ username: 'admin', password: 'correct password' }),
})
assert.equal(password.status, 202)
assert.deepEqual(JSON.parse(password.body), { authenticated: false, totpRequired: true })
const challengeCookie = password.headers['set-cookie'].find(value => value.startsWith(`${TOTP_LOGIN_COOKIE}=`)).split(';')[0]
const completed = await request(port, {
path: '/_dsh_platform/auth/session', method: 'POST',
headers: { ...headers, cookie: `${csrfCookie}; ${retryCookie}; ${challengeCookie}` },
body: JSON.stringify({ totpCode: '123456' }),
})
assert.equal(completed.status, 200)
assert.equal(JSON.parse(completed.body).authenticated, true)
assert.equal(completed.headers['set-cookie'].some(value => value.startsWith(`${DSH_SESSION_COOKIE}=dshs_totp`)), true)
assert.equal(completed.headers['set-cookie'].some(value => value.startsWith(`${TOTP_LOGIN_COOKIE}=;`)), true)
const completion = current.calls.find(value => value.path === '/v1/dsh/totp/complete')
assert.equal(completion.body.challengeToken, 'dshtl_login')
assert.equal(completion.body.code, '123456')
} finally { await close(current.server) }
})
test('login reports an unavailable Access Manager instead of a credential failure', async () => {
const access = {
request: async (_method, path) => {
if (path === '/v1/status') return { state: 'initialized' }
throw new Error('access socket unavailable')
},
}
const authentication = createBrowserAuthentication({ access, safeReturnPath })
const server = createServer((incoming, response) => {
const url = new URL(incoming.url, 'http://gateway.internal')
void authentication.handle(incoming, response, url.pathname, url.searchParams)
})
const port = await listen(server)
try {
const origin = `http://127.0.0.1:${port}`
const page = await request(port, {
path: '/_dsh_platform/auth/', headers: { host: `127.0.0.1:${port}` },
})
const csrfCookie = page.headers['set-cookie'][0].split(';')[0]
const csrf = csrfCookie.split('=')[1]
const response = await request(port, {
path: '/_dsh_platform/auth/session', method: 'POST',
headers: {
host: `127.0.0.1:${port}`, origin, cookie: csrfCookie,
'content-type': 'application/json', 'x-dsh-csrf': csrf,
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
},
body: JSON.stringify({ username: 'admin', password: 'correct password' }),
})
assert.equal(response.status, 503)
assert.equal(JSON.parse(response.body).code, 'ACCESS_MANAGER_UNAVAILABLE')
assert.match(page.body, /authentication service is temporarily unavailable/i)
} finally { await close(server) }
})
test('login waits for a restarting Access Manager before submitting credentials once', async () => {
let restarting = false
let statusAttempts = 0
let loginAttempts = 0
const access = {
request: async (_method, path) => {
if (path === '/v1/status') {
statusAttempts += 1
if (restarting && statusAttempts < 4) throw new Error('access socket unavailable')
return { state: 'initialized', account: { managementAccess: { mode: 'compat' } } }
}
if (path === '/v1/dsh/login') {
loginAttempts += 1
return { session: { token: 'dshs_restarted', csrfToken: 'dshc_restarted' } }
}
if (path === '/v1/sessions/validate') return { authenticated: false }
throw new Error(`unexpected Access request: ${path}`)
},
}
const authentication = createBrowserAuthentication({ access, safeReturnPath, wait: async () => {} })
const server = createServer((incoming, response) => {
const url = new URL(incoming.url, 'http://gateway.internal')
void authentication.handle(incoming, response, url.pathname, url.searchParams)
})
const port = await listen(server)
try {
const host = `127.0.0.1:${port}`
const origin = `http://${host}`
const page = await request(port, { path: '/_dsh_platform/auth/', headers: { host } })
const csrfCookie = page.headers['set-cookie'][0].split(';')[0]
const retryCookie = page.headers['set-cookie'][1].split(';')[0]
const csrf = csrfCookie.split('=')[1]
statusAttempts = 0
restarting = true
const response = await request(port, {
path: '/_dsh_platform/auth/session', method: 'POST',
headers: {
host, origin, cookie: `${csrfCookie}; ${retryCookie}`,
'content-type': 'application/json', 'x-dsh-csrf': csrf,
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
},
body: JSON.stringify({ username: 'admin', password: 'correct password' }),
})
assert.equal(response.status, 200)
assert.equal(statusAttempts, 4)
assert.equal(loginAttempts, 1)
} finally { await close(server) }
})
test('login preserves explicit Access Manager rate limits', async () => {
const access = {
request: async (_method, path) => {
if (path === '/v1/status') return { state: 'initialized' }
throw Object.assign(new Error('authentication is temporarily unavailable'), {
statusCode: 429,
code: 'AUTHENTICATION_RETRY_REQUIRED',
retryAfterSeconds: 7,
})
},
}
const authentication = createBrowserAuthentication({ access, safeReturnPath })
const server = createServer((incoming, response) => {
const url = new URL(incoming.url, 'http://gateway.internal')
void authentication.handle(incoming, response, url.pathname, url.searchParams)
})
const port = await listen(server)
try {
const origin = `http://127.0.0.1:${port}`
const page = await request(port, {
path: '/_dsh_platform/auth/', headers: { host: `127.0.0.1:${port}` },
})
const csrfCookie = page.headers['set-cookie'][0].split(';')[0]
const csrf = csrfCookie.split('=')[1]
const response = await request(port, {
path: '/_dsh_platform/auth/session', method: 'POST',
headers: {
host: `127.0.0.1:${port}`, origin, cookie: csrfCookie,
'content-type': 'application/json', 'x-dsh-csrf': csrf,
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
},
body: JSON.stringify({ username: 'admin', password: 'wrong password' }),
})
assert.equal(response.status, 429)
assert.equal(response.headers['retry-after'], '7')
assert.deepEqual(JSON.parse(response.body), {
error: 'authentication is temporarily unavailable',
code: 'AUTHENTICATION_RETRY_REQUIRED',
retryAfterSeconds: 7,
})
} finally { await close(server) }
})
test('legacy migration exchanges a root-issued authentication reset key for a DSH session', async () => {
const current = fixture('migration-required')
const port = await listen(current.server)
try {
const origin = `http://127.0.0.1:${port}`
const page = await request(port, {
path: '/_dsh_platform/auth/', headers: { host: `127.0.0.1:${port}`, 'accept-language': 'en' },
})
assert.match(page.body, /name="setupKey"/)
assert.match(page.body, /method="post"/)
assert.match(page.body, /Use 8 to 1024 characters\. Unicode letters, numbers, spaces, and symbols are supported; control and bidirectional-control characters are not\./)
assert.match(page.body, /The authentication reset key is invalid, expired, or already used\./)
assert.match(page.body, /name="username"[^>]+pattern="\[\^\\p\{Cc\}/)
assert.match(page.body, /name="password"[^>]+minlength="8"[^>]+pattern="\[\^\\p\{Cc\}/)
assert.match(page.body, /name="confirmPassword" type="password" autocomplete="new-password" required/)
assert.match(page.body, /html\{height:100%;color-scheme:light dark}/)
assert.match(page.body, /body\{min-height:100%;margin:0;display:grid;place-items:center;/)
assert.doesNotMatch(page.body, /100dvh/)
assert.match(page.body, /input:focus-visible\{border-color:#f3f3f4;outline:none\}/)
assert.doesNotMatch(page.body, /name="password"[^>]*value=/)
assertInlineScriptsCompile(page.body)
const csrfCookie = page.headers['set-cookie'][0].split(';')[0]
const csrf = csrfCookie.split('=')[1]
const migrated = await request(port, {
path: '/_dsh_platform/auth/reset', method: 'POST',
headers: {
host: `127.0.0.1:${port}`, origin, cookie: csrfCookie,
'content-type': 'application/json', 'x-dsh-csrf': csrf,
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
},
body: JSON.stringify({ setupKey: 'dshak_valid', username: 'admin', password: 'correct password' }),
})
assert.equal(migrated.status, 201)
assert.match(migrated.headers['set-cookie'][0], new RegExp(`^${DSH_SESSION_COOKIE}=dshs_reset`))
assert.equal(current.calls.some(call => call.path === '/v1/dsh/reset-authentication'), true)
} finally { await close(current.server) }
})
test('migration and initialization pages distinguish registration errors from login failures', async () => {
for (const [state, language, expected] of [
['never-initialized', 'zh-CN', ['密码支持 8 至 1024 个字符,可使用中文、字母、数字、空格和符号;不能包含控制字符或双向控制字符。', '无法创建管理员账户,请检查填写内容后重试。']],
['migration-required', 'zh-CN', ['认证重置密钥无效、已过期或已使用,请重新生成。', '密码支持 8 至 1024 个字符,可使用中文、字母、数字、空格和符号;不能包含控制字符或双向控制字符。']],
['never-initialized', 'en', ['Use 8 to 1024 characters. Unicode letters, numbers, spaces, and symbols are supported; control and bidirectional-control characters are not.', 'The administrator account could not be created.']],
]) {
const current = fixture(state)
const port = await listen(current.server)
try {
const page = await request(port, {
path: '/_dsh_platform/auth/',
headers: { host: `127.0.0.1:${port}`, 'accept-language': language },
})
for (const message of expected) assert.match(page.body, new RegExp(message.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')))
assertInlineScriptsCompile(page.body)
} finally { await close(current.server) }
}
})
test('a parallel TOTP page reuses the DSH session completed by another tab', async () => {
const current = fixture('initialized', { totpEnabled: true })
const port = await listen(current.server)
try {
const host = `127.0.0.1:${port}`
const origin = `http://${host}`
current.sessions.set('dshs_existing', origin)
const response = await request(port, {
path: '/_dsh_platform/auth/session?return=%2F_dsh_platform%2Fauth%2Fmanagement%2Fstart',
method: 'POST',
headers: {
host, origin,
cookie: `${DSH_SESSION_COOKIE}=dshs_existing; ${TOTP_LOGIN_COOKIE}=dshtl_consumed`,
'content-type': 'application/json',
'x-dsh-csrf': 'dsha_stale',
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
},
body: JSON.stringify({ totpCode: '123456' }),
})
assert.equal(response.status, 200)
assert.deepEqual(JSON.parse(response.body), {
authenticated: true,
next: '/_dsh_platform/auth/management/start',
})
assert.equal(response.headers['set-cookie'].some(value => value.startsWith(`${TOTP_LOGIN_COOKIE}=;`)), true)
assert.equal(current.calls.some(call => call.path === '/v1/dsh/totp/complete'), false)
const forbidden = await request(port, {
path: '/_dsh_platform/auth/session', method: 'POST',
headers: {
host, origin: 'https://attacker.example',
cookie: `${DSH_SESSION_COOKIE}=dshs_existing`,
'content-type': 'application/json',
'sec-fetch-site': 'cross-site', 'sec-fetch-mode': 'cors',
},
body: JSON.stringify({ totpCode: '123456' }),
})
assert.equal(forbidden.status, 403)
} finally { await close(current.server) }
})
test('damaged authentication requires an explicit reset page and never places credentials in its URL', async () => {
const current = fixture('recovery-required')
const port = await listen(current.server)
try {
const origin = `http://127.0.0.1:${port}`
const recovery = await request(port, {
path: '/_dsh_platform/auth/', headers: { host: `127.0.0.1:${port}`, 'accept-language': 'en' },
})
assert.match(recovery.body, /Reset administrator authentication/)
assert.match(recovery.body, /\.button:focus-visible\{outline:2px solid/)
assert.doesNotMatch(recovery.body, /name="(?:setupKey|username|password)"/)
const page = await request(port, {
path: '/_dsh_platform/auth/reset?return=%2Fsettings',
headers: { host: `127.0.0.1:${port}`, 'accept-language': 'en' },
})
assert.equal(page.status, 200)
assert.match(page.body, /<form method="post" action="\/_dsh_platform\/auth\/reset" novalidate>/)
assert.match(page.body, /name="confirmPassword" type="password" autocomplete="new-password" required/)
assert.doesNotMatch(page.body, /dshak_valid|username=|password=/)
const csrfCookie = page.headers['set-cookie'][0].split(';')[0]
const csrf = csrfCookie.split('=')[1]
const reset = await request(port, {
path: '/_dsh_platform/auth/reset', method: 'POST',
headers: {
host: `127.0.0.1:${port}`, origin, cookie: csrfCookie,
'content-type': 'application/json', 'x-dsh-csrf': csrf,
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
},
body: JSON.stringify({ setupKey: 'dshak_valid', username: 'recovered', password: 'correct password' }),
})
assert.equal(reset.status, 201)
assert.equal(current.calls.some(call => call.path === '/v1/dsh/reset-authentication'), true)
} finally { await close(current.server) }
})
test('login sessions are origin-bound and safe return paths remain local', async () => {
const current = fixture('initialized')
const port = await listen(current.server)
try {
const page = await request(port, {
path: '/_dsh_platform/auth/?return=%2Fsettings%3Fsection%3Dplugins',
headers: { host: `127.0.0.1:${port}` },
})
assert.doesNotMatch(page.body, /Administrator sign in|Enter the local administrator account/)
assert.doesNotMatch(page.body, /<h1>/)
const csrfCookie = page.headers['set-cookie'][0].split(';')[0]
const csrf = csrfCookie.split('=')[1]
const login = await request(port, {
path: '/_dsh_platform/auth/session', method: 'POST',
headers: {
host: `127.0.0.1:${port}`, origin: `http://127.0.0.1:${port}`,
cookie: csrfCookie, 'content-type': 'application/json', 'x-dsh-csrf': csrf,
},
body: JSON.stringify({ username: 'admin', password: 'correct password' }),
})
assert.equal(login.status, 200)
const sessionCookie = login.headers['set-cookie'][0].split(';')[0]
assert.equal((await current.authentication.validateDsh({ headers: {
host: `127.0.0.1:${port}`, cookie: sessionCookie,
} })).authenticated, true)
assert.equal((await current.authentication.validateDsh({ headers: {
host: 'different.example', cookie: sessionCookie,
} })).authenticated, false)
assert.equal(safeReturnPath('//evil.example'), '/')
assert.equal(safeReturnPath('/settings?section=plugins'), '/settings?section=plugins')
} finally { await close(current.server) }
})
test('direct Management access requires a DSH login before exchanging a Management session', async () => {
const current = fixture('initialized', { managementAdditionalEnabled: false })
const port = await listen(current.server)
try {
const origin = `http://127.0.0.1:${port}`
const staleCookies = `${DSH_SESSION_COOKIE}=dshs_previous_instance; ${MANAGEMENT_SESSION_COOKIE}=dshms_previous_instance`
const entry = await request(port, {
path: '/_dsh_platform/auth/management', headers: {
host: `127.0.0.1:${port}`, cookie: staleCookies,
},
})
assert.equal(entry.status, 303)
const loginEntry = new URL(entry.headers.location)
assert.equal(loginEntry.origin, origin)
assert.equal(loginEntry.pathname, '/_dsh_platform/auth/')
assert.equal(loginEntry.searchParams.get('return'), '/_dsh_platform/auth/management/start')
const page = await request(port, {
path: `${loginEntry.pathname}${loginEntry.search}`, headers: {
host: `127.0.0.1:${port}`, cookie: staleCookies,
},
})
assert.doesNotMatch(page.body, /class="field-error"|invalidUsername|invalidPassword|validateField/)
assert.doesNotMatch(page.body, /name="confirmPassword"|validatePasswordConfirmation/)
assert.doesNotMatch(page.body, /minlength="8"|pattern="/)
const loginCsrfCookie = page.headers['set-cookie'][0].split(';')[0]
const loginCsrf = loginCsrfCookie.split('=')[1]
const dshLogin = await request(port, {
path: '/_dsh_platform/auth/session?return=%2F_dsh_platform%2Fauth%2Fmanagement%2Fstart', method: 'POST',
headers: {
host: `127.0.0.1:${port}`, origin, cookie: loginCsrfCookie,
'content-type': 'application/json', 'x-dsh-csrf': loginCsrf,
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
},
body: JSON.stringify({ username: 'admin', password: 'correct password' }),
})
assert.equal(dshLogin.status, 200)
const loginResult = JSON.parse(dshLogin.body)
assert.equal(loginResult.authenticated, true)
assert.match(loginResult.next, /^\/_dsh_platform\/auth\/management\/handoff\?token=dshh_created$/)
const dshCookie = dshLogin.headers['set-cookie'][0].split(';')[0]
const exchanged = await request(port, {
path: loginResult.next,
headers: { host: `127.0.0.1:${port}`, cookie: dshCookie },
})
assert.equal(exchanged.status, 200)
assert.match(exchanged.body, /location\.replace\("\/_dsh_platform\/console\/"\)/)
assert.match(exchanged.headers['set-cookie'][0], new RegExp(`^${MANAGEMENT_SESSION_COOKIE}=dshms_exchanged`))
assert.equal(current.calls.some(call => call.path === '/v1/management/login'), false)
assert.equal((await request(port, {
path: '/_dsh_platform/auth/management/session', method: 'POST',
headers: { host: `127.0.0.1:${port}`, origin, 'content-type': 'application/json' },
body: JSON.stringify({ username: 'admin', password: 'correct password' }),
})).status, 404)
} finally { await close(current.server) }
})
test('isolated Management sends the first login layer to the verified DSH Origin', async () => {
const current = fixture('initialized', {
managementAccess: {
mode: 'isolated',
dshPublicOrigin: 'https://dsh.example',
isolatedEntry: { kind: 'public', managementPublicOrigin: 'https://manage.example' },
},
})
const isolated = createBrowserAuthentication({
access: current.access,
safeReturnPath,
paths: { authPrefix: '/auth/', accessPrefix: '/access/', transitionPrefix: '/transition/', consolePath: '/' },
})
const response = { writeHead(status, headers) { this.status = status; this.headers = headers }, end() {} }
await isolated.enterManagement({ headers: { host: 'manage.example' } }, response)
assert.equal(response.status, 303)
const entry = new URL(response.headers.location)
assert.equal(entry.origin, 'https://dsh.example')
assert.equal(entry.pathname, '/_dsh_platform/auth/')
assert.equal(entry.searchParams.get('return'), '/_dsh_platform/auth/management/start')
})
test('Management entry reuses an existing Management session instead of re-handing off', async () => {
const current = fixture('initialized', {
managementAccess: {
mode: 'isolated',
dshPublicOrigin: 'https://dsh.example',
isolatedEntry: { kind: 'public', managementPublicOrigin: 'https://manage.example' },
},
})
const isolated = createBrowserAuthentication({
access: current.access,
safeReturnPath,
paths: { authPrefix: '/auth/', accessPrefix: '/access/', transitionPrefix: '/transition/', consolePath: '/' },
})
const server = createServer((incoming, response) => {
const url = new URL(incoming.url, 'http://gateway.internal')
void isolated.handle(incoming, response, url.pathname, url.searchParams).then(handled => {
if (!handled) { response.writeHead(404); response.end() }
})
})
const port = await listen(server)
try {
const origin = `http://127.0.0.1:${port}`
current.sessions.set('dshs_existing', origin)
current.managementSessions.set('dshms_existing', origin)
current.managementSources.set('dshms_existing', 'dshs_existing')
const response = await request(port, {
path: '/auth/management',
headers: { host: `127.0.0.1:${port}`, cookie: `${MANAGEMENT_SESSION_COOKIE}=dshms_existing` },
})
assert.equal(response.status, 200)
assert.match(response.body, /location\.replace\("\/"\)/)
assert.equal(current.calls.some(call => call.path === '/v1/management/handoffs'), false)
} finally { await close(server) }
})
test('Management return-to-DSH navigation uses the current compatibility origin', async () => {
const current = fixture('initialized')
const port = await listen(current.server)
try {
const host = `127.0.0.1:${port}`
const origin = `http://${host}`
current.sessions.set('dshs_existing', origin)
current.managementSessions.set('dshms_existing', origin)
current.managementSources.set('dshms_existing', 'dshs_existing')
const response = await request(port, {
path: '/_dsh_platform/auth/return-dsh',
headers: {
host,
cookie: `${MANAGEMENT_SESSION_COOKIE}=dshms_existing`,
'sec-fetch-mode': 'navigate',
'sec-fetch-dest': 'document',
},
})
assert.equal(response.status, 303)
assert.equal(response.headers.location, `${origin}/`)
} finally { await close(current.server) }
})
test('isolated Management return-to-DSH navigation uses the stored DSH Origin', async () => {
const current = fixture('initialized', {
managementAccess: {
mode: 'isolated',
dshPublicOrigin: 'https://dsh.example',
isolatedEntry: { kind: 'public', managementPublicOrigin: 'https://manage.example' },
},
})
const isolated = createBrowserAuthentication({
access: current.access,
safeReturnPath,
paths: { authPrefix: '/auth/', accessPrefix: '/access/', transitionPrefix: '/transition/', consolePath: '/' },
})
const server = createServer((incoming, response) => {
const url = new URL(incoming.url, 'http://gateway.internal')
void isolated.handle(incoming, response, url.pathname, url.searchParams).then(handled => {
if (!handled) { response.writeHead(404); response.end() }
})
})
const port = await listen(server)
try {
current.sessions.set('dshs_existing', 'http://manage.example')
current.managementSessions.set('dshms_existing', 'http://manage.example')
current.managementSources.set('dshms_existing', 'dshs_existing')
const response = await request(port, {
path: '/auth/return-dsh',
headers: {
host: 'manage.example',
cookie: `${MANAGEMENT_SESSION_COOKIE}=dshms_existing`,
'sec-fetch-mode': 'navigate',
'sec-fetch-dest': 'document',
},
})
assert.equal(response.status, 303)
assert.equal(response.headers.location, 'https://dsh.example/')
} finally { await close(server) }
})
test('Management return-to-DSH navigation refuses missing targets and unauthenticated requests', async () => {
const current = fixture('initialized', {
managementAccess: {
mode: 'isolated',
dshPublicOrigin: null,
isolatedEntry: { kind: 'public', managementPublicOrigin: 'https://manage.example' },
},
})
const isolated = createBrowserAuthentication({
access: current.access,
safeReturnPath,
paths: { authPrefix: '/auth/', accessPrefix: '/access/', transitionPrefix: '/transition/', consolePath: '/' },
})
const server = createServer((incoming, response) => {
const url = new URL(incoming.url, 'http://gateway.internal')
void isolated.handle(incoming, response, url.pathname, url.searchParams).then(handled => {
if (!handled) { response.writeHead(404); response.end() }
})
})
const port = await listen(server)
try {
const host = `127.0.0.1:${port}`
const origin = `http://${host}`
current.sessions.set('dshs_existing', origin)
current.managementSessions.set('dshms_existing', origin)
current.managementSources.set('dshms_existing', 'dshs_existing')
const missing = await request(port, {
path: '/auth/return-dsh',
headers: { host, cookie: `${MANAGEMENT_SESSION_COOKIE}=dshms_existing` },
})
assert.equal(missing.status, 409)
assert.match(missing.body, /DSH_RETURN_UNAVAILABLE/)
const unauthenticated = await request(port, {
path: '/auth/return-dsh',
headers: { host },
})
assert.equal(unauthenticated.status, 401)
assert.match(unauthenticated.body, /AUTHENTICATION_REQUIRED/)
} finally { await close(server) }
})
test('DSH browser logout exposes only additional-password state and revokes the requested sessions', async () => {
const current = fixture('initialized')
const port = await listen(current.server)
try {
const host = `127.0.0.1:${port}`
const origin = `http://${host}`
current.sessions.set('dshs_existing', origin)
current.managementSessions.set('dshms_existing', origin)
const dshCookies = `${DSH_SESSION_COOKIE}=dshs_existing; dsh_gateway_csrf=dshc_existing`
const managementCookie = `${MANAGEMENT_SESSION_COOKIE}=dshms_existing`
const context = await request(port, {
path: '/_dsh_platform/auth/session-context', headers: { host, cookie: dshCookies },
})
assert.equal(context.status, 200)
assert.deepEqual(JSON.parse(context.body), {
managementAdditionalPasswordEnabled: true,
managementConsoleHref: '/_dsh_platform/console/',
})
assert.doesNotMatch(context.body, /account|username|verifier|password.*version/i)
const managementOnly = await request(port, {
path: '/_dsh_platform/auth/browser-logout', method: 'POST',
headers: {
host, origin, cookie: `${dshCookies}; ${managementCookie}`,
'content-type': 'application/json', 'x-dsh-csrf': 'dshc_existing',
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
},
body: JSON.stringify({ scope: 'management' }),
})
assert.equal(managementOnly.status, 200)
assert.equal(current.sessions.has('dshs_existing'), true)
assert.equal(current.managementSessions.has('dshms_existing'), false)
assert.equal(managementOnly.headers['set-cookie'].some(value => value.startsWith(`${DSH_SESSION_COOKIE}=`)), false)
assert.equal(managementOnly.headers['set-cookie'].some(value => value.startsWith(`${MANAGEMENT_SESSION_COOKIE}=`)), true)
const all = await request(port, {
path: '/_dsh_platform/auth/browser-logout', method: 'POST',
headers: {
host, origin, cookie: dshCookies,
'content-type': 'application/json', 'x-dsh-csrf': 'dshc_existing',
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
},
body: JSON.stringify({ scope: 'all' }),
})
assert.equal(all.status, 200)
assert.equal(current.sessions.has('dshs_existing'), false)
assert.equal(all.headers['set-cookie'].some(value => value.startsWith(`${DSH_SESSION_COOKIE}=`)), true)
assert.equal(all.headers['set-cookie'].some(value => value.startsWith(`${MANAGEMENT_SESSION_COOKIE}=`)), true)
assert.equal(all.headers['set-cookie'].some(value => value.startsWith(`${AUTH_CSRF_COOKIE}=`)), true)
const logoutCall = current.calls.find(value => value.path === '/v1/dsh/browser-logout' && value.body.scope === 'all')
assert.equal(logoutCall.body.dshOrigin, origin)
assert.equal(logoutCall.body.dshToken, 'dshs_existing')
} finally { await close(current.server) }
})
test('DSH session context publishes the configured separate Management Console URL', async () => {
for (const [managementAccess, expected] of [
[{ mode: 'isolated', isolatedEntry: { kind: 'public', managementPublicOrigin: 'https://manage.example' } }, 'https://manage.example/'],
[{ mode: 'isolated', isolatedEntry: { kind: 'local-only', managementLocalOrigin: 'http://127.0.0.1:43081' } }, 'http://127.0.0.1:43081/'],
]) {
const current = fixture('initialized', { managementAccess })
current.sessions.set('dshs_existing', 'http://dsh.example')
const port = await listen(current.server)
try {
const response = await request(port, {
path: '/_dsh_platform/auth/session-context',
headers: { host: 'dsh.example', cookie: `${DSH_SESSION_COOKIE}=dshs_existing` },
})
assert.equal(response.status, 200)
assert.equal(JSON.parse(response.body).managementConsoleHref, expected)
} finally { await close(current.server) }
}
})
test('Management session context rejects a browser after its linked session is revoked', async () => {
const current = fixture('initialized')
current.sessions.set('dshs_existing', 'http://dsh.example')
current.managementSessions.set('dshms_existing', 'http://dsh.example')
current.managementSources.set('dshms_existing', 'dshs_existing')
const port = await listen(current.server)
try {
const headers = { host: 'dsh.example', cookie: `${MANAGEMENT_SESSION_COOKIE}=dshms_existing` }
assert.equal((await request(port, { path: '/_dsh_platform/auth/management/session-context', headers })).status, 200)
current.managementSessions.delete('dshms_existing')
assert.equal((await request(port, { path: '/_dsh_platform/auth/management/session-context', headers })).status, 401)
} finally { await close(current.server) }
})
test('DSH browser sessions authorize only path-bound Plugin API capabilities', async () => {
const current = fixture('initialized')
current.sessions.set('dshs_existing', 'http://dsh.example')
const authorization = await current.authentication.authorizePlugin({
headers: {
host: 'dsh.example',
origin: 'http://dsh.example',
cookie: `${DSH_SESSION_COOKIE}=dshs_existing; dsh_gateway_csrf=dshc_existing`,
'x-dsh-csrf': 'dshc_existing',
'sec-fetch-site': 'same-origin',
'sec-fetch-mode': 'cors',
},
}, {
method: 'POST', target: '/_dsh_platform/api/v1/restart-dsh', requireCsrf: true,
})
assert.equal(authorization.authorized, true)
assert.equal(authorization.capability.token, 'dshcap_plugin')
const call = current.calls.find(value => value.path === '/v1/dsh/capabilities')
assert.deepEqual(call.body, {
dshToken: 'dshs_existing',
origin: 'http://dsh.example',
csrfToken: 'dshc_existing',
requireCsrf: true,
method: 'POST',
target: '/_dsh_platform/api/v1/restart-dsh',
})
assert.equal((await current.authentication.authorizePlugin({
headers: {
host: 'dsh.example', origin: 'http://dsh.example',
cookie: `${DSH_SESSION_COOKIE}=missing`, 'sec-fetch-site': 'same-origin',
},
}, { method: 'GET', target: '/_dsh_platform/api/v1/status' })).authorized, false)
})
test('Management pending page asks only for the additional password', () => {
const current = fixture('initialized')
return listen(current.server).then(async port => {
try {
const page = await request(port, {
path: '/_dsh_platform/auth/management/pending',
headers: {
host: `127.0.0.1:${port}`,
cookie: 'dsh_management_pending=dshmp_pending',
},
})
assert.equal(page.status, 200)
assert.equal(page.headers['set-cookie'].some(value => value.startsWith(`${MANAGEMENT_CSRF_COOKIE}=`)), true)
assert.equal(page.headers['set-cookie'].some(value => value.startsWith(`${AUTH_CSRF_COOKIE}=`)), false)
assertInlineScriptsCompile(page.body)
assert.match(page.body, /Management console password/)
assert.match(page.body, /html\{height:100%;color-scheme:light dark}/)
assert.match(page.body, /body\{min-height:100%;margin:0;display:grid;place-items:center;/)
assert.doesNotMatch(page.body, /100dvh/)
assert.match(page.body, /input:focus-visible\{border-color:#f3f3f4;outline:none\}/)
assert.match(page.body, /AUTHENTICATION_RETRY_REQUIRED/)
assert.match(page.body, /AUTHENTICATION_RATE_LIMITED/)
assert.match(page.body, /AUTHENTICATION_CONTEXT_STALE/)
assert.match(page.body, /location\.replace\("\/_dsh_platform\/auth\/management"\)/)
assert.match(page.body, /retryUntil=Date\.now\(\)\+seconds\*1000/)
assert.match(page.body, /Math\.ceil\(\(retryUntil-Date\.now\(\)\)\/1000\)/)
assert.match(page.body, /button\.disabled=true/)
assert.match(page.body, /error\.hidden=true;button\.disabled=false/)
assert.match(page.body, /if\(response\.ok\)\{navigating=true;location\.replace/)
assert.match(page.body, /finally\{if\(!navigating&&retryUntil<=Date\.now\(\)\)button\.disabled=false}/)
assert.match(page.body, /<form method="post" action="\/_dsh_platform\/auth\/management\/pending" novalidate>/)
assert.doesNotMatch(page.body, /minlength="8"|pattern="|field-error|Use 8 to 1024 characters/)
assert.doesNotMatch(page.body, /name="username"|management\/session/)
} finally { await close(current.server) }
})
})
test('a stale Management pending submission is renewed and completed without asking again', async () => {
for (const staleCsrf of [false, true]) {
const current = fixture('initialized', { managementPending: true })
const port = await listen(current.server)
try {
const host = `127.0.0.1:${port}`
const origin = `http://${host}`
current.sessions.set('dshs_existing', origin)
const page = await request(port, {
path: '/_dsh_platform/auth/management/pending',
headers: {
host,
cookie: `${DSH_SESSION_COOKIE}=dshs_existing; dsh_management_pending=dshmp_stale`,
},
})
const csrfCookie = page.headers['set-cookie'].find(value => value.startsWith(`${MANAGEMENT_CSRF_COOKIE}=`))
const csrf = csrfCookie.split(';')[0].split('=')[1]
const response = await request(port, {
path: '/_dsh_platform/auth/management/pending', method: 'POST',
headers: {
host, origin,
cookie: `${DSH_SESSION_COOKIE}=dshs_existing; dsh_management_pending=dshmp_stale; ${MANAGEMENT_CSRF_COOKIE}=${csrf}`,
'content-type': 'application/json',
'x-dsh-csrf': staleCsrf ? 'dshm_stale' : csrf,
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
},
body: JSON.stringify({
password: 'correct management password',
authenticationContext: staleCsrf ? 'dsh-authentication-context' : 'stale-context',
}),
})
assert.equal(response.status, 200)
assert.equal(JSON.parse(response.body).authenticated, true)
assert.equal(response.headers['set-cookie'].some(value => value.startsWith(`${MANAGEMENT_SESSION_COOKIE}=dshms_completed`)), true)
assert.equal(current.calls.filter(call => call.path === '/v1/management/handoffs').length, 1)
assert.equal(current.calls.filter(call => call.path === '/v1/management/handoffs/consume').length, 1)
assert.equal(current.calls.filter(call => call.path === '/v1/management/pending/complete').length, staleCsrf ? 1 : 2)
} finally { await close(current.server) }
}
})
test('an incorrect Management password is rejected once without renewing its pending login', async () => {
const current = fixture('initialized', { managementPending: true })
const port = await listen(current.server)
try {
const host = `127.0.0.1:${port}`
const origin = `http://${host}`
current.sessions.set('dshs_existing', origin)
const handoff = await current.access.request('POST', '/v1/management/handoffs', {
dshToken: 'dshs_existing', dshOrigin: origin, targetOrigin: origin,
})
const exchanged = await current.access.request('POST', '/v1/management/handoffs/consume', {
token: handoff.handoff.token, origin,
})
const page = await request(port, {
path: '/_dsh_platform/auth/management/pending',
headers: {
host,
cookie: `${DSH_SESSION_COOKIE}=dshs_existing; dsh_management_pending=${exchanged.pending.token}`,
},
})
const csrfCookie = page.headers['set-cookie'].find(value => value.startsWith(`${MANAGEMENT_CSRF_COOKIE}=`))
const csrf = csrfCookie.split(';')[0].split('=')[1]
const callsBefore = current.calls.length
const response = await request(port, {
path: '/_dsh_platform/auth/management/pending', method: 'POST',
headers: {
host, origin,
cookie: `${DSH_SESSION_COOKIE}=dshs_existing; dsh_management_pending=${exchanged.pending.token}; ${MANAGEMENT_CSRF_COOKIE}=${csrf}`,
'content-type': 'application/json', 'x-dsh-csrf': csrf,
'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors',
},
body: JSON.stringify({
password: 'wrong management password',
authenticationContext: 'dsh-authentication-context',
}),
})
assert.equal(response.status, 401)
assert.equal(JSON.parse(response.body).code, 'AUTHENTICATION_FAILED')
assert.deepEqual(current.calls.slice(callsBefore).map(call => call.path), ['/v1/management/pending/complete'])
} finally { await close(current.server) }
})