Support native HTTPS Web listeners

This commit is contained in:
Yichen Yan authored and Yichen Yan committed 2026-10-08 19:49:01 +08:00
1 parent 4f8e70f936
commit ac080346f0
71 files changed
+1355 -337

No files matched your search

@@ -76,11 +76,12 @@ function request(url: string, authority = '127.0.0.1:3080', init?: {
function exchange(
auth: BrowserAuth,
authority = '127.0.0.1:3080',
secure = false,
): { cookie: string; launchUrl: string; state: ResponseState } {
const launchUrl = auth.authenticatedUrl(`http://${authority}`)
const target = new URL(launchUrl)
const res = response()
expect(auth.authorizeIndex(request(`${target.pathname}${target.search}`, authority), res.value)).toBe(false)
expect(auth.authorizeIndex(request(`${target.pathname}${target.search}`, authority), res.value, secure)).toBe(false)
const setCookie = res.state.headers?.['set-cookie']
if (setCookie === undefined) throw new Error('token exchange did not set a cookie')
return { cookie: setCookie.split(';', 1)[0]!, launchUrl, state: res.state }
@@ -140,6 +141,34 @@ describe('BrowserAuth', () => {
})
})
it('keeps HTTPS port 443 distinct from HTTP port 80 with one signing secret', async () => {
const auth = await createAuth(new RecordCredentials())
const plain = exchange(auth, 'harness.example')
const tls = exchange(auth, 'harness.example', true)
expect(auth.isAuthenticated(request('/', 'harness.example', { cookie: plain.cookie }), true)).toBe(false)
expect(auth.isAuthenticated(request('/', 'harness.example', { cookie: tls.cookie }), true)).toBe(true)
expect(auth.isAuthenticated(request('/', 'harness.example:443', { cookie: tls.cookie }), true)).toBe(true)
expect(auth.isAuthenticated(request('/', 'harness.example', { cookie: tls.cookie }))).toBe(false)
})
it('rejects HTTP cookie replay after enabling TLS on the same authority', async () => {
const store = new RecordCredentials()
const authority = 'harness.example:3080'
const plain = exchange(await createAuth(store), authority)
const restarted = await createAuth(store)
const denied = response()
expect(restarted.authorizeIndex(request('/', authority, { cookie: plain.cookie }), denied.value, true)).toBe(false)
expect(denied.state.status).toBe(401)
const tls = exchange(restarted, authority, true)
expect(tls.state.headers?.['set-cookie']).toContain('; Secure')
expect(restarted.isAuthenticated(request('/', authority, { cookie: tls.cookie }), true)).toBe(true)
expect(restarted.isAuthenticated(request('/', authority, { cookie: tls.cookie }))).toBe(false)
const tlsName = tls.cookie.slice(0, tls.cookie.indexOf('='))
const plainValue = plain.cookie.slice(plain.cookie.indexOf('=') + 1)
expect(restarted.isAuthenticated(request('/', authority, { cookie: `${tlsName}=${plainValue}` }), true)).toBe(false)
})
it('preserves the caller authority and mount while adding only this process token', async () => {
const auth = await createAuth(new RecordCredentials())
const mounted = new URL(auth.authenticatedUrl('https://gateway.example/tools/dsh/'))