@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
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
import type { LifecycleRegistrar } from "@o3co/auth-provider-core";
|
|
17
17
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
18
18
|
|
|
19
|
-
//
|
|
19
|
+
// ioredis Redis constructor capture. The module under test imports
|
|
20
20
|
// `Redis` from "ioredis"; vi.mock replaces that import so the test never
|
|
21
21
|
// opens a real socket. Captured arguments verify that the validated config
|
|
22
|
-
// reaches the constructor
|
|
22
|
+
// reaches the constructor.
|
|
23
23
|
const redisCtorCalls: Array<{ url: string; options: Record<string, unknown> | undefined }> = [];
|
|
24
24
|
const quitSpies: Array<ReturnType<typeof vi.fn>> = [];
|
|
25
25
|
const onSpies: Array<ReturnType<typeof vi.fn>> = [];
|
|
@@ -60,17 +60,10 @@ const importModule = async () =>
|
|
|
60
60
|
standaloneRedisClientsModule: import("@o3co/auth-provider-core").Module;
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// exist — TS error → RED.
|
|
63
|
+
/** The module's own section, `redis-clients`, as boot hands it over. */
|
|
64
|
+
const baseSection = { url: "redis://example.com:6379", password: "test-pw" };
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
refreshTokenFamilyStore: {
|
|
69
|
-
redis: { url: "redis://example.com:6379", password: "test-pw" },
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
describe("D-2 / standaloneRedisClientsModule", () => {
|
|
66
|
+
describe("standaloneRedisClientsModule", () => {
|
|
74
67
|
beforeEach(() => {
|
|
75
68
|
redisCtorCalls.length = 0;
|
|
76
69
|
quitSpies.length = 0;
|
|
@@ -81,14 +74,14 @@ describe("D-2 / standaloneRedisClientsModule", () => {
|
|
|
81
74
|
vi.restoreAllMocks();
|
|
82
75
|
});
|
|
83
76
|
|
|
84
|
-
it("passes operator
|
|
77
|
+
it("passes the operator's Redis URL and password from its section to the ioredis constructor", async () => {
|
|
85
78
|
const { standaloneRedisClientsModule } = await importModule();
|
|
86
79
|
const provides = (
|
|
87
80
|
standaloneRedisClientsModule as unknown as {
|
|
88
81
|
provides: Record<string, (deps: Record<string, unknown>) => Promise<unknown>>;
|
|
89
82
|
}
|
|
90
83
|
).provides;
|
|
91
|
-
await provides.refreshTokenFamilyClient({
|
|
84
|
+
await provides.refreshTokenFamilyClient({ section: { ...baseSection } });
|
|
92
85
|
|
|
93
86
|
expect(redisCtorCalls).toHaveLength(1);
|
|
94
87
|
expect(redisCtorCalls[0]?.url).toBe("redis://example.com:6379");
|
|
@@ -109,7 +102,7 @@ describe("D-2 / standaloneRedisClientsModule", () => {
|
|
|
109
102
|
provides: Record<string, (deps: Record<string, unknown>) => Promise<unknown>>;
|
|
110
103
|
}
|
|
111
104
|
).provides;
|
|
112
|
-
await provides.refreshTokenFamilyClient({
|
|
105
|
+
await provides.refreshTokenFamilyClient({ section: { ...baseSection }, lifecycleRegistrar });
|
|
113
106
|
|
|
114
107
|
expect(registered).toHaveLength(1);
|
|
115
108
|
// Pre-drain: quit not yet called.
|
|
@@ -133,12 +126,12 @@ describe("D-2 / standaloneRedisClientsModule", () => {
|
|
|
133
126
|
// after the factory resolves; the registrar branch is the only place
|
|
134
127
|
// `quit()` would be wired up in the no-real-shutdown unit-test path).
|
|
135
128
|
await expect(
|
|
136
|
-
provides.refreshTokenFamilyClient({
|
|
129
|
+
provides.refreshTokenFamilyClient({ section: { ...baseSection } }),
|
|
137
130
|
).resolves.toBeDefined();
|
|
138
131
|
expect(quitSpies[0]).not.toHaveBeenCalled();
|
|
139
132
|
});
|
|
140
133
|
|
|
141
|
-
it("fails fast
|
|
134
|
+
it("fails fast on an empty redis-clients.url (no silent localhost fallback)", async () => {
|
|
142
135
|
const { standaloneRedisClientsModule } = await importModule();
|
|
143
136
|
const provides = (
|
|
144
137
|
standaloneRedisClientsModule as unknown as {
|
|
@@ -146,11 +139,12 @@ describe("D-2 / standaloneRedisClientsModule", () => {
|
|
|
146
139
|
}
|
|
147
140
|
).provides;
|
|
148
141
|
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
142
|
+
// An operator who blanked the URL — must throw instead of silently
|
|
143
|
+
// falling back to redis://localhost:6379, which in production would
|
|
144
|
+
// leave each replica with refresh-token families the others cannot
|
|
145
|
+
// see. A section with no URL at all is refused by boot's parse of it.
|
|
146
|
+
await expect(provides.refreshTokenFamilyClient({ section: { url: "" } })).rejects.toThrow(
|
|
147
|
+
/redis-clients\.url/,
|
|
154
148
|
);
|
|
155
149
|
// And no ioredis instance was constructed because we threw before
|
|
156
150
|
// `new Redis(...)`.
|
|
@@ -164,12 +158,12 @@ describe("D-2 / standaloneRedisClientsModule", () => {
|
|
|
164
158
|
provides: Record<string, (deps: Record<string, unknown>) => Promise<unknown>>;
|
|
165
159
|
}
|
|
166
160
|
).provides;
|
|
167
|
-
await provides.refreshTokenFamilyClient({
|
|
161
|
+
await provides.refreshTokenFamilyClient({ section: { ...baseSection } });
|
|
168
162
|
|
|
169
163
|
expect(onSpies[0]).toHaveBeenCalledWith("error", expect.any(Function));
|
|
170
164
|
});
|
|
171
165
|
|
|
172
|
-
it("bounds every command and every connect attempt
|
|
166
|
+
it("bounds every command and every connect attempt", async () => {
|
|
173
167
|
// Without these the driver's defaults apply: no command timeout at all,
|
|
174
168
|
// a 10s connect timeout, and 20 reconnect attempts before a queued
|
|
175
169
|
// command is failed. A partition therefore parks in-flight `/token`
|
|
@@ -181,7 +175,7 @@ describe("D-2 / standaloneRedisClientsModule", () => {
|
|
|
181
175
|
provides: Record<string, (deps: Record<string, unknown>) => Promise<unknown>>;
|
|
182
176
|
}
|
|
183
177
|
).provides;
|
|
184
|
-
await provides.refreshTokenFamilyClient({
|
|
178
|
+
await provides.refreshTokenFamilyClient({ section: { ...baseSection } });
|
|
185
179
|
|
|
186
180
|
const options = redisCtorCalls[0]?.options;
|
|
187
181
|
expect(options?.commandTimeout).toBe(1_000);
|
|
@@ -193,7 +187,7 @@ describe("D-2 / standaloneRedisClientsModule", () => {
|
|
|
193
187
|
expect(options?.lazyConnect).toBe(false);
|
|
194
188
|
});
|
|
195
189
|
|
|
196
|
-
it("keeps the offline queue ON, explicitly, because one socket serves every purpose
|
|
190
|
+
it("keeps the offline queue ON, explicitly, because one socket serves every purpose", async () => {
|
|
197
191
|
// `enableOfflineQueue` is a per-CONNECTION option and every adapter in
|
|
198
192
|
// this template draws from the single socket built below, so the
|
|
199
193
|
// "off for the rate limiter, on elsewhere" split is not expressible
|
|
@@ -207,18 +201,18 @@ describe("D-2 / standaloneRedisClientsModule", () => {
|
|
|
207
201
|
provides: Record<string, (deps: Record<string, unknown>) => Promise<unknown>>;
|
|
208
202
|
}
|
|
209
203
|
).provides;
|
|
210
|
-
await provides.refreshTokenFamilyClient({
|
|
204
|
+
await provides.refreshTokenFamilyClient({ section: { ...baseSection } });
|
|
211
205
|
|
|
212
206
|
expect(redisCtorCalls[0]?.options?.enableOfflineQueue).toBe(true);
|
|
213
207
|
});
|
|
214
208
|
|
|
215
|
-
it("
|
|
209
|
+
it("requires no configuration, and takes both registrars as optional", async () => {
|
|
216
210
|
const { standaloneRedisClientsModule } = await importModule();
|
|
217
211
|
const m = standaloneRedisClientsModule as {
|
|
218
212
|
requires?: readonly string[];
|
|
219
213
|
optional?: readonly string[];
|
|
220
214
|
};
|
|
221
|
-
expect(m.requires).toContain("config");
|
|
215
|
+
expect(m.requires ?? []).not.toContain("config");
|
|
222
216
|
expect(m.optional).toContain("lifecycleRegistrar");
|
|
223
217
|
expect(m.optional).toContain("readinessRegistrar");
|
|
224
218
|
});
|
|
@@ -238,12 +232,12 @@ describe("D-2 / standaloneRedisClientsModule", () => {
|
|
|
238
232
|
const lifecycleRegistrar: LifecycleRegistrar = { register: () => {} };
|
|
239
233
|
|
|
240
234
|
await provides.refreshTokenFamilyClient({
|
|
241
|
-
|
|
235
|
+
section: { ...baseSection },
|
|
242
236
|
lifecycleRegistrar,
|
|
243
237
|
readinessRegistrar,
|
|
244
238
|
});
|
|
245
239
|
await provides.rateLimiterClient({
|
|
246
|
-
|
|
240
|
+
section: { ...baseSection },
|
|
247
241
|
lifecycleRegistrar,
|
|
248
242
|
readinessRegistrar,
|
|
249
243
|
});
|