2018-01-15 09:44:41 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2019-08-08 18:57:28 +08:00
|
|
|
require "csrf_token_verifier"
|
|
|
|
|
2018-01-15 09:44:41 +08:00
|
|
|
# omniauth loves spending lots cycles in its magic middleware stack
|
|
|
|
# this middleware bypasses omniauth middleware and only hits it when needed
|
|
|
|
class Middleware::OmniauthBypassMiddleware
|
2019-08-12 17:55:02 +08:00
|
|
|
class AuthenticatorDisabled < StandardError
|
|
|
|
end
|
2018-01-15 09:44:41 +08:00
|
|
|
|
|
|
|
def initialize(app, options = {})
|
|
|
|
@app = app
|
|
|
|
|
2023-10-25 20:52:33 +08:00
|
|
|
OmniAuth.config.before_request_phase do |env|
|
2019-08-09 21:44:03 +08:00
|
|
|
request = ActionDispatch::Request.new(env)
|
|
|
|
|
|
|
|
# Check for CSRF token in POST requests
|
|
|
|
CSRFTokenVerifier.new.call(env) if request.request_method.downcase.to_sym != :get
|
2019-08-08 18:57:28 +08:00
|
|
|
|
2018-12-11 21:19:00 +08:00
|
|
|
# If the user is trying to reconnect to an existing account, store in session
|
|
|
|
request.session[:auth_reconnect] = !!request.params["reconnect"]
|
2021-03-31 17:23:12 +08:00
|
|
|
|
|
|
|
# If the client provided an origin, store in session to redirect back
|
|
|
|
request.session[:destination_url] = request.params["origin"]
|
2018-12-11 21:19:00 +08:00
|
|
|
end
|
2018-01-15 09:44:41 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def call(env)
|
|
|
|
if env["PATH_INFO"].start_with?("/auth")
|
2019-08-12 17:55:02 +08:00
|
|
|
begin
|
2019-08-09 21:44:03 +08:00
|
|
|
# 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]
|
|
|
|
|
2023-10-25 20:52:33 +08:00
|
|
|
omniauth =
|
|
|
|
OmniAuth::Builder.new(@app) do
|
|
|
|
Discourse.enabled_authenticators.each do |authenticator|
|
|
|
|
authenticator.register_middleware(self)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
omniauth.call(env)
|
2019-08-12 17:55:02 +08:00
|
|
|
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
|
2018-01-15 09:44:41 +08:00
|
|
|
else
|
|
|
|
@app.call(env)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|