mirror of
https://github.com/discourse/discourse.git
synced 2025-02-10 21:34:50 +00:00
This displays more useful messages for the most common issues we see: - CSRF (when the user switches browser) - Invalid IAT (when the server clock is wrong) - OAuth::Unauthorized for OAuth1 providers, when the credentials are incorrect This commit also stops earlier for disabled authenticators. Now we stop at the request phase, rather than the callback phase.
68 lines
2.5 KiB
Ruby
68 lines
2.5 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
|
|
|
|
Discourse.plugins.each(&:notify_before_auth)
|
|
|
|
# if you need to test this and are having ssl issues see:
|
|
# http://stackoverflow.com/questions/6756460/openssl-error-using-omniauth-specified-ssl-path-but-didnt-work
|
|
# OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE if Rails.env.development?
|
|
@omniauth = OmniAuth::Builder.new(app) do
|
|
Discourse.authenticators.each do |authenticator|
|
|
authenticator.register_middleware(self)
|
|
end
|
|
end
|
|
|
|
@omniauth.before_request_phase do |env|
|
|
# Check for CSRF token
|
|
CSRFTokenVerifier.new.call(env)
|
|
|
|
# Check whether the authenticator is enabled
|
|
if !Discourse.enabled_authenticators.any? { |a| a.name == env['omniauth.strategy'].name }
|
|
raise AuthenticatorDisabled
|
|
end
|
|
|
|
# If the user is trying to reconnect to an existing account, store in session
|
|
request = ActionDispatch::Request.new(env)
|
|
request.session[:auth_reconnect] = !!request.params["reconnect"]
|
|
end
|
|
end
|
|
|
|
def call(env)
|
|
if env["PATH_INFO"].start_with?("/auth")
|
|
begin
|
|
@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
|