clavis 0.7.1

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.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.actrc +4 -0
  3. data/.cursor/rules/ruby-gem.mdc +49 -0
  4. data/.gemignore +6 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +88 -0
  7. data/.vscode/settings.json +22 -0
  8. data/CHANGELOG.md +127 -0
  9. data/CODE_OF_CONDUCT.md +3 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +838 -0
  12. data/Rakefile +341 -0
  13. data/UPGRADE.md +57 -0
  14. data/app/assets/stylesheets/clavis.css +133 -0
  15. data/app/controllers/clavis/auth_controller.rb +133 -0
  16. data/config/database.yml +16 -0
  17. data/config/routes.rb +49 -0
  18. data/docs/SECURITY.md +340 -0
  19. data/docs/TESTING.md +78 -0
  20. data/docs/integration.md +272 -0
  21. data/error_handling.md +355 -0
  22. data/file_structure.md +221 -0
  23. data/gemfiles/rails_80.gemfile +17 -0
  24. data/gemfiles/rails_80.gemfile.lock +286 -0
  25. data/implementation_plan.md +523 -0
  26. data/lib/clavis/configuration.rb +196 -0
  27. data/lib/clavis/controllers/concerns/authentication.rb +232 -0
  28. data/lib/clavis/controllers/concerns/session_management.rb +117 -0
  29. data/lib/clavis/engine.rb +191 -0
  30. data/lib/clavis/errors.rb +205 -0
  31. data/lib/clavis/logging.rb +116 -0
  32. data/lib/clavis/models/concerns/oauth_authenticatable.rb +169 -0
  33. data/lib/clavis/oauth_identity.rb +174 -0
  34. data/lib/clavis/providers/apple.rb +135 -0
  35. data/lib/clavis/providers/base.rb +432 -0
  36. data/lib/clavis/providers/custom_provider_example.rb +57 -0
  37. data/lib/clavis/providers/facebook.rb +84 -0
  38. data/lib/clavis/providers/generic.rb +63 -0
  39. data/lib/clavis/providers/github.rb +87 -0
  40. data/lib/clavis/providers/google.rb +98 -0
  41. data/lib/clavis/providers/microsoft.rb +57 -0
  42. data/lib/clavis/security/csrf_protection.rb +79 -0
  43. data/lib/clavis/security/https_enforcer.rb +90 -0
  44. data/lib/clavis/security/input_validator.rb +192 -0
  45. data/lib/clavis/security/parameter_filter.rb +64 -0
  46. data/lib/clavis/security/rate_limiter.rb +109 -0
  47. data/lib/clavis/security/redirect_uri_validator.rb +124 -0
  48. data/lib/clavis/security/session_manager.rb +220 -0
  49. data/lib/clavis/security/token_storage.rb +114 -0
  50. data/lib/clavis/user_info_normalizer.rb +74 -0
  51. data/lib/clavis/utils/nonce_store.rb +14 -0
  52. data/lib/clavis/utils/secure_token.rb +17 -0
  53. data/lib/clavis/utils/state_store.rb +18 -0
  54. data/lib/clavis/version.rb +6 -0
  55. data/lib/clavis/view_helpers.rb +260 -0
  56. data/lib/clavis.rb +132 -0
  57. data/lib/generators/clavis/controller/controller_generator.rb +48 -0
  58. data/lib/generators/clavis/controller/templates/controller.rb.tt +137 -0
  59. data/lib/generators/clavis/controller/templates/views/login.html.erb.tt +145 -0
  60. data/lib/generators/clavis/install_generator.rb +182 -0
  61. data/lib/generators/clavis/templates/add_oauth_to_users.rb +28 -0
  62. data/lib/generators/clavis/templates/clavis.css +133 -0
  63. data/lib/generators/clavis/templates/initializer.rb +47 -0
  64. data/lib/generators/clavis/templates/initializer.rb.tt +76 -0
  65. data/lib/generators/clavis/templates/migration.rb +18 -0
  66. data/lib/generators/clavis/templates/migration.rb.tt +16 -0
  67. data/lib/generators/clavis/user_method/user_method_generator.rb +219 -0
  68. data/lib/tasks/provider_verification.rake +77 -0
  69. data/llms.md +487 -0
  70. data/log/development.log +20 -0
  71. data/log/test.log +0 -0
  72. data/sig/clavis.rbs +4 -0
  73. data/testing_plan.md +710 -0
  74. metadata +258 -0
data/config/routes.rb ADDED
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ Clavis::Engine.routes.draw do
4
+ # Class variable to track registered routes
5
+ unless Clavis::Engine.instance_variable_defined?(:@registered_routes)
6
+ Clavis::Engine.instance_variable_set(:@registered_routes,
7
+ Set.new)
8
+ end
9
+ registered_routes = Clavis::Engine.instance_variable_get(:@registered_routes)
10
+
11
+ # These routes will be prefixed by the engine mount point (e.g., /auth)
12
+ # No additional module scope needed since the engine already has Clavis namespace
13
+ scope do
14
+ # Provider-specific named routes
15
+ Clavis::Configuration::SUPPORTED_PROVIDERS.each do |provider|
16
+ route_name = "auth_#{provider}"
17
+ callback_route_name = "auth_#{provider}_callback"
18
+
19
+ unless registered_routes.include?(route_name)
20
+ # Routes inside engine are relative to mount point, so no additional /auth prefix
21
+ get "/#{provider}", to: "auth#authorize", as: route_name, defaults: { provider: provider }
22
+ registered_routes << route_name
23
+ end
24
+
25
+ unless registered_routes.include?(callback_route_name)
26
+ get "/#{provider}/callback", to: "auth#callback", as: callback_route_name, defaults: { provider: provider }
27
+ registered_routes << callback_route_name
28
+ end
29
+ end
30
+
31
+ # Fallback dynamic routes for custom providers
32
+ unless registered_routes.include?("auth")
33
+ get "/:provider", to: "auth#authorize", as: "auth"
34
+ registered_routes << "auth"
35
+ end
36
+
37
+ unless registered_routes.include?("auth_callback")
38
+ get "/:provider/callback", to: "auth#callback", as: "auth_callback"
39
+ registered_routes << "auth_callback"
40
+ end
41
+ end
42
+ end
43
+
44
+ # We don't need to define additional application routes, since all should go through
45
+ # the engine when mounted at /auth
46
+ Clavis::Engine.setup_routes = lambda do |_app|
47
+ # Just log that routes are set up and no action is needed
48
+ Rails.logger.info("Clavis engine is mounted. Use engine routes via engine route helpers.")
49
+ end
data/docs/SECURITY.md ADDED
@@ -0,0 +1,340 @@
1
+ # Security Best Practices for Clavis
2
+
3
+ This document outlines security best practices for using the Clavis gem in your Rails application.
4
+
5
+ ## Configuration
6
+
7
+ ### HTTPS Enforcement
8
+
9
+ Clavis enforces HTTPS for all OAuth URLs by default. This ensures that all communication with OAuth providers is encrypted.
10
+
11
+ ```ruby
12
+ # In config/initializers/clavis.rb
13
+ Clavis.configure do |config|
14
+ # Enable HTTPS enforcement (enabled by default)
15
+ config.enforce_https = true
16
+
17
+ # Allow HTTP for localhost in development (enabled by default)
18
+ config.allow_http_localhost = true
19
+
20
+ # Verify SSL certificates (enabled by default)
21
+ config.verify_ssl = true
22
+
23
+ # Set minimum TLS version (TLS 1.2 by default)
24
+ config.minimum_tls_version = :TLS1_2
25
+ end
26
+ ```
27
+
28
+ In production environments, HTTPS is always enforced, and SSL certificate validation is always enabled, regardless of configuration.
29
+
30
+ ### Token Encryption
31
+
32
+ Clavis can encrypt OAuth tokens before storing them in your database. This adds an extra layer of protection for sensitive tokens.
33
+
34
+ ```ruby
35
+ # In config/initializers/clavis.rb
36
+ Clavis.configure do |config|
37
+ # Enable token encryption
38
+ config.encrypt_tokens = true
39
+
40
+ # Set encryption key (must be at least 32 bytes)
41
+ config.encryption_key = ENV['CLAVIS_ENCRYPTION_KEY']
42
+
43
+ # Use Rails credentials for encryption key
44
+ config.use_rails_credentials = true
45
+ end
46
+ ```
47
+
48
+ For Rails applications, you can store the encryption key in your credentials:
49
+
50
+ ```yaml
51
+ # In config/credentials.yml.enc
52
+ clavis:
53
+ encryption_key: your_secure_encryption_key_at_least_32_bytes_long
54
+ ```
55
+
56
+ ### Redirect URI Validation
57
+
58
+ Clavis validates redirect URIs to prevent open redirector vulnerabilities. You should configure allowed hosts for your application:
59
+
60
+ ```ruby
61
+ # In config/initializers/clavis.rb
62
+ Clavis.configure do |config|
63
+ # Set allowed redirect hosts
64
+ config.allowed_redirect_hosts = ['your-app.com', 'www.your-app.com']
65
+
66
+ # Enable exact matching for redirect URIs
67
+ config.exact_redirect_uri_matching = true
68
+
69
+ # Allow localhost in development
70
+ config.allow_localhost_in_development = true
71
+
72
+ # Raise an exception for invalid redirect URIs
73
+ config.raise_on_invalid_redirect = true
74
+ end
75
+ ```
76
+
77
+ ### Parameter Filtering
78
+
79
+ Clavis filters sensitive parameters from logs by default. This prevents sensitive information like tokens and authorization codes from being logged.
80
+
81
+ ```ruby
82
+ # In config/initializers/clavis.rb
83
+ Clavis.configure do |config|
84
+ # Enable parameter filtering (enabled by default)
85
+ config.parameter_filter_enabled = true
86
+ end
87
+ ```
88
+
89
+ ### Input Validation
90
+
91
+ Clavis validates and sanitizes all inputs by default. This helps prevent injection attacks and other security vulnerabilities.
92
+
93
+ ```ruby
94
+ # In config/initializers/clavis.rb
95
+ Clavis.configure do |config|
96
+ # Enable input validation (enabled by default)
97
+ config.validate_inputs = true
98
+
99
+ # Enable input sanitization (enabled by default)
100
+ config.sanitize_inputs = true
101
+ end
102
+ ```
103
+
104
+ ### Session Management
105
+
106
+ Clavis includes secure session management features, including session rotation after authentication to prevent session fixation attacks.
107
+
108
+ ```ruby
109
+ # In config/initializers/clavis.rb
110
+ Clavis.configure do |config|
111
+ # Enable session rotation after login (enabled by default)
112
+ config.rotate_session_after_login = true
113
+
114
+ # Set prefix for session keys (default: 'clavis')
115
+ config.session_key_prefix = 'clavis'
116
+ end
117
+ ```
118
+
119
+ ### Rate Limiting
120
+
121
+ Clavis integrates with Rack::Attack to provide rate limiting for OAuth endpoints, protecting against DDoS and brute force attacks.
122
+
123
+ ```ruby
124
+ # In config/initializers/clavis.rb
125
+ Clavis.configure do |config|
126
+ # Enable rate limiting (enabled by default)
127
+ config.rate_limiting_enabled = true
128
+
129
+ # Configure custom throttle rules
130
+ config.custom_throttles = {
131
+ "login_page": {
132
+ limit: 30,
133
+ period: 1.minute,
134
+ block: ->(req) { req.path == "/login" ? req.ip : nil }
135
+ }
136
+ }
137
+ end
138
+ ```
139
+
140
+ By default, Clavis applies the following rate limits:
141
+
142
+ - **OAuth Authorization Endpoints**: 20 requests per minute per IP address
143
+ - **OAuth Callback Endpoints**: 15 requests per minute per IP address
144
+ - **Login Attempts by Email**: 5 requests per 20 seconds per email address
145
+
146
+ For more advanced configuration, you can define custom Rack::Attack rules in a separate initializer:
147
+
148
+ ```ruby
149
+ # config/initializers/rack_attack.rb
150
+ Rack::Attack.blocklist("block suspicious requests") do |req|
151
+ # Block requests that contain SQL injection patterns
152
+ req.path.include?("' OR '1'='1") || req.path.include?("--")
153
+ end
154
+
155
+ # Customize throttled response
156
+ Rack::Attack.throttled_responder = lambda do |req|
157
+ [429, {'Content-Type' => 'application/json'}, [{ error: "Too many requests" }.to_json]]
158
+ end
159
+
160
+ # Use a dedicated Redis instance for rate limiting
161
+ Rack::Attack.cache.store = ActiveSupport::Cache::RedisCacheStore.new(
162
+ url: ENV["REDIS_RATE_LIMIT_URL"]
163
+ )
164
+ ```
165
+
166
+ ## Model Security
167
+
168
+ ### ActiveRecord Encryption
169
+
170
+ For Rails 7+ applications, you can use ActiveRecord Encryption to encrypt OAuth tokens in the database:
171
+
172
+ ```ruby
173
+ # In app/models/oauth_identity.rb
174
+ class OauthIdentity < ApplicationRecord
175
+ encrypts :token, :refresh_token,
176
+ deterministic: false,
177
+ downcase: false,
178
+ previous: []
179
+ end
180
+ ```
181
+
182
+ ### Token Storage
183
+
184
+ If you're not using ActiveRecord Encryption, you can use Clavis's token encryption:
185
+
186
+ ```ruby
187
+ # In app/models/oauth_identity.rb
188
+ class OauthIdentity < ApplicationRecord
189
+ # Override getters and setters to use Clavis's token encryption
190
+ def token
191
+ Clavis::Security::TokenStorage.decrypt(super)
192
+ end
193
+
194
+ def token=(value)
195
+ super(Clavis::Security::TokenStorage.encrypt(value))
196
+ end
197
+
198
+ def refresh_token
199
+ Clavis::Security::TokenStorage.decrypt(super)
200
+ end
201
+
202
+ def refresh_token=(value)
203
+ super(Clavis::Security::TokenStorage.encrypt(value))
204
+ end
205
+ end
206
+ ```
207
+
208
+ ## Controller Security
209
+
210
+ ### Using the Authentication Controller Generator
211
+
212
+ Clavis provides a generator to create a secure authentication controller:
213
+
214
+ ```bash
215
+ rails generate clavis:controller Auth
216
+ ```
217
+
218
+ This will create:
219
+ - An `AuthController` with secure OAuth methods
220
+ - A login view with OAuth provider buttons
221
+ - Routes for OAuth authentication
222
+
223
+ You can customize the controller name:
224
+
225
+ ```bash
226
+ rails generate clavis:controller Authentication
227
+ ```
228
+
229
+ ### CSRF Protection
230
+
231
+ Clavis includes built-in CSRF protection for OAuth flows. The state parameter is automatically generated and validated.
232
+
233
+ ```ruby
234
+ # In your controller
235
+ def oauth_callback
236
+ # State parameter is automatically validated
237
+ auth_hash = oauth_provider.process_callback(params[:code])
238
+
239
+ # Process the auth hash
240
+ user = User.find_or_create_from_oauth(auth_hash)
241
+
242
+ # Sign in the user
243
+ sign_in(user)
244
+
245
+ # Redirect to a safe URL
246
+ redirect_to dashboard_path
247
+ end
248
+ ```
249
+
250
+ ### Secure Redirects
251
+
252
+ Always validate redirect URLs before redirecting:
253
+
254
+ ```ruby
255
+ # In your controller
256
+ def oauth_callback
257
+ # Process OAuth callback
258
+
259
+ # Get the redirect URL from the session
260
+ redirect_url = Clavis::Security::SessionManager.validate_and_retrieve_redirect_uri(
261
+ session,
262
+ default: root_path
263
+ )
264
+
265
+ # Redirect to the validated URL
266
+ redirect_to redirect_url
267
+ end
268
+ ```
269
+
270
+ ### Session Rotation
271
+
272
+ Clavis automatically rotates the session ID after authentication to prevent session fixation attacks:
273
+
274
+ ```ruby
275
+ # In your controller
276
+ def oauth_callback
277
+ # Process OAuth callback
278
+
279
+ # Rotate the session ID (done automatically by Clavis)
280
+ # You can preserve specific keys during rotation
281
+ Clavis::Security::SessionManager.rotate_session_id(
282
+ session,
283
+ SecureRandom.hex(32),
284
+ preserve_keys: [:user_id, :return_to]
285
+ )
286
+
287
+ # Redirect to a safe URL
288
+ redirect_to dashboard_path
289
+ end
290
+ ```
291
+
292
+ ## Input Validation and Sanitization
293
+
294
+ Clavis provides comprehensive input validation and sanitization:
295
+
296
+ ```ruby
297
+ # Validate a URL
298
+ if Clavis::Security::InputValidator.valid_url?(params[:redirect_uri])
299
+ # URL is valid
300
+ end
301
+
302
+ # Validate a token
303
+ if Clavis::Security::InputValidator.valid_token?(params[:token])
304
+ # Token is valid
305
+ end
306
+
307
+ # Sanitize user input
308
+ safe_input = Clavis::Security::InputValidator.sanitize(params[:user_input])
309
+
310
+ # Sanitize a hash of user inputs
311
+ safe_params = Clavis::Security::InputValidator.sanitize_hash(params.to_unsafe_h)
312
+ ```
313
+
314
+ ## General Security Recommendations
315
+
316
+ 1. **Keep Dependencies Updated**: Regularly update Clavis and other dependencies to get security fixes.
317
+
318
+ 2. **Use Environment Variables**: Store sensitive configuration in environment variables or Rails credentials, not in code.
319
+
320
+ 3. **Implement Rate Limiting**: Add rate limiting to OAuth endpoints to prevent brute force attacks.
321
+
322
+ 4. **Monitor for Suspicious Activity**: Log and monitor OAuth authentication attempts for suspicious patterns.
323
+
324
+ 5. **Implement Multi-Factor Authentication**: Consider adding MFA for sensitive operations.
325
+
326
+ 6. **Regular Security Audits**: Regularly audit your OAuth implementation for security vulnerabilities.
327
+
328
+ 7. **Secure Session Management**: Use secure, HTTP-only cookies for session management.
329
+
330
+ 8. **Implement Proper Error Handling**: Don't expose sensitive information in error messages.
331
+
332
+ ## Reporting Security Issues
333
+
334
+ If you discover a security issue in Clavis, please report it by email to [security@example.com](mailto:security@example.com). Do not disclose security bugs publicly until they have been handled by the security team.
335
+
336
+ ## Additional Resources
337
+
338
+ - [OAuth 2.0 Security Best Practices](https://oauth.net/2/security-best-practices/)
339
+ - [OpenID Connect Security](https://openid.net/specs/openid-connect-core-1_0.html#Security)
340
+ - [Rails Security Guide](https://guides.rubyonrails.org/security.html)
data/docs/TESTING.md ADDED
@@ -0,0 +1,78 @@
1
+ # Clavis Testing Guide
2
+
3
+ This document explains how to run the test matrix locally to ensure compatibility with different Ruby and Rails versions.
4
+
5
+ ## Test Matrix
6
+
7
+ Clavis is tested against the following combinations:
8
+
9
+ | Ruby Version | Rails Version | Gemfile |
10
+ |--------------|---------------|---------|
11
+ | 3.0.6 | 7.0 | gemfiles/rails_70.gemfile |
12
+ | 3.2.3 | 7.1 | gemfiles/rails_71.gemfile |
13
+ | 3.3.0 | 7.2 | gemfiles/rails_72.gemfile |
14
+ | 3.3.0 | 8.0 | gemfiles/rails_80.gemfile |
15
+ | 3.4.1 | 8.0 | gemfiles/rails_80.gemfile |
16
+
17
+ ## Using the matrix_test Script
18
+
19
+ The `bin/matrix_test` script allows you to run tests with specific Ruby and Rails combinations or the entire matrix:
20
+
21
+ ### To run a specific combination:
22
+
23
+ ```bash
24
+ # Format: bin/matrix_test <ruby_version> <gemfile>
25
+ bin/matrix_test 3.4.1 gemfiles/rails_80.gemfile
26
+ ```
27
+
28
+ ### To run all matrix combinations:
29
+
30
+ ```bash
31
+ bin/matrix_test all
32
+ ```
33
+
34
+ Note: You need to have all the Ruby versions installed via rbenv to use this script.
35
+
36
+ ## Using Act to Test GitHub Actions Locally
37
+
38
+ You can also use [act](https://github.com/nektos/act) to run the GitHub Actions matrix locally:
39
+
40
+ 1. **Install act**:
41
+ ```bash
42
+ brew install act
43
+ ```
44
+
45
+ 2. **Run the CI workflow**:
46
+ ```bash
47
+ act -W .github/workflows/ci_local.yml
48
+ ```
49
+
50
+ 3. **Run a specific job**:
51
+ ```bash
52
+ act -W .github/workflows/ci_local.yml -j test
53
+ ```
54
+
55
+ 4. **Run with specific matrix combinations**:
56
+ ```bash
57
+ # You can specify matrix values to filter the jobs
58
+ act -W .github/workflows/ci_local.yml -j test --matrix ruby-version:3.4.1
59
+ ```
60
+
61
+ ## Tips for Troubleshooting
62
+
63
+ 1. If you encounter issues with a specific Rails version, you can focus your testing on that version first:
64
+ ```bash
65
+ bin/matrix_test 3.4.1 gemfiles/rails_80.gemfile
66
+ ```
67
+
68
+ 2. If a test fails only in the GitHub CI but works locally, use act to reproduce the CI environment.
69
+
70
+ 3. To debug test failures in a specific Rails environment, you can set the Gemfile manually and add debugging output:
71
+ ```bash
72
+ BUNDLE_GEMFILE=gemfiles/rails_80.gemfile bundle exec rspec spec/failing_spec.rb -f d
73
+ ```
74
+
75
+ 4. Remember to run RuboCop before pushing changes:
76
+ ```bash
77
+ bundle exec rubocop -A
78
+ ```