@o3co/create-auth-provider 0.17.0-rc.4 → 0.18.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.
Files changed (110) hide show
  1. package/README.md +2 -198
  2. package/main.js +4 -0
  3. package/package.json +8 -30
  4. package/README.ja.md +0 -200
  5. package/dist/cli.d.mts +0 -3
  6. package/dist/cli.d.mts.map +0 -1
  7. package/dist/cli.mjs +0 -18
  8. package/dist/index.d.mts +0 -48
  9. package/dist/index.d.mts.map +0 -1
  10. package/dist/index.mjs +0 -325
  11. package/dist/internal/template-filter.d.mts +0 -14
  12. package/dist/internal/template-filter.d.mts.map +0 -1
  13. package/dist/internal/template-filter.mjs +0 -42
  14. package/templates/standalone/.dockerignore +0 -23
  15. package/templates/standalone/.env.example +0 -152
  16. package/templates/standalone/Dockerfile +0 -125
  17. package/templates/standalone/Makefile +0 -12
  18. package/templates/standalone/README.ja.md +0 -913
  19. package/templates/standalone/README.md +0 -1537
  20. package/templates/standalone/config/application.conf +0 -351
  21. package/templates/standalone/config/clients.yaml +0 -4
  22. package/templates/standalone/config/clients.yaml.example +0 -118
  23. package/templates/standalone/config/development.conf +0 -17
  24. package/templates/standalone/config/mailpit.conf +0 -13
  25. package/templates/standalone/config/production.conf +0 -15
  26. package/templates/standalone/config/reference.conf +0 -424
  27. package/templates/standalone/docker-compose.mailpit.yml +0 -39
  28. package/templates/standalone/docker-compose.production.yml +0 -229
  29. package/templates/standalone/docker-compose.test.yml +0 -16
  30. package/templates/standalone/docker-compose.yml +0 -67
  31. package/templates/standalone/gitignore +0 -23
  32. package/templates/standalone/package.json +0 -55
  33. package/templates/standalone/src/__tests__/adapters.test.mts +0 -425
  34. package/templates/standalone/src/__tests__/admission-lifecycle.test.mts +0 -87
  35. package/templates/standalone/src/__tests__/all-modules-composition.fixture.mts +0 -1192
  36. package/templates/standalone/src/__tests__/all-modules-composition.multi.test.mts +0 -285
  37. package/templates/standalone/src/__tests__/all-modules-composition.test.mts +0 -1154
  38. package/templates/standalone/src/__tests__/audit-sink.test.mts +0 -346
  39. package/templates/standalone/src/__tests__/config-notices.test.mts +0 -157
  40. package/templates/standalone/src/__tests__/configPath.test.mts +0 -69
  41. package/templates/standalone/src/__tests__/configured-mfa-mode.fixture.mts +0 -48
  42. package/templates/standalone/src/__tests__/core-federations.test.mts +0 -252
  43. package/templates/standalone/src/__tests__/core-sections.test.mts +0 -192
  44. package/templates/standalone/src/__tests__/device-code-store-client-module.test.mts +0 -95
  45. package/templates/standalone/src/__tests__/documented-env-overrides.test.mts +0 -1290
  46. package/templates/standalone/src/__tests__/federation-grants-composition.test.mts +0 -784
  47. package/templates/standalone/src/__tests__/federation-grants-survive-logout.test.mts +0 -461
  48. package/templates/standalone/src/__tests__/federation-token-refresh-outage.test.mts +0 -141
  49. package/templates/standalone/src/__tests__/federation-types.test.mts +0 -239
  50. package/templates/standalone/src/__tests__/google-session.fixture.mts +0 -308
  51. package/templates/standalone/src/__tests__/host-routes.test.mts +0 -134
  52. package/templates/standalone/src/__tests__/library-reference-resolution.smoke.test.mts +0 -29
  53. package/templates/standalone/src/__tests__/library-references.fixture.mts +0 -188
  54. package/templates/standalone/src/__tests__/lifecycle-pending-close.test.mts +0 -216
  55. package/templates/standalone/src/__tests__/listen.test.mts +0 -121
  56. package/templates/standalone/src/__tests__/logger-projection.test.mts +0 -77
  57. package/templates/standalone/src/__tests__/logout-session-cookie.test.mts +0 -113
  58. package/templates/standalone/src/__tests__/mail-sender.test.mts +0 -134
  59. package/templates/standalone/src/__tests__/mailpit-compose.test.mts +0 -393
  60. package/templates/standalone/src/__tests__/metrics.test.mts +0 -253
  61. package/templates/standalone/src/__tests__/mfa-routes-budget.test.mts +0 -122
  62. package/templates/standalone/src/__tests__/mfa-switch.redis.test.mts +0 -127
  63. package/templates/standalone/src/__tests__/mfa-switch.test.mts +0 -883
  64. package/templates/standalone/src/__tests__/own-modules.test.mts +0 -1512
  65. package/templates/standalone/src/__tests__/post-logout-redirect.test.mts +0 -183
  66. package/templates/standalone/src/__tests__/redis-assume-noeviction.test.mts +0 -214
  67. package/templates/standalone/src/__tests__/redis-connection-error-log.test.mts +0 -135
  68. package/templates/standalone/src/__tests__/redis-rate-limiter-config-survives.test.mts +0 -214
  69. package/templates/standalone/src/__tests__/refresh-token-family-client-module.test.mts +0 -249
  70. package/templates/standalone/src/__tests__/replica-safety.test.mts +0 -654
  71. package/templates/standalone/src/__tests__/repositories-logger.test.mts +0 -119
  72. package/templates/standalone/src/__tests__/router-error-answers.test.mts +0 -146
  73. package/templates/standalone/src/__tests__/scaffold-assets.test.mts +0 -610
  74. package/templates/standalone/src/__tests__/sections.test.mts +0 -504
  75. package/templates/standalone/src/__tests__/session-lifecycle.test.mts +0 -215
  76. package/templates/standalone/src/__tests__/session-logout-invalidates-token.test.mts +0 -296
  77. package/templates/standalone/src/__tests__/session-logout-lifecycle.test.mts +0 -130
  78. package/templates/standalone/src/__tests__/session-requirements.test.mts +0 -382
  79. package/templates/standalone/src/__tests__/shipped-config-notices.test.mts +0 -274
  80. package/templates/standalone/src/__tests__/shutdown.test.mts +0 -622
  81. package/templates/standalone/src/__tests__/smoke.test.mts +0 -634
  82. package/templates/standalone/src/__tests__/startup-log-lines.test.mts +0 -148
  83. package/templates/standalone/src/__tests__/store-credential.test.mts +0 -312
  84. package/templates/standalone/src/__tests__/supertest-loopback.test.mts +0 -296
  85. package/templates/standalone/src/__tests__/three-tier-resolution.test.mts +0 -265
  86. package/templates/standalone/src/__tests__/two-phase-config.test.mts +0 -605
  87. package/templates/standalone/src/__tests__/upstream-amr.acceptance.test.mts +0 -382
  88. package/templates/standalone/src/adapters.mts +0 -162
  89. package/templates/standalone/src/app.mts +0 -136
  90. package/templates/standalone/src/bootRefusal.mts +0 -117
  91. package/templates/standalone/src/buildModules.mts +0 -358
  92. package/templates/standalone/src/configPath.mts +0 -487
  93. package/templates/standalone/src/listen.mts +0 -60
  94. package/templates/standalone/src/logger.mts +0 -80
  95. package/templates/standalone/src/metrics.mts +0 -167
  96. package/templates/standalone/src/mfaSwitch.mts +0 -284
  97. package/templates/standalone/src/modules.mts +0 -705
  98. package/templates/standalone/src/rootRenames.mts +0 -95
  99. package/templates/standalone/src/routes.mts +0 -89
  100. package/templates/standalone/src/sections.mts +0 -377
  101. package/templates/standalone/src/shutdown.mts +0 -213
  102. package/templates/standalone/tests/Makefile +0 -5
  103. package/templates/standalone/tests/index.test.js +0 -283
  104. package/templates/standalone/tests/package-lock.json +0 -1617
  105. package/templates/standalone/tests/package.json +0 -16
  106. package/templates/standalone/tests/vitest.config.mjs +0 -26
  107. package/templates/standalone/tsconfig.json +0 -31
  108. package/templates/standalone/vitest.config.mts +0 -31
  109. package/templates/standalone/vitest.supertest-loopback.mts +0 -278
  110. package/templates/versions.json +0 -20
@@ -1,351 +0,0 @@
1
- # Application-level overrides for the standalone template.
2
- #
3
- # Defaults come from config/reference.conf (the template's own modules) and
4
- # from the reference.conf of each package its modules come from, core's last.
5
- # Values omitted here inherit from them; add overrides only for keys this
6
- # deployment intentionally diverges on. Write a value for a key the last
7
- # block of this file binds above that block: one written below it wins over
8
- # the variable.
9
-
10
- # Which adapter fills each slot -- the stores, the repositories, the audit
11
- # sink -- is the template's own `adapters` section, whose defaults
12
- # config/reference.conf holds with each one's variable (ADAPTERS_*). Override
13
- # one in a layer of your own or with its variable:
14
- #
15
- # adapters.rateLimiter = "redis"
16
- #
17
- # The audit stream's level is FIXED at info and `logging.level` does not gate
18
- # it: LOGGING_LEVEL=warn is an ordinary production setting and
19
- # LOGGING_LEVEL=silent a legitimate one, and either silencing the audit trail
20
- # would lose the record of every security event.
21
-
22
- # The grant switches, each in the section of the module that installs the
23
- # grant. The template is a typical web-app OP, so it turns on the universal
24
- # user-auth path: the session grant (oauth-session) and the authorization-code
25
- # and refresh-token grants (oauth-authorization). client_credentials and the
26
- # jwt-bearer grant stay off; deployments that need M2M flows set
27
- # OAUTH_AUTHORIZATION_GRANTS_CLIENT_CREDENTIALS_ENABLED=true or write
28
- # `oauth-authorization.grants.clientCredentials.enabled = true` in their own
29
- # layer.
30
- #
31
- # The modules read these switches at boot, from the configuration boot parses;
32
- # the template reads none of them before. This file writes the template's
33
- # choices and binds each variable again: the env-override line is repeated
34
- # after each value because a layer's own value wins over a variable bound
35
- # below it.
36
- #
37
- # PKCE is mandatory for every authorization-code client and `S256` is the only
38
- # method the provider accepts -- fixed policy (OAuth 2.1 §4.1.1 / RFC 9700
39
- # §2.1.1), not a deployment setting. A legacy client that genuinely cannot
40
- # compute SHA-256 is opted into `plain` one client at a time, with
41
- # `allowPlainPkce: true` in clients.yaml.
42
- oauth-session {
43
- enabled = true
44
- enabled = ${?OAUTH_SESSION_ENABLED}
45
- }
46
-
47
- oauth-authorization.grants {
48
- authorizationCode {
49
- enabled = true
50
- enabled = ${?OAUTH_AUTHORIZATION_GRANTS_AUTHORIZATION_CODE_ENABLED}
51
- }
52
- refreshToken {
53
- enabled = true
54
- enabled = ${?OAUTH_AUTHORIZATION_GRANTS_REFRESH_TOKEN_ENABLED}
55
- }
56
- clientCredentials {
57
- enabled = false
58
- enabled = ${?OAUTH_AUTHORIZATION_GRANTS_CLIENT_CREDENTIALS_ENABLED}
59
- }
60
- jwtBearer {
61
- enabled = false
62
- enabled = ${?OAUTH_AUTHORIZATION_GRANTS_JWT_BEARER_ENABLED}
63
- }
64
- }
65
-
66
- oauth {
67
- # The Authentication Context Class References this deployment can
68
- # vouch for, each mapped to the RFC 8176 `amr` values a session must carry
69
- # (`pwd` for a password login, `fed` for a federated login) — or to a list
70
- # of such lists, any one of which satisfies it. `/authorize` answers
71
- # `acr_values` from this table alone — anything else is
72
- # `unmet_authentication_requirements` — and discovery advertises the keys.
73
- #
74
- # authorize.acrValues {
75
- # "urn:example:pwd" = ["pwd"]
76
- # }
77
- #
78
- # An entry nothing this deployment installs can satisfy is dropped at boot:
79
- # not advertised, answered `unmet_authentication_requirements`, and logged
80
- # once as `acr_value_unsatisfiable`. One that needs `fed` needs a
81
- # federation installed; one that needs `mfa`, `hwk` or `swk` needs MFA
82
- # installed (MFA_MODE, below), or a federation that trusts its upstream
83
- # IdP's `amr`. One that lacks only a second factor while MFA is off is
84
- # logged at info, not warn.
85
- #
86
- # While MFA_MODE installs MFA, the template adds `"urn:o3co:acr:mfa" =
87
- # ["mfa"]`, met by a session with a verified second factor, unless this
88
- # table writes that entry itself; with MFA off it adds nothing.
89
- # Phishing-resistant authentication would be:
90
- #
91
- # "urn:o3co:acr:phr" = [["hwk"], ["swk"]]
92
-
93
- # /authorize refuses any client not marked `firstParty: true` in
94
- # clients.yaml — the template's example clients are marked. There is no
95
- # opt-out: the one-time `allowUnmarkedClients` migration flag is removed,
96
- # and a config or environment still setting it fails at boot: the oauth
97
- # module refuses the key as removed, and an exported
98
- # OAUTH_AUTHORIZE_ALLOW_UNMARKED_CLIENTS as the variable of a removed key.
99
- # POST /oauth/revoke revokes access tokens by writing their `jti` to the
100
- # denylist wired below, and boot FAILS if that denylist is missing — an
101
- # RFC 7009 endpoint must answer 200, so an unwired denylist would tell every
102
- # operator their token is revoked while it kept working until expiry.
103
- #
104
- # Deployments that genuinely do not revoke access tokens (short-lived tokens
105
- # plus refresh-token revocation only) set "unsupported" instead, and the
106
- # endpoint then answers `unsupported_token_type` for
107
- # `token_type_hint = access_token`. Refresh-token revocation works in both
108
- # modes and needs no denylist.
109
- revocation {
110
- accessToken = "denylist"
111
- accessToken = ${?OAUTH_REVOCATION_ACCESS_TOKEN}
112
- }
113
- }
114
-
115
- # The session requirements this deployment expects, in core's own section:
116
- # the extensions that change what "logged in" means, each registered by a
117
- # module (the MFA package's is named `mfa`). Boot compares the list with what
118
- # the installed modules register: a name here that nothing registers refuses
119
- # the boot (`session-requirement-missing`), and so does a registered
120
- # requirement the list leaves out (`session-requirements-undeclared`). When
121
- # MFA_MODE (`mfaMode`, config/reference.conf) installs MFA, the template
122
- # adds `mfa` to this list and writes `secondFactorAuthority = "mfa"` beside
123
- # it (one written here other than `mfa` refuses the boot); a deployment that
124
- # adds a module registering a
125
- # requirement of its own adds its name here. No environment variable sets the
126
- # list.
127
- core.sessionRequirements.expected = []
128
-
129
- # Multi-factor authentication, installed by MFA_MODE (`mfaMode`,
130
- # config/reference.conf): "off" (unset) installs nothing of it; "optional" or
131
- # "required" installs the MFA package's modules (the TOTP, recovery-code and
132
- # email factors, the operator reset) and writes `mfa.mode` from the switch.
133
- # The MFA package's reference.conf holds the `mfa` section with its
134
- # variables: the key ring's first key, MFA_ENCRYPTION_KEY (`openssl rand
135
- # -base64 32`; none, and the boot is refused), and the page a step-up starts
136
- # on, MFA_PAGE_URL (`/mfa`). One Store call's time, MFA_STORE_TIMEOUT_MS
137
- # (5000 ms), must be at least every store's own per-call timeout, the user
138
- # directory's REPOSITORIES_USER_HTTP_TIMEOUT among them: raise the two
139
- # together; above 37500 ms the boot is refused. Mail --
140
- # the account-email proof, the email factor's codes -- goes through the mail
141
- # sender, which needs STANDARD_SMTP_MAIL_SENDER_HOST and
142
- # STANDARD_SMTP_MAIL_SENDER_FROM outside development. While MFA is off,
143
- # nothing written under `mfa` is read.
144
-
145
- # Federation grants — a user's standing consent that a backend or an
146
- # agent may obtain upstream IdP tokens with no session behind the call. Off by
147
- # default; FEDERATION_GRANTS_ENABLED=true installs the routes, the two stores
148
- # `adapters.federationGrantStore` and `.federationGrantIntentStore` select
149
- # (none by default, which boot refuses naming the store) and the
150
- # subject-revocation service. Enabling it is a statement that
151
- # the deployment has a consent page (FEDERATION_GRANTS_CONSENT_URL, no default),
152
- # a `callbackURL` on every connection, and a user repository that covers each
153
- # connection's registration (or FEDERATION_GRANTS_IDENTITY_LOOKUP=unsupported,
154
- # the recorded decision not to make that check) — each refused at boot when
155
- # missing. Connections and the Redis grant store's key ring have no
156
- # environment form; write them in a deployment-owned HOCON layer (README:
157
- # Federation Grants). The section's defaults and its other variables
158
- # (FEDERATION_GRANTS_IDENTITY_LOOKUP, FEDERATION_GRANTS_CONSENT_URL) are the
159
- # federation-grants package's reference.conf, and the Redis grant store's
160
- # (REDIS_FEDERATION_GRANT_STORE_ENCRYPTION_MODE, "required" by default) the
161
- # Redis package's: each is layered only where a module of its package is
162
- # installed. The
163
- # template reads `federation-grants.enabled` before it chooses its modules,
164
- # from its own files, which is why this file binds FEDERATION_GRANTS_ENABLED;
165
- # unset, it writes nothing, and the feature is off.
166
- federation-grants.enabled = ${?FEDERATION_GRANTS_ENABLED}
167
-
168
- # The session store's section: the session cookie and the store
169
- # express-session keeps sessions in. A memory store declares itself
170
- # replica-unsafe from this section.
171
- session-store {
172
- secret = ${?SESSION_STORE_SECRET}
173
- # A `__Host-` name requires Secure, Path=/ and no Domain attribute. If
174
- # SESSION_STORE_SECURE=false or SESSION_STORE_DOMAIN is set, also set
175
- # SESSION_STORE_NAME to a name without the prefix.
176
- name = "__Host-auth.session"
177
- name = ${?SESSION_STORE_NAME}
178
- maxAge = 3600000
179
- maxAge = ${?SESSION_STORE_MAX_AGE}
180
- secure = true
181
- secure = ${?SESSION_STORE_SECURE}
182
- sameSite = "lax"
183
- sameSite = ${?SESSION_STORE_SAME_SITE}
184
- domain = null
185
- domain = ${?SESSION_STORE_DOMAIN}
186
- storage {
187
- type = "redis"
188
- type = ${?SESSION_STORE_STORAGE_TYPE}
189
- redis {
190
- url = "redis://localhost:6379"
191
- url = ${?SESSION_STORE_STORAGE_REDIS_URL}
192
- password = ${?SESSION_STORE_STORAGE_REDIS_PASSWORD}
193
- }
194
- }
195
- }
196
-
197
- # The session module's section: the login routes' settings.
198
- session {
199
- # Every URL `POST /session/login` may accept as `redirect_to`, matched
200
- # EXACTLY -- scheme, host, port, path and query all significant. Empty (the
201
- # default) refuses every `redirect_to`, so leave it alone unless your login
202
- # page actually posts one.
203
- #
204
- # redirectAllowlist = ["https://app.example.com/welcome"]
205
- #
206
- # `http://` is accepted for loopback hosts only. With `session-store.domain`
207
- # set above, an entry outside it fails boot rather than being silently
208
- # ignored. The federation flow has a separate list per provider (see
209
- # `core.federations` below).
210
- redirectAllowlist = []
211
- # POST /session/login's own attempt limit (brute-force protection), in
212
- # windowMs (milliseconds), counted on the attempt counter
213
- # (`adapters.attemptCounter`); kept in this layer so operators can tweak
214
- # it. No rate limiter changes it. It does NOT govern OAuth endpoint rate
215
- # limiting: the `rateLimiter` component
216
- # (memoryRateLimiterModule or redisRateLimiterModule) reads its own section,
217
- # `core-rate-limiter-memory` / `redis-rate-limiter` (with `windowSeconds`
218
- # instead of `windowMs`). Switch via `adapters.rateLimiter = "memory" |
219
- # "redis"` (see reference.conf). The Redis limiter's outage policy is its
220
- # own key, `redis-rate-limiter.failMode` ("closed" by default;
221
- # REDIS_RATE_LIMITER_FAIL_MODE sets it).
222
- rateLimit {
223
- login { windowMs = 900000, limit = 20 }
224
- }
225
- }
226
-
227
- # The MFA routes' budget, 60 per 300 s, is config/reference.conf's, given to
228
- # both limiters so that switching `adapters.rateLimiter` keeps it. Change it
229
- # for the limiter this deployment wires, in its own section:
230
- #
231
- # redis-rate-limiter.limits.mfa { limit = 30, windowSeconds = 300 }
232
-
233
- # The federations, in core's own section, keyed by the name each is reached
234
- # at (/session/oauth/federation/<name>). A key of one is bound to the
235
- # variable named after its path, CORE_FEDERATIONS_<NAME>_<KEY>. Each entry
236
- # names its `type`, and boot hands an enabled entry to the module that handles
237
- # that type: the template loads the `google` and `oidc` types, so an entry of
238
- # either type under any name is a federation of its own. An entry without a
239
- # `type`, enabled or not, refuses the boot, and so does an enabled entry of a
240
- # type no loaded module handles.
241
- core.federations {
242
- # Template ships a Google federation declaration disabled-by-default.
243
- # Operators enable + populate credentials via env vars. A second Google
244
- # client is another entry with `type = "google"` and its own callbackURL.
245
- google {
246
- enabled = false
247
- enabled = ${?CORE_FEDERATIONS_GOOGLE_ENABLED}
248
- type = "google"
249
- clientId = ${?CORE_FEDERATIONS_GOOGLE_CLIENT_ID}
250
- clientSecret = ${?CORE_FEDERATIONS_GOOGLE_CLIENT_SECRET}
251
- callbackURL = "http://localhost:3000/session/oauth/federation/google/callback"
252
- callbackURL = ${?CORE_FEDERATIONS_GOOGLE_CALLBACK_URL}
253
-
254
- # Every URL a `redirect_to` query parameter may name on
255
- # /session/oauth/federation/google. Matching is EXACT — scheme, host, port,
256
- # path, query and fragment all count — so list each landing page in full.
257
- # There is no wildcard and no subdomain matching.
258
- #
259
- # An empty list refuses every `redirect_to`, which is the right setting for
260
- # a deployment that does not use the parameter. It is NOT a way to allow
261
- # everything: nothing falls back to accepting any http(s) URL, which would
262
- # make this an open redirect.
263
- #
264
- # https is required, except for a loopback host (localhost, 127.0.0.0/8,
265
- # [::1]) — that carve-out is what lets a local dev front-end and a native
266
- # client on an ephemeral loopback listener work without a certificate. The
267
- # port is still part of the match, so list the port the client binds.
268
- #
269
- # redirectAllowlist = [
270
- # "https://app.example.com/welcome"
271
- # "http://localhost:5173/welcome"
272
- # ]
273
- redirectAllowlist = []
274
-
275
- # Cookie / session domain. When set, every non-loopback redirectAllowlist
276
- # entry must be inside it, checked at boot — an entry outside it fails
277
- # startup rather than sitting here looking effective.
278
- # sessionDomain = ${?CORE_FEDERATIONS_GOOGLE_SESSION_DOMAIN}
279
-
280
- # Post-callback bridge page. Required only if `redirect_to` is used at all:
281
- # the browser lands here as `<authCallbackUrl>?redirect_to=<encoded>`.
282
- # authCallbackUrl = ${?CORE_FEDERATIONS_GOOGLE_AUTH_CALLBACK_URL}
283
-
284
- # Where the browser goes after a callback carrying no `redirect_to`.
285
- # clientUrl = ${?CORE_FEDERATIONS_GOOGLE_CLIENT_URL}
286
-
287
- # RFC 9207: Google always returns `iss` on the callback, so a callback
288
- # without one is refused. `false` is the way out if Google ever stops
289
- # sending it. It permits a missing `iss` only — one that is sent and is not
290
- # Google's is refused either way.
291
- # requireAuthorizationResponseIss = ${?CORE_FEDERATIONS_GOOGLE_REQUIRE_AUTHORIZATION_RESPONSE_ISS}
292
-
293
- # Whether sign-in asks Google for a refresh token. Unset is "offline":
294
- # every sign-in shows Google's consent screen, because Google issues a
295
- # refresh token only there, and every session gets one that
296
- # POST /oauth/federation/google/token can refresh with. "online" shows the
297
- # consent screen on the first sign-in only and gets no refresh token at
298
- # all — for a deployment that uses Google to sign in and nothing more.
299
- accessType = ${?CORE_FEDERATIONS_GOOGLE_ACCESS_TYPE}
300
- }
301
-
302
- # Any OpenID Connect provider (Okta, Entra ID, Auth0, Keycloak, your own
303
- # tenant) — `@o3co/auth-provider-federation-oidc`. Disabled by
304
- # default; the env vars below switch this one instance on. The section
305
- # name is the federation name: the browser starts at
306
- # /session/oauth/federation/oidc and the identity handed to the Store is
307
- # "oidc:<sub>". An identity the Store does not know is refused (401);
308
- # nothing is provisioned. To broker to a second IdP, copy this block under
309
- # another name (`okta { type = "oidc" ... }`) with its own callbackURL.
310
- oidc {
311
- enabled = false
312
- enabled = ${?CORE_FEDERATIONS_OIDC_ENABLED}
313
- type = "oidc"
314
-
315
- # Issuer identifier, exactly as the IdP writes it into `iss`. Discovery
316
- # runs at boot from <issuer>/.well-known/openid-configuration and a
317
- # failure refuses boot. https only (plain http on localhost for dev).
318
- issuer = ${?CORE_FEDERATIONS_OIDC_ISSUER}
319
- clientId = ${?CORE_FEDERATIONS_OIDC_CLIENT_ID}
320
- # client_secret_basic. For private_key_jwt drop clientSecret and set
321
- # privateKey (a PEM PKCS#8 key, or { pem, kid, alg }) instead.
322
- clientSecret = ${?CORE_FEDERATIONS_OIDC_CLIENT_SECRET}
323
- callbackURL = "http://localhost:3000/session/oauth/federation/oidc/callback"
324
- callbackURL = ${?CORE_FEDERATIONS_OIDC_CALLBACK_URL}
325
- scopes = ["openid", "profile", "email"]
326
-
327
- # Same rules as the Google block above: exact URLs, https except on a
328
- # loopback host, and an empty list refuses every `redirect_to`.
329
- redirectAllowlist = []
330
- # sessionDomain, authCallbackUrl and clientUrl work as for Google.
331
- #
332
- # Hand-typed endpoints instead of discovery (the IdP's document is wrong
333
- # or unreachable from this network):
334
- # discovery = false
335
- # endpoints { authorizationEndpoint = "...", tokenEndpoint = "...", jwksUri = "..." }
336
- }
337
- }
338
-
339
- # The variables that set keys of the template's own modules, bound here, last,
340
- # so each wins over a value this file sets above it. Keep this block last.
341
- # config/reference.conf holds the defaults and binds the same variables.
342
- http {
343
- port = ${?HTTP_PORT}
344
- trustProxy = ${?HTTP_TRUST_PROXY}
345
- cors.allowedOrigins = ${?HTTP_CORS_ALLOWED_ORIGINS}
346
- }
347
- redis-clients {
348
- url = ${?REDIS_CLIENTS_URL}
349
- password = ${?REDIS_CLIENTS_PASSWORD}
350
- assumeNoEviction = ${?REDIS_CLIENTS_ASSUME_NO_EVICTION}
351
- }
@@ -1,4 +0,0 @@
1
- # Client definitions loaded via loadYamlMap into InMemoryClientRepository.
2
- # Add client entries here. See clients.yaml.example for format.
3
- # Per-deployment: `.gitignore` keeps this file out of git and `.dockerignore`
4
- # out of the image (#705).
@@ -1,118 +0,0 @@
1
- # Example client definitions loaded via loadYamlMap into InMemoryClientRepository.
2
- # Copy this file to clients.yaml and customize for your deployment.
3
- #
4
- # clients.yaml is per-deployment (#705): `.gitignore` keeps it out of git and
5
- # `.dockerignore` out of the image, and docker-compose.production.yml mounts it
6
- # as the secret `client_registry`. This example is the committed one.
7
- #
8
- # `tokenEndpointAuthMethod` is REQUIRED (RFC 6749 §2.3 / RFC 7591 §2):
9
- # - "client_secret_basic": HTTP Basic Authorization header — recommended for confidential clients
10
- # - "client_secret_post": form-encoded body parameters
11
- # - "private_key_jwt": the client signs a JWT with its own private key
12
- # (RFC 7523 §2.2, #484) — register its public keys as
13
- # `jwks` (inline) or `jwksUri`, exactly one; no clientSecret
14
- # - "none": public client — no clientSecret; PKCE/S256 mandatory at /authorize
15
- #
16
- # Secret format (only for confidential clients — basic / post):
17
- # - Bcrypt hash: "$2b$10$..." <-- use this
18
- # - Plain text: "my-secret" <-- accepted, for local development only
19
- #
20
- # Generate a real one:
21
- # SECRET=$(openssl rand -base64 32)
22
- # node -e "require('bcrypt').hash(process.argv[1],10).then(console.log)" "$SECRET"
23
- # Give $SECRET to the client; put the hash in this file.
24
- #
25
- # #407: the schema accepts any non-empty string here, unlike `session.secret`
26
- # and the JWT signing secret, which have carried a 256-bit floor since #282.
27
- # That is deliberate — this field also holds a bcrypt hash, whose shape those
28
- # floors would reject — but it means NOTHING stops you from shipping
29
- # `clientSecret: "changeme"`. A client secret authenticates a confidential
30
- # client at the token endpoint; treat it as you would any other credential
31
- # and give it 256 bits of randomness.
32
- #
33
- # `firstParty` (#267): REQUIRED to be `true` for a client to use `/authorize`.
34
- # `/authorize` mints a code as soon as the session is authenticated, with no
35
- # consent step, so a forced top-level navigation from an attacker's page
36
- # makes a logged-in user's browser mint one for whatever client is named.
37
- # That is defensible for clients you operate and only for those. Mark a
38
- # client `firstParty: true` only if you would be comfortable with it
39
- # receiving the user's identity without the user being asked.
40
- #
41
- # A client without it is refused at `/authorize`, as is one carrying an
42
- # explicit `firstParty: false`. There is no opt-out: mark every client you
43
- # operate before pointing it at `/authorize`.
44
- #
45
- # `allowPlainPkce` (#273): PKCE is mandatory for EVERY client — confidential
46
- # ones included — and `S256` is the only method accepted. There is no
47
- # server-wide setting that changes either half. This per-client flag is the
48
- # one exception hatch, and only for the method: `allowPlainPkce: true` lets
49
- # that one client use the RFC 7636 `plain` challenge, which stores the
50
- # verifier itself as the challenge and so proves nothing against anyone who
51
- # can read the authorization request. Set it only for a legacy client that
52
- # genuinely cannot compute SHA-256, and treat it as a migration deadline.
53
- #
54
- # `allowExchangeOfTokensIssuedToOthers`: read only where the token-exchange
55
- # module (@o3co/auth-provider-oauth-token-exchange) is installed, which this
56
- # template does not do by default. Token exchange requires the caller to be
57
- # an audience of the subject token by default: the token's `azp` is the
58
- # client, or its `aud` contains it. `true` lets this one client exchange
59
- # tokens issued to other clients (a gateway, an on-behalf-of service). Only
60
- # a strict `true` counts.
61
-
62
- # A machine client that authenticates with a key pair (#484). Nothing here is
63
- # secret: the private half stays with the client, which signs an assertion
64
- # (iss = sub = client id, aud = the issuer or the token endpoint, exp within
65
- # an hour, a fresh jti) per token request. Rotating the key is a JWKS
66
- # publish when `jwksUri` is used. Needs the replay seen-set wired
67
- # (REPLAY_SEEN_SET_ADAPTER, on by default).
68
- #
69
- # orders-service:
70
- # tokenEndpointAuthMethod: "private_key_jwt"
71
- # jwksUri: "https://orders.example.com/.well-known/jwks.json"
72
- # # or inline:
73
- # # jwks:
74
- # # keys:
75
- # # - { kty: "EC", crv: "P-256", kid: "2026-09", x: "...", y: "..." }
76
- # allowedGrantTypes: ["client_credentials"]
77
- # allowedScopes: ["orders:read"]
78
- # defaultScopes: ["orders:read"]
79
- # allowedAudiences: ["https://api.example.com/orders"]
80
-
81
- my-app:
82
- tokenEndpointAuthMethod: "client_secret_basic"
83
- firstParty: true
84
- clientSecret: "$2b$10$REPLACE_WITH_BCRYPT_HASH"
85
- allowedRedirectUris:
86
- - "http://localhost:3000/callback"
87
- allowedScopes:
88
- - "read"
89
- - "write"
90
- # What an omitted `scope` parameter grants (#396). Must be a subset of
91
- # allowedScopes (checked at boot). Without this, a request that omits
92
- # `scope` is refused with invalid_scope — the old behavior granted the
93
- # ENTIRE allowlist, which made "forgot to send scope" the maximum grant.
94
- # Declare what an unscoped request should mean, not the ceiling.
95
- defaultScopes:
96
- - "read"
97
-
98
- # Public client example (e.g., a single-page app or mobile app):
99
- # my-spa:
100
- # tokenEndpointAuthMethod: "none"
101
- # firstParty: true
102
- # # clientSecret MUST be absent for "none" clients.
103
- # allowedRedirectUris:
104
- # - "https://app.example/callback"
105
- # allowedScopes:
106
- # - "openid"
107
- # - "profile"
108
-
109
- # Legacy client that cannot compute SHA-256 (#273). Prefer fixing the client.
110
- # my-legacy-device:
111
- # tokenEndpointAuthMethod: "client_secret_basic"
112
- # firstParty: true
113
- # clientSecret: "$2b$10$REPLACE_WITH_BCRYPT_HASH"
114
- # allowPlainPkce: true
115
- # allowedRedirectUris:
116
- # - "https://legacy.example/callback"
117
- # allowedScopes:
118
- # - "openid"
@@ -1,17 +0,0 @@
1
- # Environment overlay for development.
2
- # Loaded when CONFIG_ENV (or NODE_ENV) is "development" — the default.
3
- # Values here override application.conf.
4
- #
5
- # Most values are controlled via environment variables (see the ${?VAR}
6
- # substitutions in application.conf and reference.conf). Override entries
7
- # here only when you need a fixed development-specific value that cannot come
8
- # from an environment variable.
9
-
10
- # The MFA key ring's development key: the MFA package's published sample key
11
- # (MFA_DEVELOPMENT_SAMPLE_KEY), which everyone holds. It stands in for
12
- # MFA_ENCRYPTION_KEY here: to use a key of your own in development, write it
13
- # in place of this one. It is accepted only where the configuration's name and
14
- # NODE_ENV, each where set, say development or test, and is refused under
15
- # `core.deployment.mode = "multi"`; boot warns while it is in use
16
- # (mfa_development_sample_key_in_use). Read only while MFA_MODE installs MFA.
17
- mfa.encryptionKeys = [{ key = "bzNjbzptZmE6ZGV2ZWxvcG1lbnQtc2FtcGxlLWtleSE=" }]
@@ -1,13 +0,0 @@
1
- # Environment overlay for a development run whose mail goes to Mailpit
2
- # (docker-compose.mailpit.yml). Loaded when CONFIG_ENV is "mailpit".
3
- #
4
- # The template installs the sender that logs each code only where the
5
- # configuration is selected as "development", and the SMTP sender under any
6
- # other name. For development only; a deployment names its own relay.
7
- #
8
- # It does not include development.conf, whose one setting is the MFA
9
- # development sample key: the MFA package accepts that key only where every
10
- # environment name set says development or test, and this run is selected as
11
- # "mailpit". The key ring's first key is MFA_ENCRYPTION_KEY, as the MFA
12
- # package's reference.conf binds it: write a key of your own
13
- # (openssl rand -base64 32) into .env; without one the boot is refused.
@@ -1,15 +0,0 @@
1
- # Environment overlay for production.
2
- # Loaded when CONFIG_ENV (or NODE_ENV) is "production".
3
- # Values here override application.conf.
4
- #
5
- # Most production-sensitive values (SESSION_STORE_SECRET, the JWT signing key, REDIS
6
- # URLs, federation client secrets, etc.) should be supplied via environment
7
- # variables — see the ${?VAR} substitutions in application.conf and
8
- # reference.conf.
9
- #
10
- # The signing key is a key pair delivered as files
11
- # (KEY_STORE_LOCAL_PRIVATE_KEY_PATH / KEY_STORE_LOCAL_PUBLIC_KEY_PATH), which
12
- # docker-compose.production.yml mounts as compose secrets.
13
- #
14
- # Override entries here only when a value must be fixed per-environment rather
15
- # than per-deployment (e.g. feature flags turned on only in production).