Module: OmniauthOpenidFederation::Strategy::CallbackHandling

Included in:
OmniAuth::Strategies::OpenIDFederation
Defined in:
lib/omniauth_openid_federation/strategy/callback_handling.rb

Instance Method Summary collapse

Instance Method Details

#callback_phaseObject



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/omniauth_openid_federation/strategy/callback_handling.rb', line 4

def callback_phase
  # Security: Validate user input from HTTP request
  state_param_raw = request.params["state"]
  code_param_raw = request.params["code"]
  error_param_raw = request.params["error"]
  error_description_raw = request.params["error_description"]

  state_param = state_param_raw ? OmniauthOpenidFederation::Validators.sanitize_request_param(state_param_raw) : nil
  code_param = code_param_raw ? OmniauthOpenidFederation::Validators.sanitize_request_param(code_param_raw) : nil
  error_param = error_param_raw ? OmniauthOpenidFederation::Validators.sanitize_request_param(error_param_raw) : nil
  error_description_param = error_description_raw ? OmniauthOpenidFederation::Validators.sanitize_request_param(error_description_raw) : nil
  if error_param
    error_msg = "Authorization error: #{error_param}"
    error_msg += " - #{error_description_param}" if error_description_param
    OmniauthOpenidFederation::Instrumentation.notify_unexpected_authentication_break(
      stage: "callback_phase",
      error_message: error_msg,
      error_class: "AuthorizationError",
      request_info: {
        remote_ip: request.env["REMOTE_ADDR"],
        user_agent: request.env["HTTP_USER_AGENT"],
        path: request.path
      }
    )
    env["omniauth_openid_federation.instrumented"] = true
    return fail!(:authorization_error, OmniauthOpenidFederation::ValidationError.new(error_msg))
  end

  # CSRF protection: constant-time state comparison
  state_session = session["omniauth.state"]

  if OmniauthOpenidFederation::StringHelpers.blank?(state_param) ||
      state_session.nil? ||
      !OmniauthOpenidFederation::SecureCompare.secure_compare(state_param.to_s, state_session.to_s)
    # Instrument CSRF detection
    OmniauthOpenidFederation::Instrumentation.notify_csrf_detected(
      state_param: state_param ? "[PRESENT]" : "[MISSING]",
      state_session: state_session ? "[PRESENT]" : "[MISSING]",
      request_info: {
        remote_ip: request.env["REMOTE_ADDR"],
        user_agent: request.env["HTTP_USER_AGENT"],
        path: request.path
      }
    )
    # Mark as instrumented to prevent double instrumentation in fail!
    env["omniauth_openid_federation.instrumented"] = true
    return fail!(:csrf_detected, OmniauthOpenidFederation::SecurityError.new("CSRF detected"))
  end

  # Clear state from session
  session.delete("omniauth.state")

  if OmniauthOpenidFederation::StringHelpers.blank?(code_param)
    # Instrument unexpected authentication break
    OmniauthOpenidFederation::Instrumentation.notify_unexpected_authentication_break(
      stage: "callback_phase",
      error_message: "Missing authorization code",
      error_class: "ValidationError",
      request_info: {
        remote_ip: request.env["REMOTE_ADDR"],
        user_agent: request.env["HTTP_USER_AGENT"],
        path: request.path
      }
    )
    # Mark as instrumented to prevent double instrumentation in fail!
    env["omniauth_openid_federation.instrumented"] = true
    return fail!(:missing_code, OmniauthOpenidFederation::ValidationError.new("Missing authorization code"))
  end

  begin
    @access_token = exchange_authorization_code(code_param)
  rescue => e
    # Instrument unexpected authentication break
    OmniauthOpenidFederation::Instrumentation.notify_unexpected_authentication_break(
      stage: "token_exchange",
      error_message: e.message,
      error_class: e.class.name,
      request_info: {
        remote_ip: request.env["REMOTE_ADDR"],
        user_agent: request.env["HTTP_USER_AGENT"],
        path: request.path
      }
    )
    # Mark as instrumented to prevent double instrumentation in fail!
    env["omniauth_openid_federation.instrumented"] = true
    return fail!(:token_exchange_error, e)
  end

  env["omniauth.auth"] = auth_hash
  call_app!
end