@o3co/create-auth-provider 0.16.0 → 0.17.0-rc.1
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/README.ja.md +66 -21
- package/README.md +76 -31
- package/dist/index.d.mts +33 -10
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +118 -59
- package/dist/internal/template-filter.d.mts +7 -10
- package/dist/internal/template-filter.d.mts.map +1 -1
- package/dist/internal/template-filter.mjs +7 -10
- package/package.json +6 -4
- package/templates/standalone/.dockerignore +15 -2
- package/templates/standalone/.env.example +59 -26
- package/templates/standalone/Dockerfile +6 -2
- package/templates/standalone/README.ja.md +293 -162
- package/templates/standalone/README.md +533 -199
- package/templates/standalone/config/application.conf +196 -341
- package/templates/standalone/config/clients.yaml +2 -0
- package/templates/standalone/config/clients.yaml.example +4 -0
- package/templates/standalone/config/development.conf +13 -4
- package/templates/standalone/config/mailpit.conf +8 -0
- package/templates/standalone/config/production.conf +5 -6
- package/templates/standalone/config/reference.conf +399 -0
- package/templates/standalone/docker-compose.mailpit.yml +36 -0
- package/templates/standalone/docker-compose.production.yml +58 -30
- package/templates/standalone/docker-compose.yml +13 -3
- package/templates/standalone/gitignore +1 -0
- package/templates/standalone/package.json +9 -6
- package/templates/standalone/src/__tests__/access-token-lifetime-alias.test.mts +19 -20
- package/templates/standalone/src/__tests__/adapters.test.mts +358 -0
- package/templates/standalone/src/__tests__/all-modules-composition.fixture.mts +216 -141
- package/templates/standalone/src/__tests__/all-modules-composition.multi.test.mts +48 -33
- package/templates/standalone/src/__tests__/all-modules-composition.test.mts +77 -58
- package/templates/standalone/src/__tests__/audit-sink.test.mts +78 -76
- package/templates/standalone/src/__tests__/configPath.test.mts +13 -11
- package/templates/standalone/src/__tests__/core-federations.test.mts +226 -0
- package/templates/standalone/src/__tests__/core-sections.test.mts +190 -0
- package/templates/standalone/src/__tests__/device-code-store-client-module.test.mts +9 -14
- package/templates/standalone/src/__tests__/documented-env-overrides.test.mts +656 -269
- package/templates/standalone/src/__tests__/federation-grants-composition.test.mts +425 -84
- package/templates/standalone/src/__tests__/federation-grants-survive-logout.test.mts +91 -70
- package/templates/standalone/src/__tests__/federation-token-refresh-outage.test.mts +7 -5
- package/templates/standalone/src/__tests__/google-federation-config-module.test.mts +22 -19
- package/templates/standalone/src/__tests__/google-session.fixture.mts +53 -42
- package/templates/standalone/src/__tests__/host-routes.test.mts +4 -11
- package/templates/standalone/src/__tests__/library-references.fixture.mts +183 -0
- package/templates/standalone/src/__tests__/listen.test.mts +6 -13
- package/templates/standalone/src/__tests__/logger-projection.test.mts +1 -1
- package/templates/standalone/src/__tests__/mail-sender.test.mts +134 -0
- package/templates/standalone/src/__tests__/mailpit-compose.test.mts +328 -0
- package/templates/standalone/src/__tests__/metrics.test.mts +4 -9
- package/templates/standalone/src/__tests__/mfa-switch.redis.test.mts +123 -0
- package/templates/standalone/src/__tests__/mfa-switch.test.mts +780 -0
- package/templates/standalone/src/__tests__/oidc-federation-config-module.test.mts +52 -46
- package/templates/standalone/src/__tests__/own-modules.test.mts +1457 -0
- package/templates/standalone/src/__tests__/post-logout-redirect.test.mts +7 -11
- package/templates/standalone/src/__tests__/redis-connection-error-log.test.mts +1 -5
- package/templates/standalone/src/__tests__/redis-rate-limiter-config-survives.test.mts +112 -81
- package/templates/standalone/src/__tests__/refresh-token-family-client-module.test.mts +25 -31
- package/templates/standalone/src/__tests__/replica-safety.test.mts +153 -142
- package/templates/standalone/src/__tests__/router-error-answers.test.mts +4 -7
- package/templates/standalone/src/__tests__/scaffold-assets.test.mts +275 -95
- package/templates/standalone/src/__tests__/sections.test.mts +480 -0
- package/templates/standalone/src/__tests__/session-logout-invalidates-token.test.mts +67 -55
- package/templates/standalone/src/__tests__/session-requirements.test.mts +299 -0
- package/templates/standalone/src/__tests__/shutdown.test.mts +163 -96
- package/templates/standalone/src/__tests__/smoke.test.mts +179 -252
- package/templates/standalone/src/__tests__/store-credential.test.mts +91 -50
- package/templates/standalone/src/__tests__/supertest-loopback.test.mts +182 -11
- package/templates/standalone/src/__tests__/three-tier-resolution.test.mts +108 -95
- package/templates/standalone/src/__tests__/two-phase-config.test.mts +438 -0
- package/templates/standalone/src/__tests__/upstream-amr.acceptance.test.mts +390 -0
- package/templates/standalone/src/adapters.mts +149 -0
- package/templates/standalone/src/app.mts +64 -79
- package/templates/standalone/src/buildModules.mts +212 -251
- package/templates/standalone/src/configPath.mts +422 -29
- package/templates/standalone/src/listen.mts +12 -18
- package/templates/standalone/src/logger.mts +31 -60
- package/templates/standalone/src/metrics.mts +24 -47
- package/templates/standalone/src/mfaSwitch.mts +262 -0
- package/templates/standalone/src/modules.mts +458 -476
- package/templates/standalone/src/rootRenames.mts +79 -0
- package/templates/standalone/src/routes.mts +8 -14
- package/templates/standalone/src/sections.mts +366 -0
- package/templates/standalone/src/shutdown.mts +45 -123
- package/templates/standalone/tests/index.test.js +2 -2
- package/templates/standalone/tests/package-lock.json +1615 -2223
- package/templates/standalone/tests/package.json +2 -2
- package/templates/standalone/tests/vitest.config.mjs +26 -0
- package/templates/standalone/vitest.config.mts +8 -7
- package/templates/standalone/vitest.supertest-loopback.mts +97 -69
- package/templates/versions.json +18 -15
|
@@ -14,56 +14,50 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
17
|
-
import {
|
|
18
|
-
import { parseFile } from "@o3co/ts.hocon";
|
|
19
|
-
import { validate } from "@o3co/ts.hocon/zod";
|
|
17
|
+
import { createApp } from "@o3co/auth-provider-core";
|
|
20
18
|
import express from "express";
|
|
21
19
|
import helmet from "helmet";
|
|
22
20
|
import { buildModules } from "./buildModules.mjs";
|
|
23
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
readLogging,
|
|
23
|
+
readOwnLayers,
|
|
24
|
+
readSwitches,
|
|
25
|
+
resolveConfigPaths,
|
|
26
|
+
resolveForBoot,
|
|
27
|
+
type Switches,
|
|
28
|
+
} from "./configPath.mjs";
|
|
24
29
|
import { listen } from "./listen.mjs";
|
|
25
30
|
import { createAppLogger } from "./logger.mjs";
|
|
26
31
|
import { createMetrics } from "./metrics.mjs";
|
|
27
32
|
import { mountRoutes } from "./routes.mjs";
|
|
28
|
-
import {
|
|
33
|
+
import { installGracefulShutdown } from "./shutdown.mjs";
|
|
29
34
|
|
|
30
|
-
// Step 1:
|
|
35
|
+
// Step 1: the configuration, in two phases (`configPath.mts`; template
|
|
36
|
+
// README, "Environment-specific config overlay").
|
|
31
37
|
// ENV = CONFIG_ENV || NODE_ENV || "development"; missing {ENV}.conf is fatal.
|
|
32
|
-
//
|
|
33
|
-
// 3-tier HOCON precedence (highest → lowest):
|
|
34
|
-
// 1. {env}.conf — environment-specific overrides (e.g. production.conf)
|
|
35
|
-
// 2. application.conf — template-level consumer delta
|
|
36
|
-
// 3. reference.conf — library defaults shipped in @o3co/auth-provider-core
|
|
37
38
|
const env = process.env.CONFIG_ENV || process.env.NODE_ENV || "development";
|
|
38
39
|
const configDir = new URL("../config/", import.meta.url);
|
|
39
40
|
const configDirPath = fileURLToPath(configDir);
|
|
40
41
|
const { applicationConfPath, envConfPath } = resolveConfigPaths(configDirPath, env);
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
// Read once, so both phases read the same thing.
|
|
43
|
+
const own = readOwnLayers([envConfPath, applicationConfPath]);
|
|
44
|
+
// Phase one: what the template reads before it knows its modules, only for
|
|
45
|
+
// those choices — its own `adapters` and MFA switch (`mfaMode`, MFA_MODE),
|
|
46
|
+
// and the switches core's reader parses — and for what the composition
|
|
47
|
+
// expects of session admission: the configuration's list, with `mfa` added
|
|
48
|
+
// when the MFA switch installs MFA.
|
|
49
|
+
const switches: Switches = readSwitches(own);
|
|
48
50
|
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
// nothing about what the modules emit.
|
|
54
|
-
const logger = createAppLogger(config);
|
|
51
|
+
// From the `logging` module's section, so the level holds from the first line;
|
|
52
|
+
// wired into `bootstrapComponents` so every module logs through it (template
|
|
53
|
+
// README, "Logging").
|
|
54
|
+
const logger = createAppLogger(readLogging(own));
|
|
55
55
|
|
|
56
56
|
await (async (): Promise<void> => {
|
|
57
57
|
// Step 2: Create the Express app and apply base security middleware.
|
|
58
|
+
// `trust proxy` is set from the `http` module's `httpSettings` once boot
|
|
59
|
+
// has it (step 3), before a request can arrive.
|
|
58
60
|
const app = express();
|
|
59
|
-
// `false` | `true` | a hop count | a list of IPs / CIDR ranges / named
|
|
60
|
-
// ranges — the schema hands Express exactly the shapes `trust proxy`
|
|
61
|
-
// already understands, validated at boot so a typo'd range fails here
|
|
62
|
-
// rather than becoming a rule that silently never matches (#292). Prefer
|
|
63
|
-
// naming the proxy: `true` believes a forwarded client address from anyone
|
|
64
|
-
// who can reach this process, and `req.ip` is what every IP-keyed rate
|
|
65
|
-
// limit is bucketed on.
|
|
66
|
-
app.set("trust proxy", config.http.trustProxy);
|
|
67
61
|
app.use(
|
|
68
62
|
helmet({
|
|
69
63
|
contentSecurityPolicy: {
|
|
@@ -81,70 +75,61 @@ await (async (): Promise<void> => {
|
|
|
81
75
|
const metrics = createMetrics();
|
|
82
76
|
app.use(metrics.middleware);
|
|
83
77
|
|
|
84
|
-
// Step 3:
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
// `BuilderContext.lifecycle` and drained on `handle.dispose()`.
|
|
91
|
-
// bootstrapComponents carries only host-
|
|
92
|
-
// environment values (config + pathResolver per A2-γ §4 worked example);
|
|
93
|
-
// every other component flows through composition-root-local modules.
|
|
94
|
-
// `buildModules` is the single source of truth for the module list — it
|
|
95
|
-
// gates federation modules on `config.federations.<name>.enabled`, which
|
|
96
|
-
// the standalone scaffold defaults to false.
|
|
78
|
+
// Step 3: boot the auth pipeline. `buildModules` is the single source of
|
|
79
|
+
// truth for the module list and its order; `bootstrapComponents` carries
|
|
80
|
+
// only host-environment values, every other component comes from the
|
|
81
|
+
// modules. `environment` is the name the config was selected by: the Redis
|
|
82
|
+
// federation-token store's plaintext guard reads it as well as NODE_ENV,
|
|
83
|
+
// so `CONFIG_ENV=production` is production to the guard too.
|
|
97
84
|
//
|
|
98
|
-
// `
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
// key), the same logger every module gets through the slot below.
|
|
85
|
+
// Phase two: `createApp` parses the configuration as resolved once, with
|
|
86
|
+
// every loaded module's schema (`configPath.mts`). From here on the
|
|
87
|
+
// template reads the parsed configuration.
|
|
88
|
+
const modules = buildModules(switches, { environment: env, logger });
|
|
103
89
|
const handle = await createApp({
|
|
104
|
-
modules
|
|
90
|
+
modules,
|
|
105
91
|
bootstrapComponents: {
|
|
106
|
-
config,
|
|
92
|
+
config: resolveForBoot(own, modules, switches),
|
|
107
93
|
pathResolver: import.meta.resolve,
|
|
108
94
|
logger,
|
|
109
95
|
},
|
|
110
96
|
});
|
|
97
|
+
// The `http` module's settings: core's slot and the host's own.
|
|
98
|
+
const { httpSettings, httpHostSettings } = handle.components;
|
|
99
|
+
if (httpSettings === undefined || httpHostSettings === undefined) {
|
|
100
|
+
throw new Error("createApp booted without the http module's settings");
|
|
101
|
+
}
|
|
102
|
+
// `false` | `true` | a hop count | a list of IPs / CIDR ranges / named
|
|
103
|
+
// ranges: the shapes `trust proxy` understands, validated at boot so a
|
|
104
|
+
// typo'd range fails there rather than silently never matching. Prefer
|
|
105
|
+
// naming the proxy: `true` believes a forwarded client address from anyone
|
|
106
|
+
// who can reach this process, and every IP-keyed rate limit buckets on
|
|
107
|
+
// `req.ip`.
|
|
108
|
+
app.set("trust proxy", httpSettings.trustProxy);
|
|
111
109
|
|
|
112
|
-
// Step 4: the host
|
|
113
|
-
// auth router
|
|
114
|
-
//
|
|
115
|
-
// then the composed auth router, then the terminal error handler. See
|
|
116
|
-
// `routes.mts` for what each is and why the order is what it is.
|
|
110
|
+
// Step 4: the host routes (liveness, readiness, metrics), then the composed
|
|
111
|
+
// auth router, then the terminal error handler; `routes.mts` says why in
|
|
112
|
+
// that order.
|
|
117
113
|
mountRoutes(app, {
|
|
118
114
|
router: handle.router,
|
|
119
115
|
probes: handle.readinessProbes,
|
|
120
|
-
readinessTimeoutMs:
|
|
116
|
+
readinessTimeoutMs: httpHostSettings.readinessTimeoutMs,
|
|
121
117
|
metrics,
|
|
122
118
|
logger,
|
|
123
119
|
});
|
|
124
|
-
// Step 5: start the HTTP server.
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
const server = await listen(app, config.http.port, logger);
|
|
120
|
+
// Step 5: start the HTTP server. A port that cannot be bound fails boot
|
|
121
|
+
// with that error (`listen.mts`).
|
|
122
|
+
const server = await listen(app, httpHostSettings.port, logger);
|
|
128
123
|
|
|
129
|
-
// Step 6:
|
|
130
|
-
//
|
|
131
|
-
// (
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
// from the code you deploy. See `shutdown.mts` for the stated guarantees
|
|
136
|
-
// and for the deadline the previous implementation did not have. Size
|
|
137
|
-
// `drainTimeoutMs` below your orchestrator's kill grace period.
|
|
138
|
-
//
|
|
139
|
-
// #593 slice 7: with federation grants on, cleanup gets the longest refresh
|
|
140
|
-
// tail the configured budgets allow plus a margin — 45 s under the shipped
|
|
141
|
-
// ones — rather than inheriting the drain's ten: the dispose is what waits
|
|
142
|
-
// for a rotated credential's write. The compose files size
|
|
143
|
-
// `stop_grace_period` to cover the drain, this and an exit margin for the
|
|
144
|
-
// shipped budgets; a raised budget raises both.
|
|
124
|
+
// Step 6: graceful shutdown (`shutdown.mts`). Size `drainTimeoutMs` below
|
|
125
|
+
// your orchestrator's kill grace period. Cleanup gets at least the
|
|
126
|
+
// allowance the modules registered with their cleanups (45 s with
|
|
127
|
+
// federation grants on under the shipped budgets) rather than only the
|
|
128
|
+
// drain's ten; the template README, "Shutdown guarantees", sizes the grace
|
|
129
|
+
// period for it.
|
|
145
130
|
installGracefulShutdown(server, {
|
|
146
131
|
logger,
|
|
147
132
|
cleanup: () => handle.dispose(),
|
|
148
|
-
|
|
133
|
+
cleanupAllowanceMs: () => handle.cleanupAllowanceMs,
|
|
149
134
|
});
|
|
150
135
|
})();
|