@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.
- package/README.md +2 -198
- package/main.js +4 -0
- package/package.json +8 -30
- package/README.ja.md +0 -200
- package/dist/cli.d.mts +0 -3
- package/dist/cli.d.mts.map +0 -1
- package/dist/cli.mjs +0 -18
- package/dist/index.d.mts +0 -48
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs +0 -325
- package/dist/internal/template-filter.d.mts +0 -14
- package/dist/internal/template-filter.d.mts.map +0 -1
- package/dist/internal/template-filter.mjs +0 -42
- package/templates/standalone/.dockerignore +0 -23
- package/templates/standalone/.env.example +0 -152
- package/templates/standalone/Dockerfile +0 -125
- package/templates/standalone/Makefile +0 -12
- package/templates/standalone/README.ja.md +0 -913
- package/templates/standalone/README.md +0 -1537
- package/templates/standalone/config/application.conf +0 -351
- package/templates/standalone/config/clients.yaml +0 -4
- package/templates/standalone/config/clients.yaml.example +0 -118
- package/templates/standalone/config/development.conf +0 -17
- package/templates/standalone/config/mailpit.conf +0 -13
- package/templates/standalone/config/production.conf +0 -15
- package/templates/standalone/config/reference.conf +0 -424
- package/templates/standalone/docker-compose.mailpit.yml +0 -39
- package/templates/standalone/docker-compose.production.yml +0 -229
- package/templates/standalone/docker-compose.test.yml +0 -16
- package/templates/standalone/docker-compose.yml +0 -67
- package/templates/standalone/gitignore +0 -23
- package/templates/standalone/package.json +0 -55
- package/templates/standalone/src/__tests__/adapters.test.mts +0 -425
- package/templates/standalone/src/__tests__/admission-lifecycle.test.mts +0 -87
- package/templates/standalone/src/__tests__/all-modules-composition.fixture.mts +0 -1192
- package/templates/standalone/src/__tests__/all-modules-composition.multi.test.mts +0 -285
- package/templates/standalone/src/__tests__/all-modules-composition.test.mts +0 -1154
- package/templates/standalone/src/__tests__/audit-sink.test.mts +0 -346
- package/templates/standalone/src/__tests__/config-notices.test.mts +0 -157
- package/templates/standalone/src/__tests__/configPath.test.mts +0 -69
- package/templates/standalone/src/__tests__/configured-mfa-mode.fixture.mts +0 -48
- package/templates/standalone/src/__tests__/core-federations.test.mts +0 -252
- package/templates/standalone/src/__tests__/core-sections.test.mts +0 -192
- package/templates/standalone/src/__tests__/device-code-store-client-module.test.mts +0 -95
- package/templates/standalone/src/__tests__/documented-env-overrides.test.mts +0 -1290
- package/templates/standalone/src/__tests__/federation-grants-composition.test.mts +0 -784
- package/templates/standalone/src/__tests__/federation-grants-survive-logout.test.mts +0 -461
- package/templates/standalone/src/__tests__/federation-token-refresh-outage.test.mts +0 -141
- package/templates/standalone/src/__tests__/federation-types.test.mts +0 -239
- package/templates/standalone/src/__tests__/google-session.fixture.mts +0 -308
- package/templates/standalone/src/__tests__/host-routes.test.mts +0 -134
- package/templates/standalone/src/__tests__/library-reference-resolution.smoke.test.mts +0 -29
- package/templates/standalone/src/__tests__/library-references.fixture.mts +0 -188
- package/templates/standalone/src/__tests__/lifecycle-pending-close.test.mts +0 -216
- package/templates/standalone/src/__tests__/listen.test.mts +0 -121
- package/templates/standalone/src/__tests__/logger-projection.test.mts +0 -77
- package/templates/standalone/src/__tests__/logout-session-cookie.test.mts +0 -113
- package/templates/standalone/src/__tests__/mail-sender.test.mts +0 -134
- package/templates/standalone/src/__tests__/mailpit-compose.test.mts +0 -393
- package/templates/standalone/src/__tests__/metrics.test.mts +0 -253
- package/templates/standalone/src/__tests__/mfa-routes-budget.test.mts +0 -122
- package/templates/standalone/src/__tests__/mfa-switch.redis.test.mts +0 -127
- package/templates/standalone/src/__tests__/mfa-switch.test.mts +0 -883
- package/templates/standalone/src/__tests__/own-modules.test.mts +0 -1512
- package/templates/standalone/src/__tests__/post-logout-redirect.test.mts +0 -183
- package/templates/standalone/src/__tests__/redis-assume-noeviction.test.mts +0 -214
- package/templates/standalone/src/__tests__/redis-connection-error-log.test.mts +0 -135
- package/templates/standalone/src/__tests__/redis-rate-limiter-config-survives.test.mts +0 -214
- package/templates/standalone/src/__tests__/refresh-token-family-client-module.test.mts +0 -249
- package/templates/standalone/src/__tests__/replica-safety.test.mts +0 -654
- package/templates/standalone/src/__tests__/repositories-logger.test.mts +0 -119
- package/templates/standalone/src/__tests__/router-error-answers.test.mts +0 -146
- package/templates/standalone/src/__tests__/scaffold-assets.test.mts +0 -610
- package/templates/standalone/src/__tests__/sections.test.mts +0 -504
- package/templates/standalone/src/__tests__/session-lifecycle.test.mts +0 -215
- package/templates/standalone/src/__tests__/session-logout-invalidates-token.test.mts +0 -296
- package/templates/standalone/src/__tests__/session-logout-lifecycle.test.mts +0 -130
- package/templates/standalone/src/__tests__/session-requirements.test.mts +0 -382
- package/templates/standalone/src/__tests__/shipped-config-notices.test.mts +0 -274
- package/templates/standalone/src/__tests__/shutdown.test.mts +0 -622
- package/templates/standalone/src/__tests__/smoke.test.mts +0 -634
- package/templates/standalone/src/__tests__/startup-log-lines.test.mts +0 -148
- package/templates/standalone/src/__tests__/store-credential.test.mts +0 -312
- package/templates/standalone/src/__tests__/supertest-loopback.test.mts +0 -296
- package/templates/standalone/src/__tests__/three-tier-resolution.test.mts +0 -265
- package/templates/standalone/src/__tests__/two-phase-config.test.mts +0 -605
- package/templates/standalone/src/__tests__/upstream-amr.acceptance.test.mts +0 -382
- package/templates/standalone/src/adapters.mts +0 -162
- package/templates/standalone/src/app.mts +0 -136
- package/templates/standalone/src/bootRefusal.mts +0 -117
- package/templates/standalone/src/buildModules.mts +0 -358
- package/templates/standalone/src/configPath.mts +0 -487
- package/templates/standalone/src/listen.mts +0 -60
- package/templates/standalone/src/logger.mts +0 -80
- package/templates/standalone/src/metrics.mts +0 -167
- package/templates/standalone/src/mfaSwitch.mts +0 -284
- package/templates/standalone/src/modules.mts +0 -705
- package/templates/standalone/src/rootRenames.mts +0 -95
- package/templates/standalone/src/routes.mts +0 -89
- package/templates/standalone/src/sections.mts +0 -377
- package/templates/standalone/src/shutdown.mts +0 -213
- package/templates/standalone/tests/Makefile +0 -5
- package/templates/standalone/tests/index.test.js +0 -283
- package/templates/standalone/tests/package-lock.json +0 -1617
- package/templates/standalone/tests/package.json +0 -16
- package/templates/standalone/tests/vitest.config.mjs +0 -26
- package/templates/standalone/tsconfig.json +0 -31
- package/templates/standalone/vitest.config.mts +0 -31
- package/templates/standalone/vitest.supertest-loopback.mts +0 -278
- 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,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).
|