discourse/config/initializers/009-omniauth.rb
Régis Hanol 0de08e780b
UX: better error message when social login fails (#32772)
This improves the error message(s) displayed when an error happens while
using a social login to log in / sign up into a Discourse community.

Unfortunately, we can't be super precise in the reason behind the error
(it can be the user clicked "cancel" during the authorization phase, or
any of the plethora of other possible errors) because the reason isn't
provided (either for security reasons, or because it's just hard to do
so in a reliable & consistent way accross all social logins).

The best I could do was to

- add the name of the "social login" provider in the error message
- tweak the copy a bit for the different cases handle
- fix the CSS/HTML to match the one used by the main application

In order to show the name of the provider, we use either the "provider"
or the "strategy" query parameter, or use the name of the authenticator
(if it's the only one enabled).

Internal ref - t/153662

---

**BEFORE**


![before](https://github.com/user-attachments/assets/67579a3e-5a61-4e8f-aa72-4dc375547e39)

**AFTER**


![after](https://github.com/user-attachments/assets/954f9c8a-e865-44ff-b1e3-841393a26edf)
2025-05-20 16:22:38 +02:00

54 lines
2.2 KiB
Ruby

# frozen_string_literal: true
require "middleware/omniauth_bypass_middleware"
Rails.application.config.middleware.use Middleware::OmniauthBypassMiddleware
OmniAuth.config.logger = Rails.logger
OmniAuth.config.silence_get_warning = true
# uncomment this line to force the redirect to /auth/failure in development mode
# (by default, omniauth raises an exception in development mode)
# OmniAuth.config.failure_raise_out_environments = []
OmniAuth.config.request_validation_phase = nil # We handle CSRF checks in before_request_phase
OmniAuth.config.before_request_phase do |env|
request = ActionDispatch::Request.new(env)
# Check for CSRF token in POST requests
CSRFTokenVerifier.new.call(env) if request.request_method.downcase.to_sym != :get
# If the user is trying to reconnect to an existing account, store in session
request.session[:auth_reconnect] = !!request.params["reconnect"]
# If the client provided an origin, store in session to redirect back
request.session[:destination_url] = request.params["origin"]
end
OmniAuth.config.on_failure do |env|
exception = env["omniauth.error"]
# OmniAuth 2 doesn't give us any way to know for sure whether a failure was due to an
# explicit fail! call, or a rescued exception. But, this check is a pretty good guess:
is_rescued_error = exception&.message&.to_sym == env["omniauth.error.type"]
next OmniAuth::FailureEndpoint.call(env) if !is_rescued_error # let the default behavior handle it
case exception
when OAuth::Unauthorized
# OAuth1 (i.e. Twitter) makes a web request during the setup phase
# If it fails, Omniauth does not handle the error. Handle it here
env["omniauth.error.type"] = "request_error"
when JWT::InvalidIatError
# Happens for openid-connect (including google) providers, when the server clock is wrong
env["omniauth.error.type"] = "invalid_iat"
when CSRFTokenVerifier::InvalidCSRFToken
# Happens when CSRF token is missing from request
env["omniauth.error.type"] = "csrf_detected"
else
# default omniauth behavior is to redirect to /auth/failure with error.message in the URL
# We don't want to leak that kind of unhandled exception info, so re-raise it
raise exception
end
OmniAuth::FailureEndpoint.call(env)
end