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=<base64_payload>&sig=<hmac_signature>
Decoding the base64 payload reveals the email address in plain text.
Impact
- Browser history: Email is recorded in browser history
- Nginx logs: The full URL is logged in nginx access logs
- Target site logs: The relying party receives the email without explicit user consent
- 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
- Enable enable_discourse_connect_provider
- Configure discourse_connect_provider_secrets (e.g., *.example.com|secret123)
- Have a user authenticate via DiscourseConnect Provider
- 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
- Add a site setting like discourse_connect_provider_includes_email (default: true for backward compatibility) to control whether email is included in the response
- Or implement POST-based callback instead of GET 302 redirect to avoid URL logging