Skip to content

Fix Authorization page leaking OAuth secrets via DOM and event logs - #742

Merged
ddon merged 1 commit into
BeamLabEU:mainfrom
timujinne:fix/authorization-secret-dom-log-exposure
Aug 21, 2026
Merged

Fix Authorization page leaking OAuth secrets via DOM and event logs#742
ddon merged 1 commit into
BeamLabEU:mainfrom
timujinne:fix/authorization-secret-dom-log-exposure

Conversation

@timujinne

Copy link
Copy Markdown
Contributor

Problem

authorization.html.heex rendered the real Google/GitHub/Facebook OAuth client secrets into value= on type="password" inputs. type="password" only masks the on-screen rendering — the real value sits in the HTML value= attribute in plaintext, visible via view-source or DevTools to anyone who can load the page.

Because the DOM echoed the real secret back, editing any other field on the page re-submitted it as part of the validate_settings phx-change event's full-form params (LiveView sends the whole form on every change, not just the changed field), and Phoenix's built-in LiveView event telemetry logger then wrote those params to the application log.

Neither leak is closable via filter_parameters: that's a host-app Application env key, and a dependency's own config/config.exs isn't merged into a consuming app at boot, so this can't be fixed from phoenix_kit's own config. The only real fix is root-cause: never let the real secret round-trip through the DOM/event params in the first place.

This is a different problem from #741 — that fix was about not loading the secrets into General/Users assigns unnecessarily. The Authorization page legitimately needs the real values (it manages OAuth credentials, and test_oauth uses them to test the live connection), so the fix here is safe output, not a key filter.

Fix

  • The three OAuth secret inputs (Google/GitHub/Facebook client secrets) now render value="" unconditionally instead of the real stored value. A placeholder communicates whether a secret is already configured, without ever showing it. Client ID / App ID fields are untouched — those are intentionally public.
  • @settings still holds the real secret values at all times, so test_oauth keeps working unchanged.
  • validate_settings and save_settings now run incoming params through preserve_unset_secrets/2: a blank incoming value for one of the three secret keys falls back to the currently-held real value instead of overwriting/logging it; a non-blank value (an admin actively typing a new secret) always wins. This also fixes save_settings silently wiping a stored secret whenever the form was submitted without retyping it.

Verified

  • New test suite (test/integration/phoenix_kit_web/live/settings/authorization_secret_leak_test.exs, 4 tests, 0 failures):
    • the real secret is absent from both the initial and live-rendered HTML for all three providers — confirmed by asserting it's not present in the actual page source, not by checking that the field displays masked;
    • editing an unrelated field does not leak the secret into either the rendered HTML or ExUnit.CaptureLog-captured logs;
    • saving with the secret fields left blank preserves all three stored secrets (the "don't break the scenario" check — an admin must still be able to tell a secret is configured and must not have it wiped by an untouched save);
    • saving with a newly typed value updates the stored secret (an admin must still be able to set a new one).
  • Regression: broader settings suite, 116 tests, 0 failures.
  • mix quality (format, credo --strict, dialyzer): clean.

authorization.html.heex rendered the real Google/GitHub/Facebook OAuth
client secrets into value= on type="password" inputs - masked on
screen only, plaintext in the HTML source (view-source/DevTools).
Because the DOM echoed the real secret, editing any other field on the
page re-submitted it as part of the validate_settings phx-change
event's full-form params, which Phoenix's built-in LiveView event
telemetry logger then wrote to the application log. Neither leak is
closable via filter_parameters: that's a host-app Application env key,
and a dependency's own config isn't merged into a consuming app at
boot, so phoenix_kit cannot fix this from a config file.

The three secret inputs now render value="" unconditionally, with a
placeholder that signals whether a secret is already configured
without ever showing it. Client ID/App ID fields are untouched - they
are intentionally public.

authorization.ex keeps @settings holding the real secret values at all
times (test_oauth still reads them to test the live connection), but
validate_settings and save_settings now run incoming params through a
new preserve_unset_secrets/2 helper first: a blank incoming secret
falls back to the currently-held value instead of overwriting/logging
it, while a non-blank value (an admin actively typing a new secret)
always wins. This also fixes save_settings silently wiping a stored
secret whenever the form was submitted without retyping it.
@ddon
ddon merged commit 36e9ed2 into BeamLabEU:main Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants