discourse/config/initializers/009-omniauth.rb
Régis Hanol ced043be3c
FIX: 'destination_url' cookie handling (#33072)
Since the introduction of dedicated login and signup pages (as opposed
to modals), we've been seeing reports of issues where visitors aren't
redirected back to the "page" they were at when they initiated the
_authentication_ process.

Since we have a bazillion of ways a user might authenticate
(credentials, social logins, SSO, passkeys, discourse connect, etc...),
it's really hard to know what a change will impact.

The goal of this PR is to "simplify" the way we handle this "redirection
back to origin" by leveraging the use of a single `destination_url`
cookie set on the client-side.

The changes remove scattered cookie-setting code and consolidate the redirection logic to ensure users are properly redirected back to their original page after authentication.

- Centralized destination URL cookie management in routes and authentication flows
- Removed manual cookie setting from various components in favor of automatic handling
- Updated test scenarios to properly test the new redirection behavior
2025-08-06 10:09:01 +02:00

56 lines
2.3 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"] if request.params["origin"].present?
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
OmniAuth.config.full_host = Proc.new { Discourse.base_url_no_prefix }