Repository navigation
fix(service-settings): Google login saves ON when its credentials come from the server - #24874
Conversation
…e from the server The two Google credential keys in the auth settings manifest are no longer required. The runtime needs the pair to resolve, saved on the page or from GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET, and the settings service cannot see that pair, so the required flag refused google_enabled: true on every env-credential deployment. Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK Co-authored-by: Claude <noreply@anthropic.com>
…ogle-env-credentials-save
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 9 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
席位核对 · ACCEPT ·
|
Fixes #24844
Clause-②: no
What changes
auth.google_client_idandauth.google_client_secretinpackages/services/service-settings/src/manifests/auth.manifest.tsare nowrequired: false(they wererequired: true). Nothing else moves: no default,visiblerule, option, label, help text or translation bundle. Thegoogle_enabledhelp already states the conjunction (Google is offered once a client ID and a client secret both come through, saved below or set asGOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRETon the server, which win over saved values). It still reads true, so it is unchanged. A code comment above the toggle now says why the two keys are notrequired.One changeset:
@objectstack/service-settingspatch.Why
SettingsService.setManyjudgesrequiredagainst the STORED value. It reads onlyOS_*env names (envKeyOf), so theGOOGLE_*pair is invisible to it.applyEnvSocialProviderFallbacksat boot, the Google block ofbindAuthSettings). It needs the pair to be available. It never needs it stored.setMany('auth', { google_enabled: true })answeredSETTINGS_VALIDATIONwithgoogle_client_id/requiredandgoogle_client_secret/required. The TOUCH gate judges both, because theirvisiblereadsgoogle_enabled. An admin who switched Google off on the page could not switch it back on there.Route C (the resolver reports the env pair as the source) needs
settings-service.tsor a spec-level env-name declaration, and both are outside this card. See the decision record below.The offer matrix, before and after
This was measured with a one-off probe (not a repo file). It uses a real
SettingsService, the realauthmanifest frombuiltinSettingsManifests, a realAuthPluginbooted with that service assettings, andAuthManager.getPublicConfig().socialProviders. The credential values are fake. Before is the base build atdf8bb40a03. After is this branch's build at6c1589e41c. "Offered" is the providers the login page lists; the source in brackets is whose client ID the registered provider carries.requiredx2)requiredx2)Four more rows check what a cleared credential does. Their "before" column comes from an emulated base: this branch's build with
required: trueput back on the two keys in memory, which is the only behavioural difference inservice-settings. The emulation's 16 shared rows were checked against the real base build's reading above, and they match exactly.google_client_id/required)null)null)Reading. For every stored state, the providers offered are the same before and after. Only the saves that the base refused change. Three states can now be saved:
asTrimmedString). The base already reached the same offered state with anullreset, as the rows above show.Who may sign in, and which providers the login page offers, do not change for any deployment shape.
Pins
packages/services/service-settings/src/manifests/auth.manifest.test.ts, on a realSettingsServicewith the realauthmanifest:GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRETvalues.getNamespacehands plugin-authgoogle_enabled: truefrom theglobalsource and both credentials fromdefault, so plugin-auth falls back to the env pair.The plugin-auth half, which maps stored or env values to offered providers, is already pinned in
packages/plugins/plugin-auth/src/auth-plugin.test.ts:This PR does not touch plugin-auth.
Ablation (one-off, no permanent file)
required: truewas put back on both keys throughscripts/ablation-replace.mjs: the anchor hit once, and the blob changed146f1574445fto04ddcdc314d2. Thenpnpm --filter @objectstack/service-settings exec vitest run --maxWorkers=2 src/manifests/auth.manifest.test.tsran:google_client_id: expected true to be false.code: 'SETTINGS_VALIDATION'and fieldsgoogle_client_id/requiredandgoogle_client_secret/required.Tests 2 failed | 16 passed (18). The stored-credential control is among the passes.The restore ran
git checkout HEAD -- PATHon the absolute path. Afterwards the blob was146f1574445f, equal to the HEAD blob;git diff HEADwas 0 bytes;git status --porcelainshowed 0 lines. The file then ran 18 of 18 green. The test imports the manifest from source, so no rebuild was needed for this ablation.Verification (head
6c1589e41c)pnpm --workspace-concurrency=2 --filter '@objectstack/service-settings^...' --filter '@objectstack/plugin-auth...' build, exit 0. This adds plugin-auth and service-settings themselves to the dispatch's closure, for the probe. Both were rebuilt at the final head.pnpm --filter @objectstack/service-settings testgaveTest Files 54 passed (54)andTests 906 passed (906).pnpm --filter @objectstack/service-settings typecheck, exit 0.tsc --listFilesincludessrc/manifests/auth.manifest.test.ts.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 66 commands. 65 exited 0.pnpm check:dual-build-cjs-loadsexited 3 (PREREQUISITE NOT MET): it reads every package'sdist/and needs a whole-tree build, so it was NOT MEASURED locally; CI'sLint & Repo Gatesruns it.--ranprinted✓ dispatch-gates --ran: 66 derived famil(ies) accounted for — 65 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3).Decision record (options A / B / C from the card), on the four axes
PUT /api/settings/auth, which callssetMany). The reader is plugin-auth'sbindAuthSettings.content/docs/deployment/environment-variables.mdxdocuments the env pair as a supported deployment shape.OS_env name, which is new resolver surface in the 18.x C8 territory.required: truewould conclude that the credentials must be stored. That invites bootstrap code that copies the env secrets into settings.Recommendation and route taken: B.
One forward note, a reading and not measured here: objectstack-ai/objectui#13239 makes the console refuse Save while a visible
requiredfield is empty. Withrequired: truekept, that check would block every Save on the Authentication page of an env-credential deployment while Google is on, which is its default. Withrequired: false, the console and the server agree.Related
requiredas "renderer + server-side validation" objectui#13239 is the console's required check, which reads the samerequiredflag.Acceptance notes
null, andnullalready saved before this change.google_enabledhelp says it instead.df8bb40a03. The final-head reading used builds at6c1589e41c, after mergingorigin/main(which brought fix(plugin-auth): a refused OAuth application registration answers the status the refusal declared, not 500 #24853's OAuth-registration status change in plugin-auth, unrelated to the Google binding).Generated by Claude Code