@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.
- package/README.ja.md +66 -21
- package/README.md +76 -31
- 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 +59 -29
- package/templates/standalone/Dockerfile +6 -2
- package/templates/standalone/README.ja.md +324 -169
- package/templates/standalone/README.md +618 -208
- package/templates/standalone/config/application.conf +211 -352
- 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 +418 -0
- package/templates/standalone/docker-compose.mailpit.yml +36 -0
- package/templates/standalone/docker-compose.production.yml +63 -32
- package/templates/standalone/docker-compose.yml +13 -3
- package/templates/standalone/gitignore +1 -0
- package/templates/standalone/package.json +9 -6
- package/templates/standalone/src/__tests__/adapters.test.mts +419 -0
- package/templates/standalone/src/__tests__/admission-lifecycle.test.mts +87 -0
- package/templates/standalone/src/__tests__/all-modules-composition.fixture.mts +287 -198
- package/templates/standalone/src/__tests__/all-modules-composition.multi.test.mts +71 -35
- package/templates/standalone/src/__tests__/all-modules-composition.test.mts +148 -94
- package/templates/standalone/src/__tests__/audit-sink.test.mts +84 -81
- package/templates/standalone/src/__tests__/config-notices.test.mts +154 -0
- package/templates/standalone/src/__tests__/configPath.test.mts +13 -11
- package/templates/standalone/src/__tests__/core-federations.test.mts +249 -0
- package/templates/standalone/src/__tests__/core-sections.test.mts +190 -0
- package/templates/standalone/src/__tests__/device-code-store-client-module.test.mts +10 -15
- package/templates/standalone/src/__tests__/documented-env-overrides.test.mts +970 -288
- package/templates/standalone/src/__tests__/federation-grants-composition.test.mts +421 -86
- package/templates/standalone/src/__tests__/federation-grants-survive-logout.test.mts +87 -72
- package/templates/standalone/src/__tests__/federation-token-refresh-outage.test.mts +16 -8
- package/templates/standalone/src/__tests__/federation-types.test.mts +239 -0
- package/templates/standalone/src/__tests__/google-session.fixture.mts +97 -93
- package/templates/standalone/src/__tests__/host-routes.test.mts +4 -11
- package/templates/standalone/src/__tests__/library-reference-resolution.smoke.test.mts +1 -1
- package/templates/standalone/src/__tests__/library-references.fixture.mts +188 -0
- package/templates/standalone/src/__tests__/lifecycle-pending-close.test.mts +216 -0
- package/templates/standalone/src/__tests__/listen.test.mts +6 -13
- package/templates/standalone/src/__tests__/logger-projection.test.mts +1 -1
- package/templates/standalone/src/__tests__/mail-sender.test.mts +134 -0
- package/templates/standalone/src/__tests__/mailpit-compose.test.mts +326 -0
- package/templates/standalone/src/__tests__/metrics.test.mts +4 -9
- package/templates/standalone/src/__tests__/mfa-routes-budget.test.mts +122 -0
- package/templates/standalone/src/__tests__/mfa-switch.redis.test.mts +123 -0
- package/templates/standalone/src/__tests__/mfa-switch.test.mts +825 -0
- package/templates/standalone/src/__tests__/own-modules.test.mts +1486 -0
- package/templates/standalone/src/__tests__/post-logout-redirect.test.mts +10 -14
- package/templates/standalone/src/__tests__/redis-connection-error-log.test.mts +1 -5
- package/templates/standalone/src/__tests__/redis-rate-limiter-config-survives.test.mts +116 -81
- package/templates/standalone/src/__tests__/refresh-token-family-client-module.test.mts +25 -31
- package/templates/standalone/src/__tests__/replica-safety.test.mts +265 -146
- package/templates/standalone/src/__tests__/router-error-answers.test.mts +4 -7
- package/templates/standalone/src/__tests__/scaffold-assets.test.mts +278 -103
- package/templates/standalone/src/__tests__/sections.test.mts +480 -0
- package/templates/standalone/src/__tests__/session-lifecycle.test.mts +215 -0
- package/templates/standalone/src/__tests__/session-logout-invalidates-token.test.mts +69 -61
- package/templates/standalone/src/__tests__/session-logout-lifecycle.test.mts +130 -0
- package/templates/standalone/src/__tests__/session-requirements.test.mts +382 -0
- package/templates/standalone/src/__tests__/shipped-config-notices.test.mts +270 -0
- package/templates/standalone/src/__tests__/shutdown.test.mts +163 -96
- package/templates/standalone/src/__tests__/smoke.test.mts +176 -258
- package/templates/standalone/src/__tests__/store-credential.test.mts +88 -52
- package/templates/standalone/src/__tests__/supertest-loopback.test.mts +182 -11
- package/templates/standalone/src/__tests__/three-tier-resolution.test.mts +131 -102
- package/templates/standalone/src/__tests__/two-phase-config.test.mts +604 -0
- package/templates/standalone/src/__tests__/upstream-amr.acceptance.test.mts +382 -0
- package/templates/standalone/src/adapters.mts +162 -0
- package/templates/standalone/src/app.mts +65 -79
- package/templates/standalone/src/bootRefusal.mts +117 -0
- package/templates/standalone/src/buildModules.mts +220 -309
- package/templates/standalone/src/configPath.mts +449 -29
- package/templates/standalone/src/listen.mts +12 -18
- package/templates/standalone/src/logger.mts +31 -60
- package/templates/standalone/src/metrics.mts +24 -47
- package/templates/standalone/src/mfaSwitch.mts +285 -0
- package/templates/standalone/src/modules.mts +423 -629
- package/templates/standalone/src/rootRenames.mts +96 -0
- package/templates/standalone/src/routes.mts +8 -14
- package/templates/standalone/src/sections.mts +367 -0
- package/templates/standalone/src/shutdown.mts +45 -123
- package/templates/standalone/tests/index.test.js +6 -5
- 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__/access-token-lifetime-alias.test.mts +0 -135
- package/templates/standalone/src/__tests__/google-federation-config-module.test.mts +0 -199
- package/templates/standalone/src/__tests__/oidc-federation-config-module.test.mts +0 -138
|
@@ -1,121 +1,102 @@
|
|
|
1
1
|
# Application-level overrides for the standalone template.
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
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.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
# HTTP_TRUST_PROXY=10.0.0.0/8 (or `loopback` for a sidecar) rather than
|
|
14
|
-
# `true`, which trusts a forwarded client address from anyone who can reach
|
|
15
|
-
# this process. See the header comment in the library's reference.conf.
|
|
16
|
-
trustProxy = false
|
|
17
|
-
trustProxy = ${?HTTP_TRUST_PROXY}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
# #287: where the audit trail goes. Before this block the template wired no
|
|
21
|
-
# sink at all, so every security event the routes emit -- token.issued.failure,
|
|
22
|
-
# authorize.rejected / authorize.granted, logout.cascade_failed, and the shared
|
|
23
|
-
# rate-limit guard's rate_limit.unavailable -- was discarded by the artifact
|
|
24
|
-
# operators deploy. Nothing failed and nothing warned: `emitAuditEvent` is a
|
|
25
|
-
# no-op when the slot is empty.
|
|
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:
|
|
26
14
|
#
|
|
27
|
-
#
|
|
28
|
-
# JSON to stdout, but "logger" writes it through the same pino stream that
|
|
29
|
-
# carries every other line this template emits, so a log aggregator ingests
|
|
30
|
-
# application logs and audit events with one parser and separates them on
|
|
31
|
-
# `name` ("provider" vs "audit"). "console" writes the bare event with no log
|
|
32
|
-
# envelope -- no level, no time -- which most pipelines then timestamp by
|
|
33
|
-
# ingestion. Set AUDIT_SINK_TYPE=console if that is what your pipeline wants.
|
|
15
|
+
# adapters.rateLimiter = "redis"
|
|
34
16
|
#
|
|
35
17
|
# The audit stream's level is FIXED at info and `logging.level` does not gate
|
|
36
|
-
# it
|
|
37
|
-
# legitimate one
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
#
|
|
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.
|
|
41
36
|
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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}
|
|
49
63
|
}
|
|
50
64
|
}
|
|
51
65
|
|
|
52
66
|
oauth {
|
|
53
|
-
|
|
54
|
-
# Template is a typical web-app OP — enable the universal user-auth path.
|
|
55
|
-
# client_credentials remains off (reference.conf default); deployments that
|
|
56
|
-
# need M2M flows set OAUTH_GRANTS_CLIENT_CREDENTIALS_ENABLED=true or add
|
|
57
|
-
# `client_credentials { enabled = true }` in their own application.conf.
|
|
58
|
-
#
|
|
59
|
-
# The env-override line (`enabled = ${?OAUTH_GRANTS_*_ENABLED}`) is repeated
|
|
60
|
-
# at this layer because HOCON precedence: application.conf wins over
|
|
61
|
-
# reference.conf, so without the env-override line at the template layer,
|
|
62
|
-
# an operator setting OAUTH_GRANTS_SESSION_ENABLED=false (etc.) cannot
|
|
63
|
-
# disable the grant — the env var only takes effect at the layer where
|
|
64
|
-
# the substitution is written.
|
|
65
|
-
session {
|
|
66
|
-
enabled = true
|
|
67
|
-
enabled = ${?OAUTH_GRANTS_SESSION_ENABLED}
|
|
68
|
-
}
|
|
69
|
-
# #273: there is no `pkce` block here any more. PKCE is mandatory for
|
|
70
|
-
# every authorization-code client and `S256` is the only method the
|
|
71
|
-
# provider accepts — that is fixed policy (OAuth 2.1 §4.1.1 / RFC 9700
|
|
72
|
-
# §2.1.1), not a deployment setting, so `requireS256` / `required` /
|
|
73
|
-
# `defaultMethod` / `supportedMethods` are gone. A legacy client that
|
|
74
|
-
# genuinely cannot compute SHA-256 is opted into `plain` one client at a
|
|
75
|
-
# time, with `allowPlainPkce: true` in clients.yaml. The
|
|
76
|
-
# OAUTH_GRANTS_AUTHORIZATION_CODE_PKCE_REQUIRE_S256 tombstone in core's
|
|
77
|
-
# reference.conf makes a still-exported env var warn at boot.
|
|
78
|
-
authorization_code {
|
|
79
|
-
enabled = true
|
|
80
|
-
enabled = ${?OAUTH_GRANTS_AUTHORIZATION_CODE_ENABLED}
|
|
81
|
-
}
|
|
82
|
-
refresh_token {
|
|
83
|
-
enabled = true
|
|
84
|
-
enabled = ${?OAUTH_GRANTS_REFRESH_TOKEN_ENABLED}
|
|
85
|
-
}
|
|
86
|
-
# client_credentials: omitted -> inherits reference.conf default (enabled = false).
|
|
87
|
-
# Operators that need M2M flows set OAUTH_GRANTS_CLIENT_CREDENTIALS_ENABLED=true
|
|
88
|
-
# or add `client_credentials { enabled = true }` here.
|
|
89
|
-
}
|
|
90
|
-
# OR-9: adapter switch for the OAuth authorization-code repository. When
|
|
91
|
-
# set to "redis", the standalone composition root wires
|
|
92
|
-
# `redisCodeRepositoryModule` against the shared ioredis socket
|
|
93
|
-
# (`standaloneRedisClientsModule`). Memory-only deployments lose codes on
|
|
94
|
-
# restart and across replicas — multi-replica production MUST use "redis".
|
|
95
|
-
# Supersedes the legacy `repositories.code.type` switch (kept below as
|
|
96
|
-
# deprecated for backward compat — see CHANGELOG for the removal version).
|
|
97
|
-
# #481: the Authentication Context Class References this deployment can
|
|
67
|
+
# The Authentication Context Class References this deployment can
|
|
98
68
|
# vouch for, each mapped to the RFC 8176 `amr` values a session must carry
|
|
99
|
-
# (`pwd` for a password login, `
|
|
100
|
-
#
|
|
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
|
|
101
71
|
# `acr_values` from this table alone — anything else is
|
|
102
72
|
# `unmet_authentication_requirements` — and discovery advertises the keys.
|
|
103
73
|
#
|
|
104
74
|
# authorize.acrValues {
|
|
105
75
|
# "urn:example:pwd" = ["pwd"]
|
|
106
|
-
# "urn:example:mfa" = ["pwd", "mfa"]
|
|
107
76
|
# }
|
|
108
77
|
#
|
|
109
|
-
#
|
|
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
|
|
110
94
|
# clients.yaml — the template's example clients are marked. There is no
|
|
111
|
-
# opt-out: the one-time `allowUnmarkedClients` migration flag
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
adapter = ${?OAUTH_CODE_ADAPTER}
|
|
117
|
-
}
|
|
118
|
-
# #277: POST /oauth/revoke revokes access tokens by writing their `jti` to the
|
|
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
|
|
119
100
|
# denylist wired below, and boot FAILS if that denylist is missing — an
|
|
120
101
|
# RFC 7009 endpoint must answer 200, so an unwired denylist would tell every
|
|
121
102
|
# operator their token is revoked while it kept working until expiry.
|
|
@@ -131,149 +112,144 @@ oauth {
|
|
|
131
112
|
}
|
|
132
113
|
}
|
|
133
114
|
|
|
134
|
-
#
|
|
135
|
-
#
|
|
136
|
-
#
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
#
|
|
140
|
-
#
|
|
141
|
-
#
|
|
142
|
-
#
|
|
143
|
-
#
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
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 = []
|
|
148
128
|
|
|
149
|
-
#
|
|
150
|
-
#
|
|
151
|
-
#
|
|
152
|
-
#
|
|
153
|
-
#
|
|
154
|
-
#
|
|
155
|
-
#
|
|
156
|
-
#
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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.
|
|
161
144
|
|
|
162
|
-
#
|
|
145
|
+
# Federation grants — a user's standing consent that a backend or an
|
|
163
146
|
# agent may obtain upstream IdP tokens with no session behind the call. Off by
|
|
164
147
|
# default; FEDERATION_GRANTS_ENABLED=true installs the routes, the two stores
|
|
165
|
-
#
|
|
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
|
|
166
151
|
# the deployment has a consent page (FEDERATION_GRANTS_CONSENT_URL, no default),
|
|
167
152
|
# a `callbackURL` on every connection, and a user repository that covers each
|
|
168
153
|
# connection's registration (or FEDERATION_GRANTS_IDENTITY_LOOKUP=unsupported,
|
|
169
154
|
# the recorded decision not to make that check) — each refused at boot when
|
|
170
|
-
# missing. Connections and the
|
|
171
|
-
# write them in a deployment-owned HOCON layer (README:
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
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}
|
|
183
167
|
|
|
184
|
-
#
|
|
185
|
-
#
|
|
186
|
-
#
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
#
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
adapter = ${?FEDERATION_GRANT_STORE_ADAPTER}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
federationGrantIntentStore {
|
|
196
|
-
adapter = "redis"
|
|
197
|
-
adapter = ${?FEDERATION_GRANT_INTENT_STORE_ADAPTER}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
# #527: where consent to a client that is not first-party is recorded.
|
|
201
|
-
# "none" (the default) wires no store and such clients are refused at
|
|
202
|
-
# /authorize; "memory" is single-replica only (refused under
|
|
203
|
-
# DEPLOYMENT_MODE=multi by name); "redis" (#561) keeps the consent records and
|
|
204
|
-
# the requests parked while the consent page asks on the shared ioredis socket,
|
|
205
|
-
# and is what a deployment of more than one replica selects. Its key namespace
|
|
206
|
-
# is REDIS_CONSENT_STORE_KEY_PREFIX (default "consent:").
|
|
207
|
-
consentStore {
|
|
208
|
-
adapter = "none"
|
|
209
|
-
adapter = ${?CONSENT_STORE_ADAPTER}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
session {
|
|
213
|
-
secret = ${?SESSION_SECRET}
|
|
214
|
-
# MIN-2: __Host- prefix requires Secure, Path=/, and no Domain attribute.
|
|
215
|
-
# If SESSION_SECURE=false or SESSION_DOMAIN is set, also override
|
|
216
|
-
# SESSION_NAME to a non-__Host- value.
|
|
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.
|
|
217
176
|
name = "__Host-auth.session"
|
|
218
|
-
name = ${?
|
|
177
|
+
name = ${?SESSION_STORE_NAME}
|
|
219
178
|
maxAge = 3600000
|
|
220
|
-
maxAge = ${?
|
|
179
|
+
maxAge = ${?SESSION_STORE_MAX_AGE}
|
|
221
180
|
secure = true
|
|
222
|
-
secure = ${?
|
|
181
|
+
secure = ${?SESSION_STORE_SECURE}
|
|
223
182
|
sameSite = "lax"
|
|
224
|
-
sameSite = ${?
|
|
183
|
+
sameSite = ${?SESSION_STORE_SAME_SITE}
|
|
225
184
|
domain = null
|
|
226
|
-
domain = ${?
|
|
227
|
-
|
|
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
|
|
228
200
|
# EXACTLY -- scheme, host, port, path and query all significant. Empty (the
|
|
229
201
|
# default) refuses every `redirect_to`, so leave it alone unless your login
|
|
230
202
|
# page actually posts one.
|
|
231
203
|
#
|
|
232
204
|
# redirectAllowlist = ["https://app.example.com/welcome"]
|
|
233
205
|
#
|
|
234
|
-
# `http://` is accepted for loopback hosts only. With `domain`
|
|
235
|
-
# entry outside it fails boot rather than being silently
|
|
236
|
-
# federation flow has a separate list per provider (see
|
|
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).
|
|
237
210
|
redirectAllowlist = []
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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 }
|
|
246
224
|
}
|
|
247
225
|
}
|
|
248
226
|
|
|
249
|
-
#
|
|
250
|
-
#
|
|
251
|
-
#
|
|
252
|
-
#
|
|
253
|
-
#
|
|
254
|
-
# its own `memoryRateLimiter.*` / `redisRateLimiter.*` config section
|
|
255
|
-
# (uses `windowSeconds` instead of `windowMs`). Switch via
|
|
256
|
-
# `rateLimiter.adapter = "memory" | "redis"` (see reference.conf).
|
|
257
|
-
rateLimit {
|
|
258
|
-
# `login` is per-deployment tuning (bruteforce-window + limit), kept in
|
|
259
|
-
# this layer so operators can tweak without forking reference.conf.
|
|
260
|
-
login { windowMs = 900000, limit = 20 }
|
|
261
|
-
# rateLimit.failMode: inherits reference.conf default ("closed").
|
|
262
|
-
# Deployments that prefer fail-open override here:
|
|
263
|
-
# failMode = "open"
|
|
264
|
-
# failMode = ${?RATE_LIMIT_FAIL_MODE}
|
|
265
|
-
}
|
|
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 }
|
|
266
232
|
|
|
267
|
-
federations
|
|
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 {
|
|
268
242
|
# Template ships a Google federation declaration disabled-by-default.
|
|
269
|
-
# Operators enable + populate credentials via env vars.
|
|
243
|
+
# Operators enable + populate credentials via env vars. A second Google
|
|
244
|
+
# client is another entry with `type = "google"` and its own callbackURL.
|
|
270
245
|
google {
|
|
271
246
|
enabled = false
|
|
272
|
-
enabled = ${?
|
|
273
|
-
|
|
274
|
-
|
|
247
|
+
enabled = ${?CORE_FEDERATIONS_GOOGLE_ENABLED}
|
|
248
|
+
type = "google"
|
|
249
|
+
clientId = ${?CORE_FEDERATIONS_GOOGLE_CLIENT_ID}
|
|
250
|
+
clientSecret = ${?CORE_FEDERATIONS_GOOGLE_CLIENT_SECRET}
|
|
275
251
|
callbackURL = "http://localhost:3000/session/oauth/federation/google/callback"
|
|
276
|
-
callbackURL = ${?
|
|
252
|
+
callbackURL = ${?CORE_FEDERATIONS_GOOGLE_CALLBACK_URL}
|
|
277
253
|
|
|
278
254
|
# Every URL a `redirect_to` query parameter may name on
|
|
279
255
|
# /session/oauth/federation/google. Matching is EXACT — scheme, host, port,
|
|
@@ -282,8 +258,8 @@ federations {
|
|
|
282
258
|
#
|
|
283
259
|
# An empty list refuses every `redirect_to`, which is the right setting for
|
|
284
260
|
# a deployment that does not use the parameter. It is NOT a way to allow
|
|
285
|
-
# everything:
|
|
286
|
-
#
|
|
261
|
+
# everything: nothing falls back to accepting any http(s) URL, which would
|
|
262
|
+
# make this an open redirect.
|
|
287
263
|
#
|
|
288
264
|
# https is required, except for a loopback host (localhost, 127.0.0.0/8,
|
|
289
265
|
# [::1]) — that carve-out is what lets a local dev front-end and a native
|
|
@@ -299,20 +275,20 @@ federations {
|
|
|
299
275
|
# Cookie / session domain. When set, every non-loopback redirectAllowlist
|
|
300
276
|
# entry must be inside it, checked at boot — an entry outside it fails
|
|
301
277
|
# startup rather than sitting here looking effective.
|
|
302
|
-
# sessionDomain = ${?
|
|
278
|
+
# sessionDomain = ${?CORE_FEDERATIONS_GOOGLE_SESSION_DOMAIN}
|
|
303
279
|
|
|
304
280
|
# Post-callback bridge page. Required only if `redirect_to` is used at all:
|
|
305
281
|
# the browser lands here as `<authCallbackUrl>?redirect_to=<encoded>`.
|
|
306
|
-
# authCallbackUrl = ${?
|
|
282
|
+
# authCallbackUrl = ${?CORE_FEDERATIONS_GOOGLE_AUTH_CALLBACK_URL}
|
|
307
283
|
|
|
308
284
|
# Where the browser goes after a callback carrying no `redirect_to`.
|
|
309
|
-
# clientUrl = ${?
|
|
285
|
+
# clientUrl = ${?CORE_FEDERATIONS_GOOGLE_CLIENT_URL}
|
|
310
286
|
|
|
311
287
|
# RFC 9207: Google always returns `iss` on the callback, so a callback
|
|
312
288
|
# without one is refused. `false` is the way out if Google ever stops
|
|
313
289
|
# sending it. It permits a missing `iss` only — one that is sent and is not
|
|
314
290
|
# Google's is refused either way.
|
|
315
|
-
# requireAuthorizationResponseIss = ${?
|
|
291
|
+
# requireAuthorizationResponseIss = ${?CORE_FEDERATIONS_GOOGLE_REQUIRE_AUTHORIZATION_RESPONSE_ISS}
|
|
316
292
|
|
|
317
293
|
# Whether sign-in asks Google for a refresh token. Unset is "offline":
|
|
318
294
|
# every sign-in shows Google's consent screen, because Google issues a
|
|
@@ -320,11 +296,11 @@ federations {
|
|
|
320
296
|
# POST /oauth/federation/google/token can refresh with. "online" shows the
|
|
321
297
|
# consent screen on the first sign-in only and gets no refresh token at
|
|
322
298
|
# all — for a deployment that uses Google to sign in and nothing more.
|
|
323
|
-
accessType = ${?
|
|
299
|
+
accessType = ${?CORE_FEDERATIONS_GOOGLE_ACCESS_TYPE}
|
|
324
300
|
}
|
|
325
301
|
|
|
326
302
|
# Any OpenID Connect provider (Okta, Entra ID, Auth0, Keycloak, your own
|
|
327
|
-
# tenant) — `@o3co/auth-provider-federation-oidc
|
|
303
|
+
# tenant) — `@o3co/auth-provider-federation-oidc`. Disabled by
|
|
328
304
|
# default; the env vars below switch this one instance on. The section
|
|
329
305
|
# name is the federation name: the browser starts at
|
|
330
306
|
# /session/oauth/federation/oidc and the identity handed to the Store is
|
|
@@ -333,19 +309,19 @@ federations {
|
|
|
333
309
|
# another name (`okta { type = "oidc" ... }`) with its own callbackURL.
|
|
334
310
|
oidc {
|
|
335
311
|
enabled = false
|
|
336
|
-
enabled = ${?
|
|
312
|
+
enabled = ${?CORE_FEDERATIONS_OIDC_ENABLED}
|
|
337
313
|
type = "oidc"
|
|
338
314
|
|
|
339
315
|
# Issuer identifier, exactly as the IdP writes it into `iss`. Discovery
|
|
340
316
|
# runs at boot from <issuer>/.well-known/openid-configuration and a
|
|
341
317
|
# failure refuses boot. https only (plain http on localhost for dev).
|
|
342
|
-
issuer = ${?
|
|
343
|
-
clientId = ${?
|
|
318
|
+
issuer = ${?CORE_FEDERATIONS_OIDC_ISSUER}
|
|
319
|
+
clientId = ${?CORE_FEDERATIONS_OIDC_CLIENT_ID}
|
|
344
320
|
# client_secret_basic. For private_key_jwt drop clientSecret and set
|
|
345
321
|
# privateKey (a PEM PKCS#8 key, or { pem, kid, alg }) instead.
|
|
346
|
-
clientSecret = ${?
|
|
322
|
+
clientSecret = ${?CORE_FEDERATIONS_OIDC_CLIENT_SECRET}
|
|
347
323
|
callbackURL = "http://localhost:3000/session/oauth/federation/oidc/callback"
|
|
348
|
-
callbackURL = ${?
|
|
324
|
+
callbackURL = ${?CORE_FEDERATIONS_OIDC_CALLBACK_URL}
|
|
349
325
|
scopes = ["openid", "profile", "email"]
|
|
350
326
|
|
|
351
327
|
# Same rules as the Google block above: exact URLs, https except on a
|
|
@@ -360,132 +336,15 @@ federations {
|
|
|
360
336
|
}
|
|
361
337
|
}
|
|
362
338
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
user {
|
|
373
|
-
type = "http"
|
|
374
|
-
type = ${?CLIENT_USER_TYPE}
|
|
375
|
-
yaml {
|
|
376
|
-
path = "./config/users.yaml"
|
|
377
|
-
}
|
|
378
|
-
http {
|
|
379
|
-
# Both URLs carry plaintext user credentials, so both must be absolute
|
|
380
|
-
# https URLs. http:// is accepted only for a loopback host (localhost,
|
|
381
|
-
# 127.0.0.0/8, [::1]); a private-range address or a container-network
|
|
382
|
-
# service name still needs https. Boot fails if either is not. Each must
|
|
383
|
-
# also be the endpoint that answers: no request follows a 3xx, so a URL
|
|
384
|
-
# that redirects fails every call.
|
|
385
|
-
authenticateUrl = ${?CLIENT_USER_AUTHENTICATE_URL}
|
|
386
|
-
authenticateByTokenUrl = ${?CLIENT_USER_AUTHENTICATE_BY_TOKEN_URL}
|
|
387
|
-
# #482: optional. When set, a signed-in user can link a second federated
|
|
388
|
-
# identity (GET /session/oauth/federation/<name>?link=1): the Store
|
|
389
|
-
# receives { userId, provider, sub, token, claims } and decides — never on
|
|
390
|
-
# an unverified or relay e-mail. Same https and no-redirect rule as the two URLs above.
|
|
391
|
-
# Also declared in core's reference.conf, so a composition that does not
|
|
392
|
-
# layer this file gets the same override.
|
|
393
|
-
# Without it, link=1 is refused with link_unsupported.
|
|
394
|
-
linkFederatedIdentityUrl = ${?CLIENT_USER_LINK_FEDERATED_IDENTITY_URL}
|
|
395
|
-
# #613: optional. The Store's identity lookup for federation grants —
|
|
396
|
-
# what lets this deployment keep FEDERATION_GRANTS_IDENTITY_LOOKUP=required
|
|
397
|
-
# with a connection configured. Same https and no-redirect rule. Declare what the Store
|
|
398
|
-
# covers, per registration, in a HOCON layer (README: Federation Grants):
|
|
399
|
-
# federatedIdentityLookupCoverage = [
|
|
400
|
-
# { provider = "entra-files", issuer = "...", clientId = "...", requiredClaims = ["tid", "oid"] }
|
|
401
|
-
# ]
|
|
402
|
-
findSubjectByFederatedIdentityUrl = ${?CLIENT_USER_FIND_SUBJECT_BY_FEDERATED_IDENTITY_URL}
|
|
403
|
-
# Optional. Sent on every Store request as `Authorization: Bearer <token>`
|
|
404
|
-
# so the Store can refuse any caller but this deployment; unset, none is
|
|
405
|
-
# sent. A bare token (no "Bearer " prefix), at least 32 bytes of key
|
|
406
|
-
# material -- `openssl rand -hex 32`. Boot fails on a weak, malformed or
|
|
407
|
-
# blank value. One token goes to every URL above: one trust domain. A
|
|
408
|
-
# Store that refuses it with 401 (or 403) + `WWW-Authenticate: Bearer`
|
|
409
|
-
# makes every Store call an outage rather than a wrong password: 503 and
|
|
410
|
-
# a logged error at login, federation callbacks and the jwt-bearer
|
|
411
|
-
# grant; a temporarily_unavailable redirect and
|
|
412
|
-
# federation_grant_callback_unavailable (err.name
|
|
413
|
-
# StoreCredentialRefusedError) at the federation-grants callback. Also
|
|
414
|
-
# declared in core's reference.conf.
|
|
415
|
-
bearerToken = ${?CLIENT_USER_BEARER_TOKEN}
|
|
416
|
-
# Positive integer, milliseconds, <= 2147483647. Boot fails otherwise —
|
|
417
|
-
# including on a blank env override, which HOCON substitutes as "".
|
|
418
|
-
timeout = 5000
|
|
419
|
-
timeout = ${?CLIENT_USER_TIMEOUT}
|
|
420
|
-
# Largest upstream response body accepted, in bytes. Positive integer.
|
|
421
|
-
maxResponseBytes = 1048576
|
|
422
|
-
maxResponseBytes = ${?CLIENT_USER_MAX_RESPONSE_BYTES}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
code {
|
|
426
|
-
type = "redis"
|
|
427
|
-
type = ${?CLIENT_CODE_TYPE}
|
|
428
|
-
memory {
|
|
429
|
-
defaultExpiresIn = 600
|
|
430
|
-
defaultExpiresIn = ${?CLIENT_CODE_DEFAULT_EXPIRES_IN}
|
|
431
|
-
}
|
|
432
|
-
redis {
|
|
433
|
-
defaultExpiresIn = 600
|
|
434
|
-
defaultExpiresIn = ${?CLIENT_CODE_DEFAULT_EXPIRES_IN}
|
|
435
|
-
endpointUri = ${?CLIENT_CODE_ENDPOINT_URI}
|
|
436
|
-
password = ${?CLIENT_CODE_PASSWORD}
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
endpoints {
|
|
442
|
-
login {
|
|
443
|
-
url = "/login"
|
|
444
|
-
url = ${?ENDPOINTS_LOGIN_URL}
|
|
445
|
-
}
|
|
446
|
-
# #527: the consent page a client that is not first-party is routed
|
|
447
|
-
# through (`?challenge=<id>`). Deployment-owned, like the login page: it
|
|
448
|
-
# reads GET /oauth/consent and posts to POST /oauth/consent.
|
|
449
|
-
consent {
|
|
450
|
-
url = "/consent"
|
|
451
|
-
url = ${?ENDPOINTS_CONSENT_URL}
|
|
452
|
-
}
|
|
453
|
-
# IH-10: `client { url }` and `authCallback { url }` removed — no
|
|
454
|
-
# production consumer reads them.
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
# #500: browser origins allowed to read the token, userinfo, revocation and
|
|
458
|
-
# discovery/JWKS responses. Empty (the default) means CORS is off -- leave it
|
|
459
|
-
# alone unless a browser app is served from a different origin than this
|
|
460
|
-
# provider. See the library reference.conf for the full rules; the short
|
|
461
|
-
# version is that matching is EXACT, so each entry is a bare origin with no
|
|
462
|
-
# trailing slash, no path and no wildcard, and https is required off loopback.
|
|
463
|
-
#
|
|
464
|
-
# allowedOrigins = ["https://app.example.com", "http://localhost:5173"]
|
|
465
|
-
#
|
|
466
|
-
# The env-override line is repeated at this layer for the HOCON precedence
|
|
467
|
-
# reason the oauth.grants block above explains: application.conf wins over
|
|
468
|
-
# reference.conf, so without it CORS_ALLOWED_ORIGINS could not take effect.
|
|
469
|
-
# It takes a comma-separated list.
|
|
470
|
-
#
|
|
471
|
-
# Not to be confused with `session.csrf.trustedOrigins`, which answers the
|
|
472
|
-
# other question (#272).
|
|
473
|
-
cors {
|
|
474
|
-
allowedOrigins = []
|
|
475
|
-
allowedOrigins = ${?CORS_ALLOWED_ORIGINS}
|
|
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}
|
|
476
346
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
# One ioredis socket per replica backs ALL Redis-typed clients (RT family
|
|
481
|
-
# + 4 user-session stores + rate limiter). Multi-replica deployments MUST
|
|
482
|
-
# point this at a shared Redis 7.2+ instance — without it, each replica
|
|
483
|
-
# stores RT families in its own ioredis connection against a local-only
|
|
484
|
-
# Redis, defeating the cross-replica persistence purpose.
|
|
485
|
-
refreshTokenFamilyStore {
|
|
486
|
-
redis {
|
|
487
|
-
url = "redis://localhost:6379"
|
|
488
|
-
url = ${?REFRESH_TOKEN_FAMILY_STORE_REDIS_URL}
|
|
489
|
-
password = ${?REFRESH_TOKEN_FAMILY_STORE_REDIS_PASSWORD}
|
|
490
|
-
}
|
|
347
|
+
redis-clients {
|
|
348
|
+
url = ${?REDIS_CLIENTS_URL}
|
|
349
|
+
password = ${?REDIS_CLIENTS_PASSWORD}
|
|
491
350
|
}
|