@o3co/create-auth-provider 0.15.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 +139 -48
- package/README.md +138 -47
- 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 +69 -24
- package/templates/standalone/Dockerfile +7 -3
- package/templates/standalone/README.ja.md +677 -184
- package/templates/standalone/README.md +699 -238
- package/templates/standalone/config/application.conf +203 -325
- 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 +4 -2
- package/templates/standalone/package.json +9 -6
- package/templates/standalone/src/__tests__/access-token-lifetime-alias.test.mts +65 -32
- package/templates/standalone/src/__tests__/adapters.test.mts +358 -0
- package/templates/standalone/src/__tests__/all-modules-composition.fixture.mts +1178 -0
- package/templates/standalone/src/__tests__/all-modules-composition.multi.test.mts +256 -0
- package/templates/standalone/src/__tests__/all-modules-composition.test.mts +1119 -0
- 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 +672 -271
- package/templates/standalone/src/__tests__/federation-grants-composition.test.mts +480 -78
- package/templates/standalone/src/__tests__/federation-grants-survive-logout.test.mts +102 -66
- package/templates/standalone/src/__tests__/federation-token-refresh-outage.test.mts +135 -0
- package/templates/standalone/src/__tests__/google-federation-config-module.test.mts +49 -18
- package/templates/standalone/src/__tests__/google-session.fixture.mts +315 -0
- package/templates/standalone/src/__tests__/host-routes.test.mts +134 -0
- package/templates/standalone/src/__tests__/library-references.fixture.mts +183 -0
- package/templates/standalone/src/__tests__/listen.test.mts +121 -0
- package/templates/standalone/src/__tests__/logger-projection.test.mts +77 -0
- 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 +17 -7
- 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 +183 -0
- package/templates/standalone/src/__tests__/redis-connection-error-log.test.mts +135 -0
- 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 +166 -145
- package/templates/standalone/src/__tests__/router-error-answers.test.mts +146 -0
- 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 +319 -115
- package/templates/standalone/src/__tests__/smoke.test.mts +179 -233
- package/templates/standalone/src/__tests__/store-credential.test.mts +317 -0
- 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 +71 -115
- package/templates/standalone/src/buildModules.mts +229 -251
- package/templates/standalone/src/configPath.mts +422 -29
- package/templates/standalone/src/listen.mts +60 -0
- package/templates/standalone/src/logger.mts +31 -57
- package/templates/standalone/src/metrics.mts +24 -47
- package/templates/standalone/src/mfaSwitch.mts +262 -0
- package/templates/standalone/src/modules.mts +480 -464
- package/templates/standalone/src/rootRenames.mts +79 -0
- package/templates/standalone/src/routes.mts +89 -0
- package/templates/standalone/src/sections.mts +366 -0
- package/templates/standalone/src/shutdown.mts +54 -126
- 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
- package/templates/standalone/src/__tests__/terminal-error.test.mts +0 -96
- package/templates/standalone/src/terminalError.mts +0 -60
|
@@ -14,61 +14,50 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
17
|
-
import {
|
|
18
|
-
type AppConfig,
|
|
19
|
-
AppConfigSchema,
|
|
20
|
-
createApp,
|
|
21
|
-
createHealthcheckRouter,
|
|
22
|
-
createReadinessRouter,
|
|
23
|
-
} from "@o3co/auth-provider-core";
|
|
24
|
-
import { parseFile } from "@o3co/ts.hocon";
|
|
25
|
-
import { validate } from "@o3co/ts.hocon/zod";
|
|
17
|
+
import { createApp } from "@o3co/auth-provider-core";
|
|
26
18
|
import express from "express";
|
|
27
19
|
import helmet from "helmet";
|
|
28
20
|
import { buildModules } from "./buildModules.mjs";
|
|
29
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
readLogging,
|
|
23
|
+
readOwnLayers,
|
|
24
|
+
readSwitches,
|
|
25
|
+
resolveConfigPaths,
|
|
26
|
+
resolveForBoot,
|
|
27
|
+
type Switches,
|
|
28
|
+
} from "./configPath.mjs";
|
|
29
|
+
import { listen } from "./listen.mjs";
|
|
30
30
|
import { createAppLogger } from "./logger.mjs";
|
|
31
31
|
import { createMetrics } from "./metrics.mjs";
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
32
|
+
import { mountRoutes } from "./routes.mjs";
|
|
33
|
+
import { installGracefulShutdown } from "./shutdown.mjs";
|
|
34
34
|
|
|
35
|
-
// Step 1:
|
|
35
|
+
// Step 1: the configuration, in two phases (`configPath.mts`; template
|
|
36
|
+
// README, "Environment-specific config overlay").
|
|
36
37
|
// ENV = CONFIG_ENV || NODE_ENV || "development"; missing {ENV}.conf is fatal.
|
|
37
|
-
//
|
|
38
|
-
// 3-tier HOCON precedence (highest → lowest):
|
|
39
|
-
// 1. {env}.conf — environment-specific overrides (e.g. production.conf)
|
|
40
|
-
// 2. application.conf — template-level consumer delta
|
|
41
|
-
// 3. reference.conf — library defaults shipped in @o3co/auth-provider-core
|
|
42
38
|
const env = process.env.CONFIG_ENV || process.env.NODE_ENV || "development";
|
|
43
39
|
const configDir = new URL("../config/", import.meta.url);
|
|
44
40
|
const configDirPath = fileURLToPath(configDir);
|
|
45
41
|
const { applicationConfPath, envConfPath } = resolveConfigPaths(configDirPath, env);
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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);
|
|
53
50
|
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
// nothing about what the modules emit.
|
|
59
|
-
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));
|
|
60
55
|
|
|
61
56
|
await (async (): Promise<void> => {
|
|
62
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.
|
|
63
60
|
const app = express();
|
|
64
|
-
// `false` | `true` | a hop count | a list of IPs / CIDR ranges / named
|
|
65
|
-
// ranges — the schema hands Express exactly the shapes `trust proxy`
|
|
66
|
-
// already understands, validated at boot so a typo'd range fails here
|
|
67
|
-
// rather than becoming a rule that silently never matches (#292). Prefer
|
|
68
|
-
// naming the proxy: `true` believes a forwarded client address from anyone
|
|
69
|
-
// who can reach this process, and `req.ip` is what every IP-keyed rate
|
|
70
|
-
// limit is bucketed on.
|
|
71
|
-
app.set("trust proxy", config.http.trustProxy);
|
|
72
61
|
app.use(
|
|
73
62
|
helmet({
|
|
74
63
|
contentSecurityPolicy: {
|
|
@@ -86,94 +75,61 @@ await (async (): Promise<void> => {
|
|
|
86
75
|
const metrics = createMetrics();
|
|
87
76
|
app.use(metrics.middleware);
|
|
88
77
|
|
|
89
|
-
// Step 3:
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
// `BuilderContext.lifecycle` and drained on `handle.dispose()`.
|
|
96
|
-
// bootstrapComponents carries only host-
|
|
97
|
-
// environment values (config + pathResolver per A2-γ §4 worked example);
|
|
98
|
-
// every other component flows through composition-root-local modules.
|
|
99
|
-
// `buildModules` is the single source of truth for the module list — it
|
|
100
|
-
// gates federation modules on `config.federations.<name>.enabled`, which
|
|
101
|
-
// 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.
|
|
102
84
|
//
|
|
103
|
-
// `
|
|
104
|
-
//
|
|
105
|
-
//
|
|
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 });
|
|
106
89
|
const handle = await createApp({
|
|
107
|
-
modules
|
|
90
|
+
modules,
|
|
108
91
|
bootstrapComponents: {
|
|
109
|
-
config,
|
|
92
|
+
config: resolveForBoot(own, modules, switches),
|
|
110
93
|
pathResolver: import.meta.resolve,
|
|
111
94
|
logger,
|
|
112
95
|
},
|
|
113
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);
|
|
114
109
|
|
|
115
|
-
// Step 4:
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
// Readiness: can this replica serve right now? Redis backs sessions,
|
|
125
|
-
// authorization codes and refresh-token families in the deployable
|
|
126
|
-
// defaults, so a replica that has lost it answers 503 here and should be
|
|
127
|
-
// taken out of rotation. Probes are contributed by the builders that own
|
|
128
|
-
// each connection; a memory-only deployment registers none and is always
|
|
129
|
-
// ready.
|
|
130
|
-
app.use(
|
|
131
|
-
createReadinessRouter(express, {
|
|
132
|
-
probes: handle.readinessProbes,
|
|
133
|
-
timeoutMs: config.http.readinessTimeoutMs,
|
|
134
|
-
logger,
|
|
135
|
-
}),
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
// Prometheus scrape endpoint. Same reasoning as the probes above: it has to
|
|
139
|
-
// keep answering while the auth pipeline is degraded, because that is when
|
|
140
|
-
// the series matter.
|
|
141
|
-
app.use(
|
|
142
|
-
metrics.route(express, {
|
|
143
|
-
probes: handle.readinessProbes,
|
|
144
|
-
probeTimeoutMs: config.http.readinessTimeoutMs,
|
|
145
|
-
}),
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
// Step 5: Mount the composed auth router and start the HTTP server.
|
|
149
|
-
app.use(handle.router);
|
|
150
|
-
// Terminal error handler LAST (#293 item 8): Express routes an error only
|
|
151
|
-
// to handlers registered after the route that threw it. See
|
|
152
|
-
// `terminalError.mts` for what it catches and why.
|
|
153
|
-
app.use(createTerminalErrorHandler(logger));
|
|
154
|
-
const server = app.listen(config.http.port, (): void => {
|
|
155
|
-
logger.info(`Server is running on http://localhost:${config.http.port}`);
|
|
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.
|
|
113
|
+
mountRoutes(app, {
|
|
114
|
+
router: handle.router,
|
|
115
|
+
probes: handle.readinessProbes,
|
|
116
|
+
readinessTimeoutMs: httpHostSettings.readinessTimeoutMs,
|
|
117
|
+
metrics,
|
|
118
|
+
logger,
|
|
156
119
|
});
|
|
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);
|
|
157
123
|
|
|
158
|
-
// Step 6:
|
|
159
|
-
//
|
|
160
|
-
// (
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
// from the code you deploy. See `shutdown.mts` for the stated guarantees
|
|
165
|
-
// and for the deadline the previous implementation did not have. Size
|
|
166
|
-
// `drainTimeoutMs` below your orchestrator's kill grace period.
|
|
167
|
-
//
|
|
168
|
-
// #593 slice 7: with federation grants on, cleanup gets the longest refresh
|
|
169
|
-
// tail the configured budgets allow plus a margin — 45 s under the shipped
|
|
170
|
-
// ones — rather than inheriting the drain's ten: the dispose is what waits
|
|
171
|
-
// for a rotated credential's write. The compose files size
|
|
172
|
-
// `stop_grace_period` to cover the drain, this and an exit margin for the
|
|
173
|
-
// 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.
|
|
174
130
|
installGracefulShutdown(server, {
|
|
175
131
|
logger,
|
|
176
132
|
cleanup: () => handle.dispose(),
|
|
177
|
-
|
|
133
|
+
cleanupAllowanceMs: () => handle.cleanupAllowanceMs,
|
|
178
134
|
});
|
|
179
135
|
})();
|