diff --git a/.gitignore b/.gitignore index 983fb76..6983a3c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ TAGS bower_components configs/*-secrets.js +configs/local.js coverage node_modules npm-debug.log diff --git a/README.md b/README.md index 06f244d..e8f180b 100644 --- a/README.md +++ b/README.md @@ -45,8 +45,8 @@ implements the [Credential Handler API][] would function. ## Requirements -* Node.js v16+ -* npm v8+ +* Node.js v24+ +* npm v10+ ## Development @@ -55,8 +55,22 @@ software on a local development machine. ### Configuration -The options in the `./configs/authn.localhost.js` file can be tuned to your -environment as needed. +The options in the `./configs/dev.js` file can be tuned to your environment as +needed. + +Prefer a local override to editing that file, since it is checked in. Copy the +example and edit the copy: + + cp configs/local.js.example configs/local.js + +`configs/local.js` is imported last when it exists, so its settings win, and it +is gitignored. Delete it to go back to the defaults. The server reads it at +startup, so restart after changing it. + +The example covers the case it exists for: serving the mediator under a tunnel +hostname so a phone can reach it, which needs `server.host` changed and cannot +be done from a checked-in file. It also shows how to restrict `server.bindAddr` +to loopback, since `configs/dev.js` binds to all interfaces for Docker. ### Setup diff --git a/configs/dev.js b/configs/dev.js index c191b45..590e553 100644 --- a/configs/dev.js +++ b/configs/dev.js @@ -6,8 +6,9 @@ * Copyright (c) 2015-2016, Accreditrust Technologies, LLC * All rights reserved. */ +import {fileURLToPath, pathToFileURL} from 'node:url'; import {config} from '@bedrock/core'; -import {fileURLToPath} from 'node:url'; +import {existsSync} from 'node:fs'; import os from 'node:os'; import path from 'node:path'; @@ -22,3 +23,25 @@ config.paths.log = path.join(os.tmpdir(), 'authn.localhost'); // note: this also exposes the mediator on all LAN interfaces, not just // Docker's bridge — acceptable for local dev, avoid on shared networks. config.server.bindAddr = ['0.0.0.0']; + +/* Optional local overrides, loaded last so they win. `configs/local.js` is +gitignored, so machine-specific settings -- e.g. pointing `server.host` at a +tunnel hostname so a phone can reach the mediator -- stay out of the tracked +config. See `configs/local.js.example`. + +This is the development override channel. `@bedrock/config-yaml` (imported +from `lib/index.js`) is another, but it reads `/etc/bedrock-config/app.yaml` +or a base64 blob in `BEDROCK_CONFIG`, which suits deployment rather than a +working copy. + +Assign config values only. The file is imported after `lib/index.js` has +imported `@bedrock/config-yaml`, so registering a `bedrock.events` handler +here fails with `"bedrock-config-yaml" must be the last import`. + +`pathToFileURL` because `import()` takes a URL, not a filesystem path: a +checkout under a directory containing `#` truncates at the fragment, and a +Windows path parses its drive letter as a URL scheme. */ +const localConfigPath = path.join(__dirname, 'local.js'); +if(existsSync(localConfigPath)) { + await import(pathToFileURL(localConfigPath).href); +} diff --git a/configs/local.js.example b/configs/local.js.example new file mode 100644 index 0000000..7656e7b --- /dev/null +++ b/configs/local.js.example @@ -0,0 +1,56 @@ +/*! + * Example local development overrides. + * + * Copy to `configs/local.js` and edit. That path is gitignored, so machine + * specific settings stay out of the tracked config. + * + * Assign config values only. This file is imported after `lib/index.js` has + * imported `@bedrock/config-yaml`, so registering a `bedrock.events` handler + * here fails with `"bedrock-config-yaml" must be the last import`. + * + * Copyright (c) 2026, Digital Bazaar, Inc. + */ +import {config} from '@bedrock/core'; + +/* Serve the mediator under a tunnel hostname, so a phone can reach it. + +`server.host` is what the HTTP->HTTPS redirect uses. Left at its default, a +request arriving through a tunnel is redirected to +`https://authn.localhost:33443`, which resolves only on the machine running +the server -- so the mediator iframe fails to load on a phone. + +Set `server.host` rather than `server.domain`: `host` is derived as +`domain + ':' + port` unless the port is 443, so overriding `domain` yields a +tunnel hostname with the local port glued on. Overriding the port to 443 to +avoid that would stop the server listening on 33443. Setting `host` leaves +both local ports (33443 HTTPS, 33080 HTTP) alone. + +`server.baseUri` needs no override: it is the lazily-evaluated template +`https://${server.host}`, so it follows `host` automatically. Setting both +means two values that have to agree. + +Point the tunnel at the HTTPS port (33443), not the HTTP one. The HTTP port +redirects unconditionally to `https://`, which with a tunnel +hostname redirects to itself forever. For example, with ngrok: + + ngrok http --url=YOUR-HOSTNAME.ngrok.app \ + --upstream-tls-verify=false https://localhost:33443 + +Coordinator sites under test must point their CHAPI mediator origin at the +same hostname. + +One side effect while this is set: `@bedrock/express` sends +`Access-Control-Allow-Private-Network` only when `server.host` contains +`localhost`, so a coordinator site on a public origin that reaches the local +mediator *directly* -- rather than through the tunnel -- will start failing +Chrome's private-network preflight. Traffic through the tunnel is unaffected. +Delete this file to restore the header. */ +config.server.host = 'YOUR-HOSTNAME.ngrok.app'; + +/* `configs/dev.js` binds to all interfaces so Docker containers can reach the +host via `host.docker.internal`. That also exposes the dev mediator on every +LAN interface, which is worth avoiding on a shared or public network. Restrict +it here when you do not need Docker access: + +config.server.bindAddr = ['127.0.0.1']; +*/