]> git.ardis.lu - envpass/commitdiff
Use custom `Envpass-Challenge` header
authorArdis Lu <ardisglu@gmail.com>
Sun, 24 May 2026 06:16:34 +0000 (23:16 -0700)
committerArdis Lu <ardisglu@gmail.com>
Sun, 24 May 2026 06:16:34 +0000 (23:16 -0700)
Separating `challenge` from `prf` allows more flexibility to change the format that `prf` is bridged (e.g., to `application/octet-stream`) and it is more consistent with how similar material is usually passed in other APIs.

src/getPrf.html
src/getPrf.js
test/getPrf.test.js

index 4c84f464c686f1a28a7cb397e0558ae03265839e..1965a30471f19d812600a3a0b0ab2c05498ade80 100644 (file)
       const prf = new Uint8Array(prfBuffer).toHex();
       const response = await fetch(`http://localhost:${location.port}`, { // Intentionally hardcoding localhost, do not broadcast anywhere else
         method: 'POST',
-        headers: { 'Content-Type': 'application/json' },
-        body: JSON.stringify({ challenge, prf })
+        headers: {
+          'Content-Type': 'application/json',
+          'Envpass-Challenge': challenge
+        },
+        body: JSON.stringify({ prf })
       });
       if (response.status === 200) {
         p.textContent = 'Successfully sent passkey. You may close this window now.';
index 8dc3c6f2ad142bfcd2cacd6a615448d255f19113..be19216bf0b80446c4a69903727323f729d13b75 100644 (file)
@@ -125,14 +125,14 @@ export async function getPrf(options = {}) {
         response.writeHead(400).end();
         return;
       }
+      if (request.headers['envpass-challenge'] !== challenge) {
+        response.writeHead(401).end();
+        return;
+      }
       let body = '';
       request.on('data', chunk => body += chunk);
       request.on('end', () => {
         const payload = JSON.parse(body);
-        if (payload.challenge !== challenge) {
-          response.writeHead(401).end();
-          return;
-        }
         const prf = parsePrf(payload.prf);
         if (prf === null) {
           response.writeHead(400).end();
index 0828d57f5f04f0c489eceb99bb2cf7c71352c731..a582431addcdc1449ea795ea47f8f34cee8e56f8 100644 (file)
@@ -22,7 +22,6 @@ function filterNullValues(obj) {
  * @typedef {Object} MakePostRequestOptions
  * @property {Record<string,string|null>} [headers] Headers to set on the request. Set a property to
  * `null` to *delete* that header.
- * @property {string|null} [challenge] `challenge` string to pass on the request body.
  * @property {string|null} [prf] `prf` string to pass on the request body.
  */
 
@@ -35,7 +34,6 @@ function makePostRequest(url, options = {}) {
   const { origin, searchParams } = new URL(url);
   const {
     headers = {},
-    challenge = searchParams.get('challenge'),
     prf = 'deadbeef000000000000000000000000000000000000000000000000cafebabe'
   } = options;
   const mergedHeaders = {
@@ -44,10 +42,11 @@ function makePostRequest(url, options = {}) {
     'Sec-Fetch-Site': 'same-origin',
     'Sec-Fetch-Mode': 'cors',
     'Sec-Fetch-Dest': 'empty',
+    'Envpass-Challenge': searchParams.get('challenge'),
     ...headers
   }
   const filteredHeaders = filterNullValues(mergedHeaders);
-  const body = filterNullValues({ challenge, prf });
+  const body = filterNullValues({ prf });
   return new Request(origin, {
     method: 'POST',
     headers: filteredHeaders,
@@ -88,7 +87,6 @@ suite('getPrf.js (server)', () => {
     // See https://github.com/nodejs/undici/issues/1305
     const { url } = await setupServer(t);
     const { origin, searchParams } = new URL(url);
-    const challenge = searchParams.get('challenge');
     const prf = 'deadbeef000000000000000000000000000000000000000000000000cafebabe';
     const options = {
       method: 'POST',
@@ -96,9 +94,10 @@ suite('getPrf.js (server)', () => {
         'Content-Type': 'application/json',
         'Origin': origin,
         'Sec-Fetch-Site': 'same-origin',
-        'Sec-Fetch-Dest': 'empty'
+        'Sec-Fetch-Dest': 'empty',
+        'Envpass-Challenge': searchParams.get('challenge')
       }),
-      body: JSON.stringify({ challenge, prf })
+      body: JSON.stringify({ prf })
     }
     deepStrictEqual((await request(origin, options)).statusCode, 403); // Tests the unset case
     options.headers['Sec-Fetch-Mode'] = 'navigate';
@@ -126,10 +125,10 @@ suite('getPrf.js (server)', () => {
     deepStrictEqual((await fetch(makePostRequest(url, { headers: { 'Content-Type': null } }))).status, 400);
     deepStrictEqual((await fetch(makePostRequest(url, { headers: { 'Content-Type': 'INVALID' } }))).status, 400);
   });
-  test('validates challenge on POST', async (t) => {
+  test('validates Envpass-Challenge header on POST', async (t) => {
     const { url } = await setupServer(t);
-    deepStrictEqual((await fetch(makePostRequest(url, { challenge: null }))).status, 401);
-    deepStrictEqual((await fetch(makePostRequest(url, { challenge: 'INVALID' }))).status, 401);
+    deepStrictEqual((await fetch(makePostRequest(url, { headers: { 'Envpass-Challenge': null } }))).status, 401);
+    deepStrictEqual((await fetch(makePostRequest(url, { headers: { 'Envpass-Challenge': 'INVALID' } }))).status, 401);
   });
   test('validates prf on POST', async (t) => {
     const { url } = await setupServer(t);
This page took 0.047381 seconds and 4 git commands to generate.