/** * B7 · unit tests for the relay-side DPoP subprotocol decoder (`extractDpopProofFromSubprotocols`). * The browser (relay-web) offers the DPoP proof as an extra `term.dpop.` entry on * the WS upgrade because native WebSocket cannot set request headers. These tests pin the wire * contract (valid decodes; absent → null; malformed → null, fail-closed) against the SAME isomorphic * base64url helper relay-web encodes with (relay-contracts), so the two sides agree byte-for-byte. */ import { describe, it, expect } from 'vitest' import { encodeBase64UrlString } from 'relay-contracts' import { extractDpopProofFromSubprotocols, DPOP_SUBPROTOCOL_PREFIX, } from '../src/servers/dpop-subprotocol.js' import { APP_SUBPROTOCOL, TOKEN_SUBPROTOCOL_PREFIX } from 'relay-contracts' /** Build the wire entry exactly as relay-web `encodeDpopSubprotocol` does. */ function dpopEntry(proofJws: string): string { return DPOP_SUBPROTOCOL_PREFIX + encodeBase64UrlString(proofJws) } // A representative three-part DPoP proof JWS (header.payload.sig) — content is opaque to the decoder. const PROOF = 'eyJhbGciOiJFZERTQSJ9.eyJodHUiOiJodHRwczovL2FsaWNlL3dzIn0.c2ln' describe('extractDpopProofFromSubprotocols — valid', () => { it('decodes a term.dpop. entry back to the exact proof JWS', () => { const subprotocols = [APP_SUBPROTOCOL, dpopEntry(PROOF)] expect(extractDpopProofFromSubprotocols(subprotocols)).toBe(PROOF) }) it('finds the DPoP entry regardless of position (after app + token entries)', () => { const subprotocols = [ APP_SUBPROTOCOL, TOKEN_SUBPROTOCOL_PREFIX + encodeBase64UrlString('v4.public.raw-token'), dpopEntry(PROOF), ] expect(extractDpopProofFromSubprotocols(subprotocols)).toBe(PROOF) }) }) describe('extractDpopProofFromSubprotocols — absent → null', () => { it('returns null when no term.dpop. entry is present', () => { expect(extractDpopProofFromSubprotocols([APP_SUBPROTOCOL])).toBeNull() }) it('returns null for an empty list', () => { expect(extractDpopProofFromSubprotocols([])).toBeNull() }) it('returns null when the prefix is present but the payload is empty', () => { expect(extractDpopProofFromSubprotocols([DPOP_SUBPROTOCOL_PREFIX])).toBeNull() }) }) describe('extractDpopProofFromSubprotocols — malformed → null (fail-closed)', () => { it('returns null for a non-base64url payload (illegal characters)', () => { expect(extractDpopProofFromSubprotocols([DPOP_SUBPROTOCOL_PREFIX + 'not*base64url!'])).toBeNull() }) it('returns null for base64url bytes that are not valid UTF-8', () => { // 0xFF 0xFE is not a valid UTF-8 sequence; base64url of those two bytes is "__4". expect(extractDpopProofFromSubprotocols([DPOP_SUBPROTOCOL_PREFIX + '__4'])).toBeNull() }) })