javascript-solid-server 0.0.157 → 0.0.158

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "javascript-solid-server",
3
- "version": "0.0.157",
3
+ "version": "0.0.158",
4
4
  "description": "A minimal, fast Solid server",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/config.js CHANGED
@@ -324,6 +324,29 @@ export async function loadConfig(cliOptions = {}, configFile = null) {
324
324
  config.conneg = true;
325
325
  }
326
326
 
327
+ // Single-user mode strongly implies the built-in IdP. Operators seeding
328
+ // a password for `me` on localhost almost always want the IdP enabled
329
+ // so clients can authenticate. Imply --idp unless the user explicitly
330
+ // disabled it from any config source — CLI, env, or config file (see #331).
331
+ if (config.singleUser && !config.idp) {
332
+ const idpExplicitlyDisabled =
333
+ cliOptions.idp === false ||
334
+ envConfig.idp === false ||
335
+ fileConfig.idp === false;
336
+ if (idpExplicitlyDisabled) {
337
+ // Respect the explicit disable. Warn only when there is no external
338
+ // --idp-issuer either: without the built-in IdP and without an
339
+ // external issuer, /.well-known/openid-configuration returns 404
340
+ // and clients fail with confusing OIDC discovery errors. If the
341
+ // operator pointed JSS at an external issuer, no footgun applies.
342
+ if (!config.idpIssuer) {
343
+ console.warn('⚠️ --single-user is enabled but --idp is disabled and no --idp-issuer is set. Clients won\'t be able to authenticate. Use --idp, or pass an external --idp-issuer.');
344
+ }
345
+ } else {
346
+ config.idp = true;
347
+ }
348
+ }
349
+
327
350
  // Validate SSL config
328
351
  if ((config.sslKey && !config.sslCert) || (!config.sslKey && config.sslCert)) {
329
352
  throw new Error('Both --ssl-key and --ssl-cert must be provided together');
@@ -7,7 +7,7 @@
7
7
  * become a real boolean and break downstream code (bcrypt, etc.).
8
8
  */
9
9
 
10
- import { describe, it, before, after } from 'node:test';
10
+ import { describe, it, before, after, beforeEach } from 'node:test';
11
11
  import assert from 'node:assert';
12
12
  import { loadConfig } from '../src/config.js';
13
13
 
@@ -64,3 +64,97 @@ describe('config — env var boolean coercion', () => {
64
64
  assert.strictEqual(cfg2.multiuser, true);
65
65
  });
66
66
  });
67
+
68
+ // Regression coverage for #331: --single-user without --idp boots a server
69
+ // that returns 404 for /.well-known/openid-configuration, which is a
70
+ // footgun. loadConfig() now implies --idp when --single-user is set,
71
+ // unless the user explicitly disables IdP via --no-idp / JSS_IDP=false /
72
+ // idp:false in config file.
73
+ describe('config — --single-user implies --idp (#331)', () => {
74
+ // Hermetic env-var handling so the runner's environment doesn't leak.
75
+ // JSS_LOG_LEVEL is included because loadConfig() can emit a warning
76
+ // for invalid log levels and we don't want that to pollute assertions
77
+ // about the #331-specific warning.
78
+ const KEYS = ['JSS_IDP', 'JSS_SINGLE_USER', 'JSS_IDP_ISSUER', 'JSS_LOG_LEVEL'];
79
+ const original = {};
80
+ let originalWarn;
81
+ let warnings;
82
+
83
+ before(() => {
84
+ for (const k of KEYS) original[k] = process.env[k];
85
+ originalWarn = console.warn;
86
+ });
87
+
88
+ after(() => {
89
+ for (const k of KEYS) {
90
+ if (original[k] === undefined) delete process.env[k];
91
+ else process.env[k] = original[k];
92
+ }
93
+ console.warn = originalWarn;
94
+ });
95
+
96
+ // Capture warnings so we can assert on them without polluting test output.
97
+ // We filter to the #331-specific warning so unrelated warnings (e.g. from
98
+ // a noisy runner env) don't break the assertions.
99
+ const isIdpFootgunWarning = (msg) =>
100
+ msg.includes('--single-user') && msg.includes('--idp');
101
+
102
+ beforeEach(() => {
103
+ for (const k of KEYS) delete process.env[k];
104
+ warnings = [];
105
+ console.warn = (msg) => warnings.push(String(msg));
106
+ });
107
+
108
+ it('implies --idp when --single-user is set and --idp is not specified', async () => {
109
+ const cfg = await loadConfig({ singleUser: true }, null);
110
+ assert.strictEqual(cfg.idp, true,
111
+ '--single-user should imply --idp by default');
112
+ assert.ok(!warnings.some(isIdpFootgunWarning),
113
+ 'no #331 warning when implying (this is the happy path)');
114
+ });
115
+
116
+ it('does not imply --idp when --single-user is not set', async () => {
117
+ const cfg = await loadConfig({}, null);
118
+ assert.notStrictEqual(cfg.idp, true,
119
+ '--idp should not be implied without --single-user');
120
+ });
121
+
122
+ it('respects explicit --idp=true with --single-user', async () => {
123
+ const cfg = await loadConfig({ singleUser: true, idp: true }, null);
124
+ assert.strictEqual(cfg.idp, true);
125
+ });
126
+
127
+ it('respects explicit --no-idp with --single-user (warns but does not flip)', async () => {
128
+ const cfg = await loadConfig({ singleUser: true, idp: false }, null);
129
+ assert.strictEqual(cfg.idp, false,
130
+ 'explicit --no-idp should override the implication');
131
+ assert.ok(warnings.some(isIdpFootgunWarning),
132
+ 'should warn about the footgun when --single-user + --no-idp + no --idp-issuer');
133
+ });
134
+
135
+ it('respects explicit JSS_IDP=false with --single-user (warns but does not flip)', async () => {
136
+ process.env.JSS_IDP = 'false';
137
+ const cfg = await loadConfig({ singleUser: true }, null);
138
+ assert.strictEqual(cfg.idp, false,
139
+ 'explicit JSS_IDP=false should override the implication');
140
+ assert.ok(warnings.some(isIdpFootgunWarning),
141
+ 'should warn when JSS_IDP=false + --single-user + no --idp-issuer');
142
+ });
143
+
144
+ it('does not warn when --no-idp + --single-user but --idp-issuer is set', async () => {
145
+ const cfg = await loadConfig({
146
+ singleUser: true,
147
+ idp: false,
148
+ idpIssuer: 'https://external-issuer.example/'
149
+ }, null);
150
+ assert.strictEqual(cfg.idp, false);
151
+ assert.ok(!warnings.some(isIdpFootgunWarning),
152
+ 'no footgun if an external --idp-issuer is configured');
153
+ });
154
+
155
+ it('does not warn when --single-user is unset', async () => {
156
+ await loadConfig({ idp: false }, null);
157
+ assert.ok(!warnings.some(isIdpFootgunWarning),
158
+ '--no-idp without --single-user should not trigger the #331 warning');
159
+ });
160
+ });