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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
# File 'lib/omniauth_openid_federation/strategy/authorization_request.rb', line 4
def authorize_uri
validate_provider_trust_configuration!
request_params = request.params
sanitized_params = {}
request_params.each do |key, value|
next unless value
key_str = key.to_s
next if key_str.length > 256
if value.is_a?(Array)
if value.length > 100
next
end
sanitized_array = value.map { |v| OmniauthOpenidFederation::Validators.sanitize_request_param(v) }.compact
next if sanitized_array.empty?
sanitized_params[key_str] = if key_str == "acr_values"
sanitized_array
else
sanitized_array.join(" ")
end
else
sanitized = OmniauthOpenidFederation::Validators.sanitize_request_param(value)
sanitized_params[key_str] = sanitized if sanitized
end
end
request_params = sanitized_params
if options.default_request_object_claims.is_a?(Hash)
options.default_request_object_claims.each do |key, value|
key_str = key.to_s
next if key_str.length > 256
next if OmniauthOpenidFederation::StringHelpers.blank?(value)
request_params[key_str] ||= value
end
end
if options.prepare_request_object_params.respond_to?(:call)
request_params = options.prepare_request_object_params.call(request_params.dup) || request_params
request_params = {} unless request_params.is_a?(Hash)
end
client_options_hash = options.client_options || {}
normalized_options = OmniauthOpenidFederation::Validators.normalize_hash(client_options_hash)
private_key = normalized_options[:private_key]
OmniauthOpenidFederation::Validators.validate_private_key!(private_key)
resolved_issuer = options.issuer
unless OmniauthOpenidFederation::StringHelpers.present?(resolved_issuer)
resolved_issuer = resolve_issuer_from_metadata
options.issuer = resolved_issuer if resolved_issuer
end
audience_value = resolve_audience(client_options_hash, resolved_issuer)
unless OmniauthOpenidFederation::StringHelpers.present?(audience_value)
error_msg = "Audience is required for signed request objects. " \
"Set audience option, provide entity statement with provider issuer, or configure token_endpoint"
OmniauthOpenidFederation::Logger.error("[Strategy] #{error_msg}")
raise OmniauthOpenidFederation::ConfigurationError, error_msg
end
state_value = new_state
nonce_value = options.send_nonce ? new_nonce : nil
configured_redirect_uri = normalized_options[:redirect_uri] || callback_url
client_registration_type = options.client_registration_type || :explicit
client_id_for_request = normalized_options[:identifier]
client_entity_statement = nil
if client_registration_type == :automatic
client_entity_statement = load_client_entity_statement(
options.client_entity_statement_path,
options.client_entity_statement_url
)
entity_identifier = (client_entity_statement, options.client_entity_identifier)
unless OmniauthOpenidFederation::StringHelpers.present?(entity_identifier)
error_msg = "Failed to extract entity identifier from client entity statement. " \
"Set client_entity_identifier option or ensure entity statement has 'sub' claim"
OmniauthOpenidFederation::Logger.error("[Strategy] #{error_msg}")
raise OmniauthOpenidFederation::ConfigurationError, error_msg
end
client_id_for_request = entity_identifier
if client.respond_to?(:identifier=)
client.identifier = entity_identifier
elsif client.respond_to?(:client_id=)
client.client_id = entity_identifier
end
OmniauthOpenidFederation::Logger.debug("[Strategy] Using automatic registration with entity identifier: #{entity_identifier}")
end
signing_key_source = options.signing_key_source || options.key_source || :local
jwks = normalized_options[:jwks] || normalized_options["jwks"]
validated_state = state_value.to_s.strip
validated_nonce = nonce_value&.to_s&.strip
validated_login_hint = request_params["login_hint"]
validated_ui_locales = request_params["ui_locales"]
validated_claims_locales = request_params["claims_locales"]
validated_client_id = client_id_for_request.to_s.strip
validated_redirect_uri = configured_redirect_uri.to_s.strip
validated_scope = Array(options.scope).join(" ").strip
validated_response_type = options.response_type.to_s.strip
validated_prompt = options.prompt&.to_s&.strip
validated_hd = options.hd&.to_s&.strip
validated_response_mode = options.response_mode&.to_s&.strip
validated_issuer = (resolved_issuer || options.issuer)&.to_s&.strip
validated_audience = audience_value&.to_s&.strip
normalized_acr_values = OmniauthOpenidFederation::Validators.normalize_acr_values(request_params["acr_values"], skip_sanitization: true) || nil
request_object_claims = request_params.dup
request_object_claims["acr_values"] = normalized_acr_values if normalized_acr_values
request_object_claims["ui_locales"] = validated_ui_locales if validated_ui_locales
request_object_claims["claims_locales"] = validated_claims_locales if validated_claims_locales
request_object_claims["login_hint"] = validated_login_hint if validated_login_hint
OmniauthOpenidFederation::Validators.validate_required_request_object_claims!(
request_object_claims,
options.required_request_object_claims
)
jws_builder = OmniauthOpenidFederation::Jws.new(
client_id: validated_client_id,
redirect_uri: validated_redirect_uri,
scope: validated_scope,
issuer: validated_issuer,
audience: validated_audience,
state: validated_state,
nonce: validated_nonce,
response_type: validated_response_type,
response_mode: validated_response_mode,
login_hint: validated_login_hint,
ui_locales: validated_ui_locales,
claims_locales: validated_claims_locales,
prompt: validated_prompt,
hd: validated_hd,
acr_values: normalized_acr_values,
extra_params: options. || {},
private_key: normalized_options[:private_key],
jwks: jwks,
entity_statement_path: options.entity_statement_path,
key_source: signing_key_source,
client_entity_statement: client_entity_statement
)
options.request_object_params&.each do |key|
key_str = key.to_s
next if key_str.length > 256
value = request_params[key_str]
jws_builder.add_claim(key_str.to_sym, value) if value
end
provider_metadata = load_provider_metadata_for_encryption
signed_request_object = jws_builder.sign(
provider_metadata: provider_metadata,
always_encrypt: options.always_encrypt_request_object
)
unless OmniauthOpenidFederation::StringHelpers.present?(signed_request_object)
error_msg = "Failed to generate signed request object - authentication cannot proceed without signed request"
OmniauthOpenidFederation::Logger.error("[Strategy] #{error_msg}")
raise OmniauthOpenidFederation::SecurityError, error_msg
end
auth_endpoint = client.authorization_endpoint
unless OmniauthOpenidFederation::StringHelpers.present?(auth_endpoint)
error_msg = "Authorization endpoint not configured. Provide authorization_endpoint in client_options or entity statement"
OmniauthOpenidFederation::Logger.error("[Strategy] #{error_msg}")
raise OmniauthOpenidFederation::ConfigurationError, error_msg
end
begin
uri = URI.parse(auth_endpoint)
rescue URI::InvalidURIError => e
error_msg = "Invalid authorization endpoint URI format: #{e.message}"
OmniauthOpenidFederation::Logger.error("[Strategy] #{error_msg}")
raise OmniauthOpenidFederation::ConfigurationError, error_msg
end
max_string_length = ::OmniauthOpenidFederation::Configuration.config.max_string_length
if signed_request_object.length > max_string_length
OmniauthOpenidFederation::Logger.warn("[Strategy] Request object exceeds maximum length")
end
uri.query = URI.encode_www_form(request: signed_request_object)
uri.to_s
end
|