@o3co/create-auth-provider 0.16.0 → 0.17.0-rc.2

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.
Files changed (100) hide show
  1. package/README.ja.md +66 -21
  2. package/README.md +76 -31
  3. package/dist/index.d.mts +33 -10
  4. package/dist/index.d.mts.map +1 -1
  5. package/dist/index.mjs +118 -59
  6. package/dist/internal/template-filter.d.mts +7 -10
  7. package/dist/internal/template-filter.d.mts.map +1 -1
  8. package/dist/internal/template-filter.mjs +7 -10
  9. package/package.json +6 -4
  10. package/templates/standalone/.dockerignore +15 -2
  11. package/templates/standalone/.env.example +59 -29
  12. package/templates/standalone/Dockerfile +6 -2
  13. package/templates/standalone/README.ja.md +324 -169
  14. package/templates/standalone/README.md +618 -208
  15. package/templates/standalone/config/application.conf +211 -352
  16. package/templates/standalone/config/clients.yaml +2 -0
  17. package/templates/standalone/config/clients.yaml.example +4 -0
  18. package/templates/standalone/config/development.conf +13 -4
  19. package/templates/standalone/config/mailpit.conf +8 -0
  20. package/templates/standalone/config/production.conf +5 -6
  21. package/templates/standalone/config/reference.conf +418 -0
  22. package/templates/standalone/docker-compose.mailpit.yml +36 -0
  23. package/templates/standalone/docker-compose.production.yml +63 -32
  24. package/templates/standalone/docker-compose.yml +13 -3
  25. package/templates/standalone/gitignore +1 -0
  26. package/templates/standalone/package.json +9 -6
  27. package/templates/standalone/src/__tests__/adapters.test.mts +419 -0
  28. package/templates/standalone/src/__tests__/admission-lifecycle.test.mts +87 -0
  29. package/templates/standalone/src/__tests__/all-modules-composition.fixture.mts +287 -198
  30. package/templates/standalone/src/__tests__/all-modules-composition.multi.test.mts +71 -35
  31. package/templates/standalone/src/__tests__/all-modules-composition.test.mts +148 -94
  32. package/templates/standalone/src/__tests__/audit-sink.test.mts +84 -81
  33. package/templates/standalone/src/__tests__/config-notices.test.mts +154 -0
  34. package/templates/standalone/src/__tests__/configPath.test.mts +13 -11
  35. package/templates/standalone/src/__tests__/core-federations.test.mts +249 -0
  36. package/templates/standalone/src/__tests__/core-sections.test.mts +190 -0
  37. package/templates/standalone/src/__tests__/device-code-store-client-module.test.mts +10 -15
  38. package/templates/standalone/src/__tests__/documented-env-overrides.test.mts +970 -288
  39. package/templates/standalone/src/__tests__/federation-grants-composition.test.mts +421 -86
  40. package/templates/standalone/src/__tests__/federation-grants-survive-logout.test.mts +87 -72
  41. package/templates/standalone/src/__tests__/federation-token-refresh-outage.test.mts +16 -8
  42. package/templates/standalone/src/__tests__/federation-types.test.mts +239 -0
  43. package/templates/standalone/src/__tests__/google-session.fixture.mts +97 -93
  44. package/templates/standalone/src/__tests__/host-routes.test.mts +4 -11
  45. package/templates/standalone/src/__tests__/library-reference-resolution.smoke.test.mts +1 -1
  46. package/templates/standalone/src/__tests__/library-references.fixture.mts +188 -0
  47. package/templates/standalone/src/__tests__/lifecycle-pending-close.test.mts +216 -0
  48. package/templates/standalone/src/__tests__/listen.test.mts +6 -13
  49. package/templates/standalone/src/__tests__/logger-projection.test.mts +1 -1
  50. package/templates/standalone/src/__tests__/mail-sender.test.mts +134 -0
  51. package/templates/standalone/src/__tests__/mailpit-compose.test.mts +326 -0
  52. package/templates/standalone/src/__tests__/metrics.test.mts +4 -9
  53. package/templates/standalone/src/__tests__/mfa-routes-budget.test.mts +122 -0
  54. package/templates/standalone/src/__tests__/mfa-switch.redis.test.mts +123 -0
  55. package/templates/standalone/src/__tests__/mfa-switch.test.mts +825 -0
  56. package/templates/standalone/src/__tests__/own-modules.test.mts +1486 -0
  57. package/templates/standalone/src/__tests__/post-logout-redirect.test.mts +10 -14
  58. package/templates/standalone/src/__tests__/redis-connection-error-log.test.mts +1 -5
  59. package/templates/standalone/src/__tests__/redis-rate-limiter-config-survives.test.mts +116 -81
  60. package/templates/standalone/src/__tests__/refresh-token-family-client-module.test.mts +25 -31
  61. package/templates/standalone/src/__tests__/replica-safety.test.mts +265 -146
  62. package/templates/standalone/src/__tests__/router-error-answers.test.mts +4 -7
  63. package/templates/standalone/src/__tests__/scaffold-assets.test.mts +278 -103
  64. package/templates/standalone/src/__tests__/sections.test.mts +480 -0
  65. package/templates/standalone/src/__tests__/session-lifecycle.test.mts +215 -0
  66. package/templates/standalone/src/__tests__/session-logout-invalidates-token.test.mts +69 -61
  67. package/templates/standalone/src/__tests__/session-logout-lifecycle.test.mts +130 -0
  68. package/templates/standalone/src/__tests__/session-requirements.test.mts +382 -0
  69. package/templates/standalone/src/__tests__/shipped-config-notices.test.mts +270 -0
  70. package/templates/standalone/src/__tests__/shutdown.test.mts +163 -96
  71. package/templates/standalone/src/__tests__/smoke.test.mts +176 -258
  72. package/templates/standalone/src/__tests__/store-credential.test.mts +88 -52
  73. package/templates/standalone/src/__tests__/supertest-loopback.test.mts +182 -11
  74. package/templates/standalone/src/__tests__/three-tier-resolution.test.mts +131 -102
  75. package/templates/standalone/src/__tests__/two-phase-config.test.mts +604 -0
  76. package/templates/standalone/src/__tests__/upstream-amr.acceptance.test.mts +382 -0
  77. package/templates/standalone/src/adapters.mts +162 -0
  78. package/templates/standalone/src/app.mts +65 -79
  79. package/templates/standalone/src/bootRefusal.mts +117 -0
  80. package/templates/standalone/src/buildModules.mts +220 -309
  81. package/templates/standalone/src/configPath.mts +449 -29
  82. package/templates/standalone/src/listen.mts +12 -18
  83. package/templates/standalone/src/logger.mts +31 -60
  84. package/templates/standalone/src/metrics.mts +24 -47
  85. package/templates/standalone/src/mfaSwitch.mts +285 -0
  86. package/templates/standalone/src/modules.mts +423 -629
  87. package/templates/standalone/src/rootRenames.mts +96 -0
  88. package/templates/standalone/src/routes.mts +8 -14
  89. package/templates/standalone/src/sections.mts +367 -0
  90. package/templates/standalone/src/shutdown.mts +45 -123
  91. package/templates/standalone/tests/index.test.js +6 -5
  92. package/templates/standalone/tests/package-lock.json +1615 -2223
  93. package/templates/standalone/tests/package.json +2 -2
  94. package/templates/standalone/tests/vitest.config.mjs +26 -0
  95. package/templates/standalone/vitest.config.mts +8 -7
  96. package/templates/standalone/vitest.supertest-loopback.mts +97 -69
  97. package/templates/versions.json +18 -15
  98. package/templates/standalone/src/__tests__/access-token-lifetime-alias.test.mts +0 -135
  99. package/templates/standalone/src/__tests__/google-federation-config-module.test.mts +0 -199
  100. package/templates/standalone/src/__tests__/oidc-federation-config-module.test.mts +0 -138
@@ -16,32 +16,31 @@
16
16
 
17
17
  /**
18
18
  * The all-modules composition of `all-modules-composition.test.mts` on more
19
- * than one replica: every shared store on Redis, `deployment.mode = "multi"`,
19
+ * than one replica: every shared store on Redis, `core.deployment.mode = "multi"`,
20
20
  * every module the template can turn on switched on together.
21
21
  *
22
- * What it holds: the replica-safety declarations hold with every module on —
23
- * the all-Redis composition boots with nothing declaring replica-unsafe state,
24
- * and each store switched back to memory is refused at boot, naming the
25
- * module — and the Redis adapters change nothing a client can see: the same
26
- * routes, the same discovery document as on one replica.
22
+ * With every module on, the all-Redis composition boots with nothing declaring
23
+ * replica-unsafe state, and each store switched back to memory is refused at
24
+ * boot, naming the module. The Redis adapters change nothing a client can
25
+ * see: the same routes, the same discovery document as on one replica.
27
26
  *
28
27
  * ioredis, node-redis and connect-redis are stand-ins, as in
29
- * `replica-safety.test.mts`: what is under test is the composition and the
30
- * boot's verdict, and nothing here issues a command. The template has no
31
- * Redis server to run against — `packages/redis` runs its adapters against
32
- * one.
28
+ * `replica-safety.test.mts`: nothing here issues a command. `packages/redis`
29
+ * runs its adapters against a real server.
33
30
  */
34
31
 
35
32
  import { type Module, replicaUnsafeReason } from "@o3co/auth-provider-core";
36
33
  import * as redisPackage from "@o3co/auth-provider-redis";
37
34
  import request from "supertest";
38
35
  import { afterEach, describe, expect, it, vi } from "vitest";
36
+ import { readOwnLayers, resolveForBoot } from "#/configPath.mjs";
39
37
  import { standaloneRedisClientsModule } from "#/modules.mjs";
40
38
  import {
41
39
  type Composition,
42
40
  compose,
43
41
  composedModules,
44
42
  MULTI_ENV,
43
+ ownFiles,
45
44
  resolveConfig,
46
45
  } from "./all-modules-composition.fixture.mjs";
47
46
 
@@ -110,7 +109,7 @@ const MULTI = { env: MULTI_ENV, shippedRefreshTokenFamilyStore: true } as const;
110
109
 
111
110
  /** What `buildModules` lists with every switch on and every shared store on Redis. */
112
111
  const ALL_ON_REDIS_MODULES = [
113
- "sessionStoreModule",
112
+ "session-store",
114
113
  "federation-grant-background",
115
114
  "federation-grants",
116
115
  "oauth",
@@ -118,19 +117,22 @@ const ALL_ON_REDIS_MODULES = [
118
117
  "oauth-authorization",
119
118
  "jwks",
120
119
  "session",
121
- "federation:google",
122
- "standalone:google-federation-config",
123
- "standalone:oidc-federation-config",
124
- "federation:oidc:oidc",
125
- "standalone:key-store",
120
+ "federation-google-type",
121
+ "federation-oidc",
122
+ "logging",
123
+ "http",
124
+ "key-store",
126
125
  "test:repositories",
127
- "standalone:audit-sink",
128
- "standalone:redis-clients",
126
+ "audit-sink",
127
+ "standard-smtp-mail-sender",
128
+ "redis-clients",
129
129
  "redis-federation-token-store",
130
130
  "redis-federation-grant-store",
131
131
  "redis-federation-grant-intent-store",
132
- "redisSessionStores",
132
+ "redis-session-stores",
133
+ "core-session-lifecycle",
133
134
  "redis-rate-limiter",
135
+ "redis-attempt-counter",
134
136
  "redis-code-repository",
135
137
  "redis-access-token-denylist",
136
138
  "redis-replay-seen-set",
@@ -141,11 +143,18 @@ const ALL_ON_REDIS_MODULES = [
141
143
  "subject-revocation-service",
142
144
  ];
143
145
 
144
- describe('every module on, every shared store on Redis, deployment.mode = "multi"', () => {
146
+ describe('every module on, every shared store on Redis, core.deployment.mode = "multi"', () => {
145
147
  it("lists every module, none declaring replica-unsafe state, and boots", async () => {
146
- const modules = composedModules(resolveConfig(MULTI_ENV), MULTI);
148
+ const own = readOwnLayers(ownFiles(MULTI_ENV), { env: MULTI_ENV });
149
+ const config = resolveConfig(MULTI_ENV, own);
150
+ const modules = composedModules(config, MULTI);
147
151
  expect(modules.map((m) => m.name)).toEqual(ALL_ON_REDIS_MODULES);
148
- for (const module of modules) expect(replicaUnsafeReason(module), module.name).toBeUndefined();
152
+ // Each module's section at its name, as boot is handed it: a
153
+ // declaration made from the section is answered for it.
154
+ const sections = resolveForBoot(own, modules, config) as unknown as Record<string, unknown>;
155
+ for (const module of modules) {
156
+ expect(replicaUnsafeReason(module, sections[module.name]), module.name).toBeUndefined();
157
+ }
149
158
  current = await compose(MULTI);
150
159
  expect(current.handle.readinessProbes.map((p) => p.name)).toContain("redis");
151
160
  });
@@ -166,21 +175,36 @@ describe('every module on, every shared store on Redis, deployment.mode = "multi
166
175
  expect(discovery.body).toEqual(single.discovery);
167
176
  });
168
177
 
178
+ it("boots as multi on CORE_DEPLOYMENT_MODE=multi alone", async () => {
179
+ expect(MULTI_ENV).toMatchObject({ CORE_DEPLOYMENT_MODE: "multi" });
180
+ expect(MULTI_ENV).not.toHaveProperty("DEPLOYMENT_MODE");
181
+ current = await compose(MULTI);
182
+ expect(current.handle.components.deploymentMode).toBe("multi");
183
+ });
184
+
185
+ it("boots as multi on DEPLOYMENT_MODE=multi beside CORE_DEPLOYMENT_MODE=multi, as the umbrella sets them", async () => {
186
+ current = await compose({
187
+ ...MULTI,
188
+ env: { ...MULTI_ENV, DEPLOYMENT_MODE: "multi", CORE_DEPLOYMENT_MODE: "multi" },
189
+ });
190
+ expect(current.handle.components.deploymentMode).toBe("multi");
191
+ });
192
+
169
193
  /**
170
194
  * Each store's switch back to memory, alone, with every module on. The
171
195
  * boot names the module that declared the state it would fork.
172
196
  */
173
197
  const MEMORY_SWITCHES: ReadonlyArray<readonly [variable: string, module: string]> = [
174
- ["SESSION_STORAGE_TYPE", "sessionStoreModule"],
175
- ["USER_SESSION_STORES_ADAPTER", "standalone:in-memory-session-stores"],
176
- ["OAUTH_CODE_ADAPTER", "standalone:in-memory-code-repository"],
177
- ["FEDERATION_TOKEN_STORE_TYPE", "standalone:in-memory-federation-token-store"],
178
- ["RATE_LIMITER_ADAPTER", "core-rate-limiter-memory"],
179
- ["ACCESS_TOKEN_DENYLIST_ADAPTER", "core-access-token-denylist-memory"],
180
- ["REPLAY_SEEN_SET_ADAPTER", "core-replay-seen-set-memory"],
181
- ["CONSENT_STORE_ADAPTER", "core-consent-store-memory"],
182
- ["FEDERATION_GRANT_STORE_ADAPTER", "core-federation-grant-store-memory"],
183
- ["FEDERATION_GRANT_INTENT_STORE_ADAPTER", "core-federation-grant-intent-store-memory"],
198
+ ["SESSION_STORE_STORAGE_TYPE", "session-store"],
199
+ ["ADAPTERS_USER_SESSION_STORES", "standalone-in-memory-session-stores"],
200
+ ["ADAPTERS_CODE_REPOSITORY", "standalone-in-memory-code-repository"],
201
+ ["ADAPTERS_FEDERATION_TOKEN_STORE", "standalone-in-memory-federation-token-store"],
202
+ ["ADAPTERS_RATE_LIMITER", "core-rate-limiter-memory"],
203
+ ["ADAPTERS_ACCESS_TOKEN_DENYLIST", "core-access-token-denylist-memory"],
204
+ ["ADAPTERS_REPLAY_SEEN_SET", "core-replay-seen-set-memory"],
205
+ ["ADAPTERS_CONSENT_STORE", "core-consent-store-memory"],
206
+ ["ADAPTERS_FEDERATION_GRANT_STORE", "core-federation-grant-store-memory"],
207
+ ["ADAPTERS_FEDERATION_GRANT_INTENT_STORE", "core-federation-grant-intent-store-memory"],
184
208
  ];
185
209
 
186
210
  it.each(MEMORY_SWITCHES)("%s=memory is refused at boot, naming %s", async (variable, module) => {
@@ -193,6 +217,18 @@ describe('every module on, every shared store on Redis, deployment.mode = "multi
193
217
  });
194
218
  });
195
219
 
220
+ it("ADAPTERS_ATTEMPT_COUNTER=memory is refused at boot: the login would count its attempts per replica", async () => {
221
+ await expect(
222
+ compose({ ...MULTI, env: { ...MULTI_ENV, ADAPTERS_ATTEMPT_COUNTER: "memory" } }),
223
+ ).rejects.toMatchObject({
224
+ name: "BootError",
225
+ reason: "contribute-factory-failed",
226
+ cause: {
227
+ message: expect.stringContaining('no shared attemptCounter is wired for "login"'),
228
+ },
229
+ });
230
+ });
231
+
196
232
  it("names every memory store together when every switch is memory", async () => {
197
233
  const env = { ...MULTI_ENV };
198
234
  for (const [variable] of MEMORY_SWITCHES) env[variable] = "memory";
@@ -212,8 +248,8 @@ describe("the shared Redis socket can back every store the Redis package ships",
212
248
  * the others to the manifest with the modules that read them (the device
213
249
  * grant's code store, WebAuthn's challenge store), and each requires its
214
250
  * client from this module. One it does not provide is a boot refused with
215
- * `missing-required-component` — the #439 shape, which `modules.mts`
216
- * guards against for the device-code store by name.
251
+ * `missing-required-component`, which `modules.mts` guards against for
252
+ * the device-code store by name.
217
253
  */
218
254
  const storeModules: Module[] = Object.entries(redisPackage).flatMap(([name, value]) => {
219
255
  if (name.endsWith("ModuleFor") && typeof value === "function") {
@@ -226,7 +262,7 @@ describe("the shared Redis socket can back every store the Redis package ships",
226
262
  });
227
263
 
228
264
  it.each(storeModules.map((m) => [m.name, m] as const))(
229
- "%s: its client slot is provided by standalone:redis-clients",
265
+ "%s: its client slot is provided by redis-clients",
230
266
  (_name, module) => {
231
267
  const provided = Object.keys(standaloneRedisClientsModule.provides ?? {});
232
268
  const clients = (module.requires ?? []).filter((slot) => String(slot).endsWith("Client"));