Security/Privacy concern: Email exposed in DiscourseConnect Provider redirect URL

Describe the bug

When using Discourse as a DiscourseConnect Provider (SSO Provider), the user's email address is exposed in the 302 redirect URL to the relying party. This happens because the populate_user_data method in lib/second_factor/actions/discourse_connect_provider.rb always sets the email:

 def populate_user_data(sso) sso.name = current_user.name sso.username = current_user.username sso.email = current_user.email # <-- Always included sso.external_id = current_user.id.to_s # ... end

This email is thenBase64-encoded and included in the redirect URL:https://target-site.com/callback?sso=&sig=

Decoding the base64 payload reveals the email address in plain text.

Impact

  1. Browser history: Email is recorded in browser history
  2. Nginx logs: The full URL is logged in nginx access logs
  3. Target site logs: The relying party receives the email without explicit user consent
  4. User expectation: Users typically authorize to prove "I am a legitimate user" - they don't expect their email to be shared

Expected behavior

Users should be able to control whether their email is shared with the relying party. There should be a configuration option similar to discourse_connect_overrides_groups, discourse_connect_overrides_avatar, etc.

Currently there is no site setting to disable this behavior. Writing a plugin to override this behavior is possible but not ideal.

Reproduction steps

  1. Enable enable_discourse_connect_provider
  2. Configure discourse_connect_provider_secrets (e.g., *.example.com|secret123)
  3. Have a user authenticate via DiscourseConnect Provider
  4. Check the 302 redirect URL - email is visible in the URL parameters

The redirect URL looks like:https://relying-party.com/sso?sso=bm9uY2U9xxx&sig=xxx

Decoding sso parameter reveals:nonce=xxx&return_sso_url=xxx&email=user@example.com&external_id=123

Environment

  • Discourse version: (latest)
  • Self-hosted

Possible fix suggestions

  1. Add a site setting like discourse_connect_provider_includes_email (default: true for backward compatibility) to control whether email is included in the response
  2. Or implement POST-based callback instead of GET 302 redirect to avoid URL logging


Discuss this on our forum.