mirror of
https://github.com/discourse/discourse.git
synced 2024-12-19 17:14:06 +08:00
c88303bb27
In the past we would build the stack of Omniauth providers at boot, which meant that plugins had to register any authenticators in the root of their plugin.rb (i.e. not in an `after_initialize` block). This could be frustrating because many features are not available that early in boot (e.g. Zeitwerk autoloading). Now that we build the omniauth strategy stack 'just in time', it is safe for plugins to register their auth methods in an `after_initialize` block. This commit relaxes the old restrictions so that plugin authors have the option to move things around.
68 lines
2.6 KiB
Ruby
68 lines
2.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "csrf_token_verifier"
|
|
|
|
# omniauth loves spending lots cycles in its magic middleware stack
|
|
# this middleware bypasses omniauth middleware and only hits it when needed
|
|
class Middleware::OmniauthBypassMiddleware
|
|
class AuthenticatorDisabled < StandardError
|
|
end
|
|
|
|
def initialize(app, options = {})
|
|
@app = app
|
|
|
|
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
|
|
end
|
|
|
|
def call(env)
|
|
if env["PATH_INFO"].start_with?("/auth")
|
|
begin
|
|
# When only one provider is enabled, assume it can be completely trusted, and allow GET requests
|
|
only_one_provider =
|
|
!SiteSetting.enable_local_logins && Discourse.enabled_authenticators.length == 1
|
|
OmniAuth.config.allowed_request_methods = only_one_provider ? %i[get post] : [:post]
|
|
|
|
omniauth =
|
|
OmniAuth::Builder.new(@app) do
|
|
Discourse.enabled_authenticators.each do |authenticator|
|
|
authenticator.register_middleware(self)
|
|
end
|
|
end
|
|
|
|
omniauth.call(env)
|
|
rescue AuthenticatorDisabled => e
|
|
# Authenticator is disabled, pretend it doesn't exist and pass request to app
|
|
@app.call(env)
|
|
rescue OAuth::Unauthorized => e
|
|
# 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"
|
|
Rails.logger.error "Authentication failure! request_error: #{e.class}, #{e.message}"
|
|
OmniAuth::FailureEndpoint.call(env)
|
|
rescue JWT::InvalidIatError => e
|
|
# Happens for openid-connect (including google) providers, when the server clock is wrong
|
|
env["omniauth.error.type"] ||= "invalid_iat"
|
|
Rails.logger.error "Authentication failure! invalid_iat: #{e.class}, #{e.message}"
|
|
OmniAuth::FailureEndpoint.call(env)
|
|
rescue CSRFTokenVerifier::InvalidCSRFToken => e
|
|
# Happens when CSRF token is missing from request
|
|
env["omniauth.error.type"] ||= "csrf_detected"
|
|
OmniAuth::FailureEndpoint.call(env)
|
|
end
|
|
else
|
|
@app.call(env)
|
|
end
|
|
end
|
|
end
|