# frozen_string_literal: true class OpenIDConnectAuthenticator < Auth::ManagedAuthenticator def name 'oidc' end def can_revoke? SiteSetting.openid_connect_allow_association_change end def can_connect_existing_user? SiteSetting.openid_connect_allow_association_change end def enabled? SiteSetting.openid_connect_enabled end def primary_email_verified?(auth) supplied_verified_boolean = auth['extra']['raw_info']['email_verified'] # If the payload includes the email_verified boolean, use it. Otherwise assume true if supplied_verified_boolean.nil? true else # Many providers violate the spec, and send this as a string rather than a boolean supplied_verified_boolean == true || supplied_verified_boolean == 'true' end end def always_update_user_email? SiteSetting.openid_connect_overrides_email end def discovery_document document_url = SiteSetting.openid_connect_discovery_document.presence if !document_url oidc_log("No discovery document URL specified", error: true) return end from_cache = true result = Discourse.cache.fetch("openid-connect-discovery-#{document_url}", expires_in: 10.minutes) do from_cache = false oidc_log("Fetching discovery document from #{document_url}") connection = Faraday.new { |c| c.use Faraday::Response::RaiseError } JSON.parse(connection.get(document_url).body) rescue Faraday::Error, JSON::ParserError => e oidc_log("Fetching discovery document raised error #{e.class} #{e.message}", error: true) nil end oidc_log("Discovery document loaded from cache") if from_cache oidc_log("Discovery document is\n\n#{result.to_yaml}") result end def oidc_log(message, error: false) if error Rails.logger.error("OIDC Log: #{message}") elsif SiteSetting.openid_connect_verbose_logging Rails.logger.warn("OIDC Log: #{message}") end end def register_middleware(omniauth) omniauth.provider :openid_connect, name: :oidc, error_handler: lambda { |error, message| handlers = SiteSetting.openid_connect_error_redirects.split("\n") handlers.each do |row| parts = row.split("|") return parts[1] if message.include? parts[0] end nil }, verbose_logger: lambda { |message| oidc_log(message) }, setup: lambda { |env| opts = env['omniauth.strategy'].options token_params = {} token_params[:scope] = SiteSetting.openid_connect_token_scope if SiteSetting.openid_connect_token_scope.present? opts.deep_merge!( client_id: SiteSetting.openid_connect_client_id, client_secret: SiteSetting.openid_connect_client_secret, discovery_document: discovery_document, scope: SiteSetting.openid_connect_authorize_scope, token_params: token_params, passthrough_authorize_options: SiteSetting.openid_connect_authorize_parameters.split("|"), claims: SiteSetting.openid_connect_claims ) if SiteSetting.openid_connect_verbose_logging opts[:client_options][:connection_build] = lambda { |builder| builder.response :logger, Rails.logger, { bodies: true, formatter: OIDCFaradayFormatter } # Default stack: builder.request :url_encoded # form-encode POST params builder.adapter Faraday.default_adapter # make requests with Net::HTTP } end } end end