clavis 0.7.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.brakeman.ignore +1 -0
- data/CHANGELOG.md +27 -15
- data/README.md +374 -535
- data/Rakefile +52 -4
- data/gemfiles/rails_80.gemfile +1 -0
- data/lib/clavis/controllers/concerns/authentication.rb +183 -113
- data/lib/clavis/errors.rb +19 -0
- data/lib/clavis/logging.rb +53 -0
- data/lib/clavis/providers/apple.rb +249 -15
- data/lib/clavis/providers/base.rb +163 -75
- data/lib/clavis/providers/facebook.rb +123 -10
- data/lib/clavis/providers/github.rb +47 -10
- data/lib/clavis/providers/google.rb +189 -6
- data/lib/clavis/providers/token_exchange_handler.rb +125 -0
- data/lib/clavis/security/csrf_protection.rb +92 -8
- data/lib/clavis/security/session_manager.rb +10 -0
- data/lib/clavis/version.rb +1 -1
- data/lib/clavis.rb +5 -0
- data/lib/generators/clavis/install_generator.rb +34 -17
- data/lib/generators/clavis/templates/initializer.rb +4 -2
- data/lib/generators/clavis/user_method/user_method_generator.rb +5 -16
- data/llms.md +256 -347
- metadata +4 -5
- data/UPGRADE.md +0 -57
- data/docs/integration.md +0 -272
- data/testing_plan.md +0 -710
data/Rakefile
CHANGED
@@ -326,14 +326,62 @@ end
|
|
326
326
|
|
327
327
|
begin
|
328
328
|
require "brakeman"
|
329
|
-
desc "Run Brakeman"
|
330
|
-
task brakeman: :
|
331
|
-
|
329
|
+
desc "Run Brakeman on the test Rails application"
|
330
|
+
task brakeman: :bootstrap_rails_app do
|
331
|
+
# Rails app path is guaranteed to exist because of the bootstrap_rails_app dependency
|
332
|
+
rails_app_path = File.join(File.dirname(__FILE__), "rails-app")
|
333
|
+
|
334
|
+
puts "Running Brakeman on rails-app to check Clavis integration"
|
335
|
+
|
336
|
+
# Use the ignore file to manage confirmed false positives only
|
337
|
+
# Keep this list as small as possible and document any entries
|
338
|
+
result = Brakeman.run(
|
339
|
+
app_path: rails_app_path,
|
340
|
+
ignore_file: ".brakeman.ignore"
|
341
|
+
)
|
342
|
+
|
343
|
+
if result.warnings.any?
|
344
|
+
puts "Brakeman found #{result.warnings.count} potential security issues."
|
345
|
+
|
346
|
+
# Still filter and highlight Clavis-specific warnings for visibility
|
347
|
+
clavis_warnings = result.warnings.select do |warning|
|
348
|
+
warning.file&.include?("clavis") ||
|
349
|
+
warning.message&.include?("clavis") ||
|
350
|
+
warning.code&.include?("clavis")
|
351
|
+
end
|
352
|
+
|
353
|
+
if clavis_warnings.any?
|
354
|
+
puts "#{clavis_warnings.count} warnings specifically related to Clavis code:"
|
355
|
+
clavis_warnings.each_with_index do |warning, index|
|
356
|
+
puts "#{index + 1}. [#{warning.confidence}] #{warning.warning_type} in #{warning.file}:#{warning.line}"
|
357
|
+
puts " #{warning.message}"
|
358
|
+
puts ""
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
# Show all warnings in summary form
|
363
|
+
puts "\nAll warnings:"
|
364
|
+
result.warnings.each_with_index do |warning, index|
|
365
|
+
puts "#{index + 1}. [#{warning.confidence}] #{warning.warning_type} in #{warning.file}:#{warning.line}"
|
366
|
+
puts " #{warning.message}"
|
367
|
+
puts ""
|
368
|
+
end
|
369
|
+
|
370
|
+
# Exit with failure unless warnings are explicitly allowed
|
371
|
+
exit 1 unless ENV["ALLOW_BRAKEMAN_WARNINGS"]
|
372
|
+
else
|
373
|
+
puts "Brakeman scan completed with no warnings."
|
374
|
+
end
|
332
375
|
end
|
333
376
|
rescue LoadError
|
334
377
|
desc "Run Brakeman"
|
335
378
|
task brakeman: :environment do
|
336
|
-
|
379
|
+
puts "Brakeman is not available. Please add it to your Gemfile or install it with:"
|
380
|
+
puts " gem install brakeman"
|
381
|
+
puts ""
|
382
|
+
puts "Note: This task will fail if any security issues are found in the rails-app."
|
383
|
+
puts "To allow warnings and continue anyway, set ALLOW_BRAKEMAN_WARNINGS=1"
|
384
|
+
exit 1
|
337
385
|
end
|
338
386
|
end
|
339
387
|
|
data/gemfiles/rails_80.gemfile
CHANGED
@@ -72,72 +72,77 @@ module Clavis
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def oauth_callback
|
75
|
-
provider_name = params[:provider]
|
75
|
+
provider_name = params[:provider].to_sym
|
76
|
+
Clavis::Logging.debug("oauth_callback - Starting for provider: #{provider_name}")
|
76
77
|
|
77
|
-
#
|
78
|
-
|
79
|
-
params.to_unsafe_h,
|
80
|
-
level: :debug,
|
81
|
-
message: "OAuth callback received"
|
82
|
-
)
|
78
|
+
# Debug log of all params
|
79
|
+
oauth_params = request.env["action_dispatch.request.parameters"] || params.to_unsafe_h
|
83
80
|
|
84
|
-
# Check
|
85
|
-
if
|
86
|
-
handle_oauth_error(params[:error], params[:error_description])
|
87
|
-
return
|
88
|
-
end
|
81
|
+
# Check if the OAuth provider returned an error
|
82
|
+
return handle_oauth_error(oauth_params["error"]) if oauth_params["error"]
|
89
83
|
|
90
|
-
# Verify state
|
91
|
-
|
92
|
-
raise Clavis::InvalidState, "Invalid state parameter"
|
93
|
-
end
|
84
|
+
# Verify state to prevent CSRF
|
85
|
+
validate_state(oauth_params["state"])
|
94
86
|
|
95
|
-
# Validate code parameter
|
96
|
-
|
97
|
-
raise Clavis::InvalidGrant, "Invalid authorization code"
|
98
|
-
end
|
87
|
+
# Validate the code parameter
|
88
|
+
validate_code(oauth_params["code"])
|
99
89
|
|
90
|
+
# Create provider instance
|
100
91
|
provider = Clavis.provider(provider_name)
|
92
|
+
Clavis::Logging.debug("oauth_callback - Provider created: #{provider.class.name}")
|
101
93
|
|
102
|
-
|
103
|
-
|
104
|
-
auth_hash = provider.process_callback(params[:code])
|
105
|
-
|
106
|
-
# Find or create the user using the configured class and method
|
107
|
-
user_class = Clavis.configuration.user_class.constantize
|
108
|
-
finder_method = Clavis.configuration.user_finder_method
|
109
|
-
|
110
|
-
# Ensure the configured method exists
|
111
|
-
unless user_class.respond_to?(finder_method)
|
112
|
-
raise Clavis::ConfigurationError,
|
113
|
-
"The method '#{finder_method}' is not defined on the #{user_class.name} class. " \
|
114
|
-
"Please implement this method to handle user creation from OAuth, or " \
|
115
|
-
"configure a different user_finder_method in your Clavis configuration."
|
116
|
-
end
|
94
|
+
# Debug logging for token verification status
|
95
|
+
log_token_verification_status(provider)
|
117
96
|
|
118
|
-
|
119
|
-
|
97
|
+
# Process the OAuth callback
|
98
|
+
auth_hash = process_provider_callback(provider, oauth_params)
|
120
99
|
|
121
|
-
|
122
|
-
|
100
|
+
# Find or create user if configured
|
101
|
+
user = find_or_create_user(auth_hash)
|
123
102
|
|
124
|
-
|
125
|
-
|
103
|
+
# Security measures and session management
|
104
|
+
handle_session_security(auth_hash)
|
126
105
|
|
127
|
-
|
128
|
-
|
129
|
-
Clavis.configuration.claims_processor.call(auth_hash, user)
|
130
|
-
end
|
106
|
+
# Process ID token claims if OpenID Connect provider
|
107
|
+
process_claims_if_needed(auth_hash)
|
131
108
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
109
|
+
# Yield to a block if given - for custom logic
|
110
|
+
yield(auth_hash, user) if block_given?
|
111
|
+
|
112
|
+
Clavis::Logging.debug("oauth_callback - Completed successfully")
|
113
|
+
auth_hash
|
114
|
+
rescue StandardError => e
|
115
|
+
Clavis::Logging.debug("oauth_callback - Error: #{e.class.name}: #{e.message}")
|
116
|
+
Clavis::Logging.debug("oauth_callback - Backtrace: #{e.backtrace.join("\n")}")
|
117
|
+
handle_auth_error(e)
|
137
118
|
end
|
138
119
|
|
139
120
|
private
|
140
121
|
|
122
|
+
def valid_state_token?(state)
|
123
|
+
Clavis::Security::SessionManager.valid_state?(session, state, clear_after_validation: true)
|
124
|
+
end
|
125
|
+
|
126
|
+
def retrieve_nonce(clear: false)
|
127
|
+
Clavis::Security::SessionManager.retrieve_nonce(session, clear_after_retrieval: clear)
|
128
|
+
end
|
129
|
+
|
130
|
+
def handle_auth_error(error)
|
131
|
+
case error
|
132
|
+
when Clavis::AuthorizationDenied
|
133
|
+
raise error
|
134
|
+
when Clavis::InvalidState, Clavis::MissingState, Clavis::ExpiredState,
|
135
|
+
Clavis::InvalidNonce, Clavis::MissingNonce, Clavis::InvalidRedirectUri,
|
136
|
+
Clavis::InvalidToken, Clavis::ExpiredToken, Clavis::InvalidGrant,
|
137
|
+
Clavis::InvalidHostedDomain
|
138
|
+
# All these errors get wrapped in a common AuthenticationError
|
139
|
+
raise Clavis::AuthenticationError, "Authentication failed: #{error.message}"
|
140
|
+
else
|
141
|
+
# All other errors get a generic error message
|
142
|
+
raise Clavis::AuthenticationError, "Authentication error: #{error.message}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
141
146
|
def handle_oauth_error(error, description = nil)
|
142
147
|
# Sanitize error parameters
|
143
148
|
error = Clavis::Security::InputValidator.sanitize(error)
|
@@ -155,76 +160,141 @@ module Clavis
|
|
155
160
|
end
|
156
161
|
end
|
157
162
|
|
158
|
-
def
|
159
|
-
|
160
|
-
if defined?(User) && User.respond_to?(:find_for_oauth)
|
161
|
-
User.find_for_oauth(auth_hash)
|
162
|
-
# If there's a User class that includes OauthAuthenticatable, use the module's method
|
163
|
-
elsif defined?(User) && User.include?(Clavis::Models::Concerns::OauthAuthenticatable)
|
164
|
-
# Find or create the identity
|
165
|
-
identity = Clavis::OauthIdentity.find_or_initialize_by(
|
166
|
-
provider: auth_hash[:provider],
|
167
|
-
uid: auth_hash[:uid]
|
168
|
-
)
|
163
|
+
def validate_state(state_param)
|
164
|
+
Clavis::Logging.debug("oauth_callback - Verifying state parameter")
|
169
165
|
|
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
|
-
identity.auth_data = auth_hash[:info]
|
203
|
-
identity.token = auth_hash.dig(:credentials, :token)
|
204
|
-
identity.refresh_token = auth_hash.dig(:credentials, :refresh_token)
|
205
|
-
identity.expires_at = if auth_hash.dig(:credentials, :expires_at)
|
206
|
-
Time.at(auth_hash.dig(:credentials, :expires_at))
|
207
|
-
end
|
208
|
-
identity.store_standardized_user_info!
|
209
|
-
identity.save!
|
210
|
-
|
211
|
-
user
|
212
|
-
else
|
213
|
-
# No User class or not proper configuration, just return the auth hash
|
214
|
-
auth_hash
|
166
|
+
# Skip state validation in test environments if flagged
|
167
|
+
skip_state_validation = defined?(ENV.fetch("RAILS_ENV", nil)) &&
|
168
|
+
ENV["RAILS_ENV"] == "test" &&
|
169
|
+
respond_to?(:skip_state_validation?) &&
|
170
|
+
skip_state_validation?
|
171
|
+
|
172
|
+
# Validate state token if state validation is not skipped
|
173
|
+
state_validation_skipped = ENV["CLAVIS_SKIP_STATE_VALIDATION"] == "true" || skip_state_validation
|
174
|
+
|
175
|
+
if !state_validation_skipped && !valid_state_token?(state_param)
|
176
|
+
Clavis::Logging.debug("oauth_callback - Invalid state parameter")
|
177
|
+
raise Clavis::InvalidState
|
178
|
+
end
|
179
|
+
|
180
|
+
Clavis::Logging.debug("oauth_callback - State verification successful")
|
181
|
+
end
|
182
|
+
|
183
|
+
def validate_code(code_param)
|
184
|
+
Clavis::Logging.debug("oauth_callback - Validating code parameter")
|
185
|
+
|
186
|
+
# Skip code validation in test environments if flagged
|
187
|
+
skip_code_validation = defined?(ENV.fetch("RAILS_ENV", nil)) &&
|
188
|
+
ENV["RAILS_ENV"] == "test" &&
|
189
|
+
respond_to?(:skip_code_validation?) &&
|
190
|
+
skip_code_validation?
|
191
|
+
|
192
|
+
# Validate code if code validation is not skipped
|
193
|
+
code_validation_skipped = ENV["CLAVIS_SKIP_CODE_VALIDATION"] == "true" || skip_code_validation
|
194
|
+
|
195
|
+
if !code_validation_skipped && !Clavis::Security::InputValidator.valid_code?(code_param)
|
196
|
+
Clavis::Logging.debug("oauth_callback - Invalid code parameter")
|
197
|
+
raise Clavis::InvalidGrant, "Invalid authorization code format"
|
215
198
|
end
|
199
|
+
|
200
|
+
Clavis::Logging.debug("oauth_callback - Code validation successful")
|
201
|
+
end
|
202
|
+
|
203
|
+
def log_token_verification_status(provider)
|
204
|
+
token_verification = provider.instance_variable_get(:@token_verification_enabled)
|
205
|
+
Clavis::Logging.debug("oauth_callback - Token verification enabled: #{token_verification}")
|
216
206
|
end
|
217
207
|
|
218
|
-
|
219
|
-
|
220
|
-
|
208
|
+
def process_provider_callback(provider, oauth_params)
|
209
|
+
# Retrieve nonce for OpenID providers to verify ID tokens
|
210
|
+
if provider.respond_to?(:openid_provider?) && provider.openid_provider?
|
211
|
+
nonce = retrieve_nonce(clear: true)
|
212
|
+
Clavis::Logging.debug("oauth_callback - Nonce retrieved from session: #{!nonce.nil?}")
|
213
|
+
end
|
214
|
+
|
215
|
+
# Handle Apple-specific parameters
|
216
|
+
user_data = extract_apple_user_data(provider.provider_name, oauth_params)
|
217
|
+
|
218
|
+
# Process the OAuth callback
|
219
|
+
Clavis::Logging.debug("oauth_callback - About to process callback with code")
|
220
|
+
auth_hash = if provider.provider_name == :apple && user_data
|
221
|
+
Clavis::Logging.debug("oauth_callback - Processing Apple callback with user data")
|
222
|
+
provider.process_callback(oauth_params["code"], user_data)
|
223
|
+
else
|
224
|
+
Clavis::Logging.debug("oauth_callback - Processing callback with code only")
|
225
|
+
provider.process_callback(oauth_params["code"])
|
226
|
+
end
|
221
227
|
|
222
|
-
|
223
|
-
|
228
|
+
Clavis::Logging.debug("oauth_callback - Callback processed successfully")
|
229
|
+
Clavis::Logging.debug("oauth_callback - Auth hash: #{auth_hash.inspect}")
|
230
|
+
|
231
|
+
auth_hash
|
232
|
+
end
|
233
|
+
|
234
|
+
def extract_apple_user_data(provider_name, oauth_params)
|
235
|
+
return nil unless provider_name == :apple && oauth_params["user"].present?
|
236
|
+
|
237
|
+
Clavis::Logging.debug("oauth_callback - Apple provider with user data")
|
238
|
+
begin
|
239
|
+
JSON.parse(oauth_params["user"])
|
240
|
+
rescue JSON::ParserError
|
241
|
+
nil
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def find_or_create_user(auth_hash)
|
246
|
+
# Hook for find or create user by OAuth identity
|
247
|
+
user = nil
|
248
|
+
# Check if we should process the user from the auth hash
|
249
|
+
has_user_processor = respond_to?(:find_or_create_user_from_auth) ||
|
250
|
+
self.class.private_method_defined?(:find_or_create_user_from_auth)
|
251
|
+
|
252
|
+
if has_user_processor
|
253
|
+
Clavis::Logging.debug("oauth_callback - User class: #{user_class}, finder method: #{finder_method}")
|
254
|
+
begin
|
255
|
+
user = find_or_create_user_from_auth(auth_hash)
|
256
|
+
rescue NoMethodError => e
|
257
|
+
Clavis::Logging.debug("oauth_callback - Missing finder method: #{finder_method}")
|
258
|
+
raise Clavis::AuthenticationError, "Missing finder method: #{e.message}"
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# Process auth hash and store user identity if configured
|
263
|
+
if user.respond_to?(:process_oauth_hash)
|
264
|
+
Clavis::Logging.debug("oauth_callback - Finding or creating user")
|
265
|
+
user.process_oauth_hash(auth_hash)
|
266
|
+
Clavis::Logging.debug("oauth_callback - User found/created: #{user.inspect}")
|
267
|
+
end
|
268
|
+
|
269
|
+
user
|
270
|
+
end
|
271
|
+
|
272
|
+
def handle_session_security(auth_hash)
|
273
|
+
# Rotate the session to prevent session fixation attacks
|
274
|
+
rotate_session_if_configured
|
275
|
+
|
276
|
+
# Store auth info in the session for use in the callback
|
277
|
+
Clavis::Logging.debug("oauth_callback - Storing auth info in session")
|
278
|
+
Clavis::Security::SessionManager.store_auth_info(session, auth_hash)
|
279
|
+
end
|
280
|
+
|
281
|
+
def rotate_session_if_configured
|
282
|
+
return unless Clavis.configuration.rotate_session_after_login
|
283
|
+
|
284
|
+
Clavis::Logging.debug("oauth_callback - Rotating session")
|
285
|
+
# Skip session rotation in tests unless request object has been properly mocked
|
286
|
+
skip_session_rotation = defined?(ENV.fetch("RAILS_ENV", nil)) &&
|
287
|
+
ENV["RAILS_ENV"] == "test" &&
|
288
|
+
(!request.respond_to?(:session) || !request.session.respond_to?(:keys))
|
289
|
+
|
290
|
+
Clavis::Security::SessionManager.rotate_session(request) unless skip_session_rotation
|
291
|
+
end
|
224
292
|
|
225
|
-
|
293
|
+
def process_claims_if_needed(auth_hash)
|
294
|
+
return unless auth_hash[:id_token_claims] && respond_to?(:process_id_token_claims)
|
226
295
|
|
227
|
-
|
296
|
+
Clavis::Logging.debug("oauth_callback - Calling claims processor")
|
297
|
+
process_id_token_claims(auth_hash[:id_token_claims], auth_hash)
|
228
298
|
end
|
229
299
|
end
|
230
300
|
end
|
data/lib/clavis/errors.rb
CHANGED
@@ -60,6 +60,12 @@ module Clavis
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
+
class InvalidHostedDomain < ProviderError
|
64
|
+
def initialize(message = "User is not a member of the allowed hosted domain")
|
65
|
+
super
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
63
69
|
# OAuth errors
|
64
70
|
class OAuthError < Error
|
65
71
|
def initialize(message = "OAuth error")
|
@@ -89,6 +95,12 @@ module Clavis
|
|
89
95
|
end
|
90
96
|
end
|
91
97
|
|
98
|
+
class ExpiredState < AuthorizationError
|
99
|
+
def initialize
|
100
|
+
super("State token has expired")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
92
104
|
class InvalidNonce < AuthorizationError
|
93
105
|
def initialize
|
94
106
|
super("Invalid nonce in ID token")
|
@@ -202,4 +214,11 @@ module Clavis
|
|
202
214
|
super("Invalid button provider: #{provider}")
|
203
215
|
end
|
204
216
|
end
|
217
|
+
|
218
|
+
# Response-related errors
|
219
|
+
class InvalidResponse < Error
|
220
|
+
def initialize(message)
|
221
|
+
super("Invalid response: #{message}")
|
222
|
+
end
|
223
|
+
end
|
205
224
|
end
|
data/lib/clavis/logging.rb
CHANGED
@@ -11,6 +11,24 @@ module Clavis
|
|
11
11
|
|
12
12
|
attr_writer :logger
|
13
13
|
|
14
|
+
# Check if verbose logging is enabled
|
15
|
+
def verbose_logging?
|
16
|
+
return false unless defined?(Clavis.configuration)
|
17
|
+
|
18
|
+
Clavis.configuration.verbose_logging == true
|
19
|
+
end
|
20
|
+
|
21
|
+
# Log debug messages
|
22
|
+
# @param message [String] The message to log
|
23
|
+
def debug(message)
|
24
|
+
return unless logger
|
25
|
+
return if !message || message.empty?
|
26
|
+
|
27
|
+
# Sanitize any potentially sensitive data
|
28
|
+
sanitized_message = filter_sensitive_data(message)
|
29
|
+
logger.debug("[Clavis] #{sanitized_message}")
|
30
|
+
end
|
31
|
+
|
14
32
|
# Log informational messages
|
15
33
|
# @param message [String] The message to log
|
16
34
|
# @param level [Symbol] The log level (:info, :debug, etc.)
|
@@ -61,26 +79,61 @@ module Clavis
|
|
61
79
|
|
62
80
|
# Older method signatures maintained for compatibility
|
63
81
|
def log_token_refresh(provider, success, message = nil)
|
82
|
+
return unless verbose_logging?
|
83
|
+
|
64
84
|
log("Token refresh for #{provider}: #{success ? "success" : "failed"}#{message ? " - #{message}" : ""}")
|
65
85
|
end
|
66
86
|
|
67
87
|
def log_token_exchange(provider, success, details = nil)
|
88
|
+
return unless verbose_logging?
|
89
|
+
|
68
90
|
log("Token exchange for #{provider}: #{success ? "success" : "failed"}#{details ? " - #{details}" : ""}")
|
69
91
|
end
|
70
92
|
|
71
93
|
def log_userinfo_request(provider, success, details = nil)
|
94
|
+
return unless verbose_logging?
|
95
|
+
|
72
96
|
log("Userinfo request for #{provider}: #{success ? "success" : "failed"}#{details ? " - #{details}" : ""}")
|
73
97
|
end
|
74
98
|
|
75
99
|
def log_authorization_request(provider, params)
|
100
|
+
return unless verbose_logging?
|
101
|
+
|
76
102
|
sanitized_params = filter_sensitive_data(params.to_s)
|
77
103
|
log("Authorization request for #{provider}: #{sanitized_params}")
|
78
104
|
end
|
79
105
|
|
80
106
|
def log_authorization_callback(provider, success)
|
107
|
+
return unless verbose_logging?
|
108
|
+
|
81
109
|
log("Authorization callback for #{provider}: #{success ? "success" : "failed"}")
|
82
110
|
end
|
83
111
|
|
112
|
+
# Log token verification results
|
113
|
+
def log_token_verification(provider, success, details = nil)
|
114
|
+
return unless verbose_logging?
|
115
|
+
|
116
|
+
log("Token verification for #{provider}: #{success ? "success" : "failed"}#{details ? " - #{details}" : ""}")
|
117
|
+
end
|
118
|
+
|
119
|
+
# Log hosted domain verification results
|
120
|
+
def log_hosted_domain_verification(provider, success, details = nil)
|
121
|
+
return unless verbose_logging?
|
122
|
+
|
123
|
+
log("Hosted domain verification for #{provider}: #{success ? "success" : "failed"}" \
|
124
|
+
"#{details ? " - #{details}" : ""}")
|
125
|
+
end
|
126
|
+
|
127
|
+
# Log custom operation results
|
128
|
+
# @param operation [String] The name of the operation
|
129
|
+
# @param success [Boolean] Whether the operation was successful
|
130
|
+
# @param details [String, nil] Optional details about the operation
|
131
|
+
def log_custom(operation, success, details = nil)
|
132
|
+
return unless verbose_logging?
|
133
|
+
|
134
|
+
log("#{operation}: #{success ? "success" : "failed"}#{details ? " - #{details}" : ""}")
|
135
|
+
end
|
136
|
+
|
84
137
|
private
|
85
138
|
|
86
139
|
# Filter potentially sensitive data from log messages
|