Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
TAGS
bower_components
configs/*-secrets.js
configs/local.js
coverage
node_modules
npm-debug.log
Expand Down
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ implements the [Credential Handler API][] would function.

## Requirements

* Node.js v16+
* npm v8+
* Node.js v24+
* npm v10+

## Development

Expand All @@ -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

Expand Down
25 changes: 24 additions & 1 deletion configs/dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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);
}
56 changes: 56 additions & 0 deletions configs/local.js.example
Original file line number Diff line number Diff line change
@@ -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://<server.host>`, 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'];
*/