dsh-remote 0.5.0: node settings, session-cookie edge auth, sign-in popup

- Settings → Remote devices → Node: type master|slave (stored in state, beats
  config role; Restart DSH button under systemd/launchd), origin (read-only:
  own origin / paired master), 'Master has an auth middleware' toggle
- middleware on: a non-DSH answer on the link (login redirect, 401/403, inline
  HTML) raises auth-required; the UI opens <master>/dsh-remote/signin in a popup,
  the user signs in to the proxy, one click hands the proxy session cookie back
  (postMessage, origin-checked, trusted origins only), link retries
- edge mode 'cookie': pasted authentik_proxy_… (or a whole Cookie header),
  rotated proxy cookies persisted back; pair.mjs/install-mac --edge cookie
- connectMaster serialised; a failed attempt never clears a finished link
- tests: cookie seed/mode, HTML login wall; e2e-signin against a fake proxy
This commit is contained in:
lyuksovannyy committed 2026-10-12 09:37:21 +00:00
1 parent 78fce8e1ce
commit bfbc3c66e4
13 files changed
+491 -34

No files matched your search

+31 -2
View File
@@ -78,7 +78,7 @@ test('mode none: a login redirect becomes a clear edge error with a hint', async
const { srv } = fakeProxy()
const port = await listen(srv)
const e = createEdgeAuth({ mode: 'none' }, resolve)
await assert.rejects(e.edgeFetch(`http://127.0.0.1:${port}/dsh-remote/enroll`, { method: 'POST' }), (err) => /auth proxy/.test(err.message) && /configure edgeAuth/.test(err.hint))
await assert.rejects(e.edgeFetch(`http://127.0.0.1:${port}/dsh-remote/enroll`, { method: 'POST' }), (err) => /auth proxy/.test(err.message) && /sign in through the auth proxy/.test(err.hint))
srv.close()
})
@@ -123,7 +123,7 @@ test('oauth2 bad credentials and basic wrong password surface clear errors', asy
const o = createEdgeAuth({ mode: 'oauth2', tokenUrl: `http://127.0.0.1:${port}/token`, clientId: 'cid', username: 'svc', passwordRef: 'NOPE' }, async (r) => (r === 'NOPE' ? 'wrong' : undefined))
await assert.rejects(o.edgeFetch(`http://127.0.0.1:${port}/x`), /token request failed \(HTTP 400: invalid_grant/)
const b = createEdgeAuth({ mode: 'basic', username: 'svc', passwordRef: 'NOPE' }, async () => 'wrong')
await assert.rejects(b.edgeFetch(`http://127.0.0.1:${port}/x`), (err) => /refused the request \(HTTP 401\)/.test(err.message) && /check the edgeAuth credentials/.test(err.hint))
await assert.rejects(b.edgeFetch(`http://127.0.0.1:${port}/x`), (err) => /refused the request \(HTTP 401\)/.test(err.message) && /rejected the stored credentials/.test(err.hint))
const missing = createEdgeAuth({ mode: 'basic', username: 'svc', passwordRef: 'UNSET' }, async () => undefined)
await assert.rejects(missing.edgeFetch(`http://127.0.0.1:${port}/x`), /secret "UNSET"/)
srv.close()
@@ -152,6 +152,35 @@ test('cookie jar: expiry and deletion', () => {
assert.equal(j.header(), 'c=3')
})
test('cookie jar seed: accepts name=value, a Cookie: header and multi-line paste; drops dsh-auth-*', () => {
const j = new CookieJar()
assert.equal(j.seed('Cookie: authentik_proxy_ab12=XYZ; dsh-auth-foo=bar; other="q"'), 2)
assert.equal(j.header(), 'authentik_proxy_ab12=XYZ; other=q')
j.clear()
j.seed('authentik_proxy_ab12=XYZ\nextra=1\n')
assert.equal(j.header(), 'authentik_proxy_ab12=XYZ; extra=1')
})
test('mode cookie: pasted cookie passes the proxy, rotated cookie is persisted, HTML page counts as a login wall', async () => {
const { srv, sessions } = fakeProxy()
const port = await listen(srv)
sessions.add('abc123')
const saved = []
const e = createEdgeAuth({ mode: 'cookie', cookieRef: 'COOK' }, async () => 'Cookie: edge_session=abc123; dsh-auth-x=ignored', { persistCookie: (c) => saved.push(c) })
const r = await e.edgeFetch(`http://127.0.0.1:${port}/dsh-remote/enroll`, { method: 'POST' })
assert.equal(r.status, 200)
assert.deepEqual(e.describe().cookies, ['edge_session'])
// A stale cookie → the proxy redirects → clear error with a sign-in hint.
const stale = createEdgeAuth({ mode: 'cookie', cookieRef: 'COOK' }, async () => 'edge_session=nope')
await assert.rejects(stale.edgeFetch(`http://127.0.0.1:${port}/dsh-remote/enroll`, { method: 'POST' }), (err) => /refused the request/.test(err.message) && /sign in again/.test(err.hint))
// An inline HTML sign-in page (200) is not DSH either.
const htmlSrv = http.createServer((req, res) => { res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' }); res.end('<html>Sign in</html>') })
const hp = await listen(htmlSrv)
const none = createEdgeAuth({ mode: 'none' }, resolve)
await assert.rejects(none.edgeFetch(`http://127.0.0.1:${hp}/dsh-remote/enroll`, { method: 'POST' }), /HTML page/)
srv.close(); htmlSrv.close()
})
test('pair.mjs credentials.yaml ref writer keeps existing records and refs', () => {
const before = 'version: 1\nrecords:\n client-connection/browser-session:\n kind: grant\nrefs:\n OMNI_API_KEY: sk-1\n'
const after = setRefIn(before, 'DSH_REMOTE_EDGE_PASSWORD', 'p"w')