clavis 0.7.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd0c38b67afcfa2ff9c2322f6ab599d8f37960052e0ce826396d5b62836939f5
4
- data.tar.gz: 0d8fdb18d986ad9532c68c9e339c039665938fb1f108daf9f1c6a62579f549de
3
+ metadata.gz: ba3852474eb378c5c1793f18941c9530e4479ba53f81999314797b044dc7ed21
4
+ data.tar.gz: 0522726240b51050be94cd3b9057e5a3c4d6baec2beb05ed7ac005abd9136be0
5
5
  SHA512:
6
- metadata.gz: e34aa640baa0caf1fc37d5337673866e10222de56a44f3c86c0d4c615ea5f6f8a52d5e6e1f16fcec4b2fad531ae565985f048ced81b8d3510aa661b6af14f59c
7
- data.tar.gz: 53a32a6d4b0728e53870ccf0c01622878cad917eb6c0a57fe34211838d6013de6879528f4ca83b04a76e53ce2c7f6dac58cb950cf8ed725217e63b8c82e33ee1
6
+ metadata.gz: b0231cade528d2841c85b671dc8529ead75b4f9341c12b141a319a76488d8c8c663b767ea2a91536a2e7f1b7b3f634c2706b9336eb10d9e7dbda758dfb62441e
7
+ data.tar.gz: f497cdf8041d7c3810926e7d337cf985a3b067bc908eb87ca36ce0300f79109d1d308df79d4ad8bdb9330d6b5453aada1da10f070c27dd998ab3d1215e76c6bb
data/README.md CHANGED
@@ -193,6 +193,25 @@ end
193
193
 
194
194
  See `config/initializers/clavis.rb` for all configuration options.
195
195
 
196
+ #### Verbose Logging
197
+
198
+ By default, Clavis keeps its logs minimal to avoid cluttering your application logs. If you need more detailed logs during authentication processes for debugging purposes, you can enable verbose logging:
199
+
200
+ ```ruby
201
+ Clavis.configure do |config|
202
+ # Enable detailed authentication flow logs
203
+ config.verbose_logging = true
204
+ end
205
+ ```
206
+
207
+ When enabled, this will log details about:
208
+ - Token exchanges
209
+ - User info requests
210
+ - Token refreshes and verifications
211
+ - Authorization requests and callbacks
212
+
213
+ This is particularly useful for debugging OAuth integration issues, but should typically be disabled in production.
214
+
196
215
  ## User Management
197
216
 
198
217
  Clavis delegates user creation and management to your application through the `find_or_create_from_clavis` method. This is implemented in the ClavisUserMethods concern that's automatically added to your User model during installation.
@@ -483,11 +502,97 @@ When setting up OAuth, correctly configuring redirect URIs in both your app and
483
502
  1. Go to [GitHub Developer Settings](https://github.com/settings/developers)
484
503
  2. Navigate to "OAuth Apps" and create or edit your app
485
504
  3. In the "Authorization callback URL" field, add exactly the same URI as in your Clavis config
505
+ - For development: `http://localhost:3000/auth/github/callback`
506
+ - For production: `https://your-app.com/auth/github/callback`
486
507
 
487
508
  #### Common Errors
488
509
  - **Error 400: redirect_uri_mismatch** - This means the URI in your code doesn't match what's registered in the provider's console
489
510
  - **Solution**: Ensure both URIs match exactly, including protocol (http/https), domain, port, and full path
490
511
 
512
+ #### GitHub Enterprise Support
513
+
514
+ Clavis supports GitHub Enterprise installations with custom configuration options:
515
+
516
+ ```ruby
517
+ config.providers = {
518
+ github: {
519
+ client_id: ENV["GITHUB_CLIENT_ID"],
520
+ client_secret: ENV["GITHUB_CLIENT_SECRET"],
521
+ redirect_uri: "https://your-app.com/auth/github/callback",
522
+ # GitHub Enterprise settings:
523
+ site_url: "https://api.github.yourdomain.com", # Your Enterprise API endpoint
524
+ authorize_url: "https://github.yourdomain.com/login/oauth/authorize",
525
+ token_url: "https://github.yourdomain.com/login/oauth/access_token"
526
+ }
527
+ }
528
+ ```
529
+
530
+ | Option | Description | Default |
531
+ |--------|-------------|---------|
532
+ | `site_url` | Base URL for the GitHub API | `https://api.github.com` |
533
+ | `authorize_url` | Authorization endpoint URL | `https://github.com/login/oauth/authorize` |
534
+ | `token_url` | Token exchange endpoint URL | `https://github.com/login/oauth/access_token` |
535
+
536
+ #### Facebook
537
+ 1. Go to [Facebook Developer Portal](https://developers.facebook.com)
538
+ 2. Create or select a Facebook app
539
+ 3. Navigate to Settings > Basic to find your App ID and App Secret
540
+ 4. Set up "Facebook Login" and configure "Valid OAuth Redirect URIs" with the exact URI from your Clavis config:
541
+ - For development: `http://localhost:3000/auth/facebook/callback`
542
+ - For production: `https://your-app.com/auth/facebook/callback`
543
+
544
+ ### Provider Configuration Options
545
+
546
+ Providers can be configured with additional options for customizing behavior:
547
+
548
+ #### Facebook Provider Options
549
+
550
+ ```ruby
551
+ config.providers = {
552
+ facebook: {
553
+ client_id: ENV["FACEBOOK_CLIENT_ID"],
554
+ client_secret: ENV["FACEBOOK_CLIENT_SECRET"],
555
+ redirect_uri: "https://your-app.com/auth/facebook/callback",
556
+ # Optional settings:
557
+ display: "popup", # Display mode - options: page, popup, touch
558
+ auth_type: "rerequest", # Auth type - useful for permission re-requests
559
+ image_size: "large", # Profile image size - small, normal, large, square
560
+ # Alternative: provide exact dimensions
561
+ image_size: { width: 200, height: 200 },
562
+ secure_image_url: true # Force HTTPS for image URLs (default true)
563
+ }
564
+ }
565
+ ```
566
+
567
+ | Option | Description | Values | Default |
568
+ |--------|-------------|--------|---------|
569
+ | `display` | Controls how the authorization dialog is displayed | `page`, `popup`, `touch` | `page` |
570
+ | `auth_type` | Specifies the auth flow behavior | `rerequest`, `reauthenticate` | N/A |
571
+ | `image_size` | Profile image size | String: `small`, `normal`, `large`, `square` or Hash: `{ width: 200, height: 200 }` | N/A |
572
+ | `secure_image_url` | Force HTTPS for profile image URLs | `true`, `false` | `true` |
573
+
574
+ #### Using Facebook Long-Lived Tokens
575
+
576
+ Facebook access tokens are short-lived by default. The Facebook provider includes methods to exchange these for long-lived tokens:
577
+
578
+ ```ruby
579
+ # Exchange a short-lived token for a long-lived token
580
+ provider = Clavis.provider(:facebook)
581
+ long_lived_token_data = provider.exchange_for_long_lived_token(oauth_identity.access_token)
582
+
583
+ # Update the OAuth identity with the new token
584
+ oauth_identity.update(
585
+ access_token: long_lived_token_data[:access_token],
586
+ expires_at: Time.now + long_lived_token_data[:expires_in].to_i.seconds
587
+ )
588
+ ```
589
+
590
+ #### Common Errors
591
+
592
+ - **Error 400: Invalid OAuth access token** - The token is invalid or expired
593
+ - **Error 400: redirect_uri does not match** - Mismatch between registered and provided redirect URI
594
+ - **Solution**: Ensure the redirect URI in your code matches exactly what's registered in Facebook Developer Portal
595
+
491
596
  ## Security & Rate Limiting
492
597
 
493
598
  Clavis includes built-in integration with the [Rack::Attack](https://github.com/rack/rack-attack) gem to protect your OAuth endpoints against DDoS and brute force attacks.
@@ -14,4 +14,5 @@ group :development, :test do
14
14
  gem "generator_spec"
15
15
  gem "ostruct"
16
16
  gem "rspec-rails"
17
+ gem "webmock", "~> 3.25"
17
18
  end
@@ -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
- # Log parameters safely
78
- Clavis::Security::ParameterFilter.log_parameters(
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 for error response from provider
85
- if params[:error].present?
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 parameter to prevent CSRF
91
- unless Clavis::Security::SessionManager.valid_state?(session, params[:state], clear_after_validation: true)
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
- unless Clavis::Security::InputValidator.valid_code?(params[:code])
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
- begin
103
- # Exchange code for tokens
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
- # Call the configured method to find or create the user
119
- user = user_class.public_send(finder_method, auth_hash)
97
+ # Process the OAuth callback
98
+ auth_hash = process_provider_callback(provider, oauth_params)
120
99
 
121
- # Rotate session ID for security
122
- Clavis::Security::SessionManager.rotate_session(request) if Clavis.configuration.rotate_session_after_login
100
+ # Find or create user if configured
101
+ user = find_or_create_user(auth_hash)
123
102
 
124
- # Store additional information in the session
125
- Clavis::Security::SessionManager.store_auth_info(session, auth_hash)
103
+ # Security measures and session management
104
+ handle_session_security(auth_hash)
126
105
 
127
- # Invoke custom claims processor if configured
128
- if Clavis.configuration.claims_processor.respond_to?(:call)
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
- # Yield to block for custom processing
133
- yield(user, auth_hash) if block_given?
134
- rescue StandardError => e
135
- raise Clavis::AuthenticationError, e.message
136
- end
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 find_or_create_user_from_oauth(auth_hash)
159
- # If the User class has the find_for_oauth method, use it
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
- user = if identity.user.present?
171
- identity.user
172
- elsif auth_hash.dig(:info, :email).present?
173
- # Try to find user by email
174
- user_email_field = User.new.respond_to?(:email) ? :email : :email_address
175
- User.find_by(user_email_field => auth_hash.dig(:info, :email)) ||
176
- begin
177
- new_user = User.new
178
- if new_user.respond_to?(user_email_field)
179
- new_user.send(:"#{user_email_field}=", auth_hash.dig(:info, :email))
180
- end
181
-
182
- # Set password if applicable
183
- if new_user.respond_to?(:password=) && new_user.respond_to?(:password_confirmation=)
184
- password = SecureRandom.hex(16)
185
- new_user.password = password
186
- new_user.password_confirmation = password if new_user.respond_to?(:password_confirmation=)
187
- end
188
-
189
- # Set name if applicable
190
- set_user_name_from_auth_hash(new_user, auth_hash) if auth_hash.dig(:info, :name).present?
191
-
192
- new_user.save!
193
- new_user
194
- end
195
- else
196
- # No email found, create a new user without email
197
- User.create!(password: SecureRandom.hex(16))
198
- end
199
-
200
- # Update the identity with the latest auth data
201
- identity.user = user
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
- # Helper method to set user name from auth hash
219
- def set_user_name_from_auth_hash(user, auth_hash)
220
- return unless auth_hash.dig(:info, :name).present?
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
- name_parts = auth_hash.dig(:info, :name).split
223
- user.first_name = name_parts.first if user.respond_to?(:first_name=)
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
- return unless name_parts.size > 1 && user.respond_to?(:last_name=)
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
- user.last_name = name_parts.drop(1).join(" ")
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
@@ -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