better_auth-rails 0.2.1 → 0.5.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/README.md +27 -28
- data/lib/better_auth/rails/active_record_adapter.rb +102 -27
- data/lib/better_auth/rails/configuration.rb +47 -0
- data/lib/better_auth/rails/option_builder.rb +58 -0
- data/lib/better_auth/rails/version.rb +1 -1
- data/lib/better_auth/rails.rb +1 -0
- data/lib/generators/better_auth/install/templates/initializer.rb.tt +34 -27
- metadata +16 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d3bbbf52a2438b83d1668fc4b3d15eb28f162e9cc81cd4e88c37008407fcb5ff
|
|
4
|
+
data.tar.gz: d2b18b69ffcfb01601aa9a48e01883d0b1a6cc032e087b1fa92c5efa9d2eba08
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4b70675596c580f94602b8505fc0df556da325a614aaff02c19b9939ff9d959354405986c55c0737c1d7f6b10dfeabc0f3168a75eff9736607651d26dc020544
|
|
7
|
+
data.tar.gz: 2cfa4a247413b9caa48c21613873021ccde047d813f34adf311b83bfa58192f186375d215f9eb883c81debf1b7406e4e628a2d721811bf060fab5db9c1ca5aed
|
data/README.md
CHANGED
|
@@ -80,48 +80,47 @@ BetterAuth::Rails.configure do |config|
|
|
|
80
80
|
|
|
81
81
|
config.base_url = ENV["BETTER_AUTH_URL"]
|
|
82
82
|
config.base_path = "/api/auth"
|
|
83
|
-
config.database = ->(options) { BetterAuth::Rails::ActiveRecordAdapter.new(options) }
|
|
84
83
|
config.trusted_origins = [
|
|
85
84
|
ENV["BETTER_AUTH_URL"]
|
|
86
85
|
].compact
|
|
87
86
|
|
|
88
|
-
config.session
|
|
89
|
-
cookie_cache
|
|
90
|
-
enabled
|
|
91
|
-
max_age
|
|
92
|
-
strategy
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
config.advanced
|
|
97
|
-
ip_address
|
|
98
|
-
ip_address_headers
|
|
99
|
-
disable_ip_tracking
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
config.experimental
|
|
104
|
-
joins
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
config.social_providers
|
|
108
|
-
# github
|
|
87
|
+
config.session do |session|
|
|
88
|
+
session.cookie_cache do |cookie|
|
|
89
|
+
cookie.enabled = true
|
|
90
|
+
cookie.max_age = 5 * 60
|
|
91
|
+
cookie.strategy = "jwe"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
config.advanced do |advanced|
|
|
96
|
+
advanced.ip_address do |ip|
|
|
97
|
+
ip.ip_address_headers = ["x-forwarded-for"]
|
|
98
|
+
ip.disable_ip_tracking = false
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
config.experimental do |experimental|
|
|
103
|
+
experimental.joins = false
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
config.social_providers do |providers|
|
|
107
|
+
# providers.github = BetterAuth::SocialProviders.github(
|
|
109
108
|
# client_id: ENV.fetch("GITHUB_CLIENT_ID"),
|
|
110
109
|
# client_secret: ENV.fetch("GITHUB_CLIENT_SECRET")
|
|
111
110
|
# )
|
|
112
|
-
|
|
111
|
+
end
|
|
113
112
|
|
|
114
113
|
config.plugins = []
|
|
115
|
-
config.hooks
|
|
116
|
-
before
|
|
117
|
-
after
|
|
118
|
-
|
|
114
|
+
config.hooks do |hooks|
|
|
115
|
+
hooks.before = []
|
|
116
|
+
hooks.after = []
|
|
117
|
+
end
|
|
119
118
|
end
|
|
120
119
|
```
|
|
121
120
|
|
|
122
121
|
Rails configuration is a thin option builder for the core Rack auth object. The same option concepts are available in core Ruby through `BetterAuth.auth(...)`; Rails places them in `config/initializers/better_auth.rb` so applications can rely on credentials, ActiveRecord, and Rails environment configuration.
|
|
123
122
|
|
|
124
|
-
The
|
|
123
|
+
Rails uses `BetterAuth::Rails::ActiveRecordAdapter` by default. The adapter uses whichever database adapter the Rails app is already configured with, including PostgreSQL and MySQL. To be explicit, set `config.database_adapter = :active_record`; for custom adapters, assign `config.database` directly.
|
|
125
124
|
|
|
126
125
|
### JavaScript Client
|
|
127
126
|
|
|
@@ -20,12 +20,16 @@ module BetterAuth
|
|
|
20
20
|
|
|
21
21
|
attr_reader :connection
|
|
22
22
|
|
|
23
|
-
def initialize(options, connection: nil)
|
|
23
|
+
def initialize(options = nil, connection: nil)
|
|
24
24
|
super(options)
|
|
25
|
-
@connection = connection || ::ActiveRecord::Base
|
|
25
|
+
@connection = connection || (options ? ::ActiveRecord::Base : nil)
|
|
26
26
|
@models = {}
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
def call(options)
|
|
30
|
+
self.class.new(options, connection: connection)
|
|
31
|
+
end
|
|
32
|
+
|
|
29
33
|
def create(model:, data:, force_allow_id: false)
|
|
30
34
|
model = model.to_s
|
|
31
35
|
input = transform_input(model, data, "create", force_allow_id)
|
|
@@ -219,36 +223,107 @@ module BetterAuth
|
|
|
219
223
|
end
|
|
220
224
|
|
|
221
225
|
def define_join_associations(model, klass)
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
226
|
+
schema_models.each_key do |join_model|
|
|
227
|
+
next if join_model == model
|
|
228
|
+
|
|
229
|
+
definition = safe_join_definition(model, join_model)
|
|
230
|
+
next unless definition
|
|
231
|
+
|
|
232
|
+
association = definition.fetch(:association)
|
|
233
|
+
next if association_defined?(klass, association)
|
|
234
|
+
|
|
235
|
+
if definition[:owner] == :base && definition[:collection]
|
|
236
|
+
next unless klass.respond_to?(:has_many)
|
|
237
|
+
|
|
238
|
+
klass.has_many(
|
|
239
|
+
association,
|
|
240
|
+
class_name: model_class(join_model).name,
|
|
241
|
+
foreign_key: storage_field(join_model, definition.fetch(:to)),
|
|
242
|
+
primary_key: storage_field(model, definition.fetch(:from))
|
|
243
|
+
)
|
|
244
|
+
elsif definition[:owner] == :base
|
|
245
|
+
next unless klass.respond_to?(:has_one)
|
|
246
|
+
|
|
247
|
+
klass.has_one(
|
|
248
|
+
association,
|
|
249
|
+
class_name: model_class(join_model).name,
|
|
250
|
+
foreign_key: storage_field(join_model, definition.fetch(:to)),
|
|
251
|
+
primary_key: storage_field(model, definition.fetch(:from))
|
|
252
|
+
)
|
|
253
|
+
else
|
|
254
|
+
next unless klass.respond_to?(:belongs_to)
|
|
255
|
+
|
|
256
|
+
klass.belongs_to(
|
|
257
|
+
association,
|
|
258
|
+
class_name: model_class(join_model).name,
|
|
259
|
+
foreign_key: storage_field(model, definition.fetch(:from)),
|
|
260
|
+
primary_key: storage_field(join_model, definition.fetch(:to)),
|
|
261
|
+
optional: true
|
|
262
|
+
)
|
|
263
|
+
end
|
|
242
264
|
end
|
|
243
265
|
end
|
|
244
266
|
|
|
245
267
|
def join_definition(model, join_model)
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
268
|
+
inferred_join_config(model.to_s, join_model.to_s).merge(association: join_model.to_sym)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def safe_join_definition(model, join_model)
|
|
272
|
+
join_definition(model, join_model)
|
|
273
|
+
rescue BetterAuth::Error
|
|
274
|
+
nil
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def inferred_join_config(model, join_model)
|
|
278
|
+
foreign_keys = schema_for(join_model).fetch(:fields).select do |_field, attributes|
|
|
279
|
+
reference_model_matches?(attributes, model)
|
|
251
280
|
end
|
|
281
|
+
|
|
282
|
+
unless foreign_keys.empty?
|
|
283
|
+
raise BetterAuth::Error, "Multiple foreign keys found for model #{join_model} and base model #{model} while performing join operation. Only one foreign key is supported." if foreign_keys.length > 1
|
|
284
|
+
|
|
285
|
+
foreign_key, attributes = foreign_keys.first
|
|
286
|
+
reference = attributes.fetch(:references)
|
|
287
|
+
unique = attributes[:unique] == true
|
|
288
|
+
return {
|
|
289
|
+
from: reference.fetch(:field).to_s,
|
|
290
|
+
to: foreign_key,
|
|
291
|
+
collection: !unique,
|
|
292
|
+
owner: :base
|
|
293
|
+
}
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
foreign_keys = schema_for(model).fetch(:fields).select do |_field, attributes|
|
|
297
|
+
reference_model_matches?(attributes, join_model)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
raise BetterAuth::Error, "No foreign key found for model #{join_model} and base model #{model} while performing join operation." if foreign_keys.empty?
|
|
301
|
+
raise BetterAuth::Error, "Multiple foreign keys found for model #{join_model} and base model #{model} while performing join operation. Only one foreign key is supported." if foreign_keys.length > 1
|
|
302
|
+
|
|
303
|
+
foreign_key, attributes = foreign_keys.first
|
|
304
|
+
reference = attributes.fetch(:references)
|
|
305
|
+
{
|
|
306
|
+
from: foreign_key,
|
|
307
|
+
to: reference.fetch(:field).to_s,
|
|
308
|
+
collection: false,
|
|
309
|
+
owner: :join
|
|
310
|
+
}
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def reference_model_matches?(attributes, model)
|
|
314
|
+
reference = attributes[:references]
|
|
315
|
+
return false unless reference
|
|
316
|
+
|
|
317
|
+
reference_model = reference[:model] || reference["model"]
|
|
318
|
+
reference_model.to_s == model.to_s || reference_model.to_s == table_for(model)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def association_defined?(klass, association)
|
|
322
|
+
klass.respond_to?(:reflect_on_association) && klass.reflect_on_association(association)
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def schema_models
|
|
326
|
+
BetterAuth::Schema.auth_tables(options)
|
|
252
327
|
end
|
|
253
328
|
|
|
254
329
|
def model_namespace
|
|
@@ -29,9 +29,35 @@ module BetterAuth
|
|
|
29
29
|
disabled_paths
|
|
30
30
|
logger
|
|
31
31
|
].freeze
|
|
32
|
+
BLOCK_OPTION_NAMES = %i[
|
|
33
|
+
rate_limit
|
|
34
|
+
session
|
|
35
|
+
account
|
|
36
|
+
user
|
|
37
|
+
verification
|
|
38
|
+
advanced
|
|
39
|
+
email_and_password
|
|
40
|
+
email_verification
|
|
41
|
+
social_providers
|
|
42
|
+
experimental
|
|
43
|
+
database_hooks
|
|
44
|
+
hooks
|
|
45
|
+
on_api_error
|
|
46
|
+
].freeze
|
|
32
47
|
|
|
33
48
|
attr_accessor(*AUTH_OPTION_NAMES)
|
|
34
49
|
|
|
50
|
+
BLOCK_OPTION_NAMES.each do |name|
|
|
51
|
+
define_method(name) do |&block|
|
|
52
|
+
value = instance_variable_get(:"@#{name}")
|
|
53
|
+
return value unless block
|
|
54
|
+
|
|
55
|
+
builder = OptionBuilder.new(value.is_a?(Hash) ? value : {})
|
|
56
|
+
block.call(builder)
|
|
57
|
+
public_send(:"#{name}=", deep_merge(value.is_a?(Hash) ? value : {}, builder.to_h))
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
35
61
|
def initialize
|
|
36
62
|
@base_path = BetterAuth::Configuration::DEFAULT_BASE_PATH
|
|
37
63
|
@plugins = []
|
|
@@ -39,6 +65,15 @@ module BetterAuth
|
|
|
39
65
|
@database = ->(options) { ActiveRecordAdapter.new(options) }
|
|
40
66
|
end
|
|
41
67
|
|
|
68
|
+
def database_adapter=(adapter)
|
|
69
|
+
case adapter&.to_sym
|
|
70
|
+
when :active_record
|
|
71
|
+
self.database = ->(options) { ActiveRecordAdapter.new(options) }
|
|
72
|
+
else
|
|
73
|
+
raise ArgumentError, "Unsupported database_adapter: #{adapter.inspect}. Use :active_record or assign a custom adapter with config.database = ..."
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
42
77
|
def to_auth_options
|
|
43
78
|
AUTH_OPTION_NAMES.each_with_object({}) do |name, options|
|
|
44
79
|
value = public_send(name)
|
|
@@ -48,6 +83,18 @@ module BetterAuth
|
|
|
48
83
|
options[name] = value
|
|
49
84
|
end
|
|
50
85
|
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def deep_merge(base, override)
|
|
90
|
+
base.merge(override) do |_key, old_value, new_value|
|
|
91
|
+
if old_value.is_a?(Hash) && new_value.is_a?(Hash)
|
|
92
|
+
deep_merge(old_value, new_value)
|
|
93
|
+
else
|
|
94
|
+
new_value
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
51
98
|
end
|
|
52
99
|
end
|
|
53
100
|
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuth
|
|
4
|
+
module Rails
|
|
5
|
+
class OptionBuilder
|
|
6
|
+
def initialize(values = {})
|
|
7
|
+
@values = deep_dup(symbolize_keys(values || {}))
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def to_h
|
|
11
|
+
deep_dup(@values)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def method_missing(name, *args, &block)
|
|
15
|
+
method_name = name.to_s
|
|
16
|
+
if method_name.end_with?("=")
|
|
17
|
+
key = method_name.delete_suffix("=").to_sym
|
|
18
|
+
@values[key] = args.first
|
|
19
|
+
elsif block
|
|
20
|
+
key = method_name.to_sym
|
|
21
|
+
nested = self.class.new(@values[key].is_a?(Hash) ? @values[key] : {})
|
|
22
|
+
yield nested
|
|
23
|
+
@values[key] = nested.to_h
|
|
24
|
+
elsif args.empty?
|
|
25
|
+
@values[method_name.to_sym]
|
|
26
|
+
else
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def respond_to_missing?(_name, _include_private = false)
|
|
32
|
+
true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def symbolize_keys(value)
|
|
38
|
+
return value unless value.is_a?(Hash)
|
|
39
|
+
|
|
40
|
+
value.each_with_object({}) do |(key, object_value), result|
|
|
41
|
+
normalized_key = key.to_s
|
|
42
|
+
.gsub(/([a-z\d])([A-Z])/, "\\1_\\2")
|
|
43
|
+
.tr("-", "_")
|
|
44
|
+
.downcase
|
|
45
|
+
.to_sym
|
|
46
|
+
result[normalized_key] = object_value.is_a?(Hash) ? symbolize_keys(object_value) : object_value
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def deep_dup(value)
|
|
51
|
+
return value.transform_values { |entry| deep_dup(entry) } if value.is_a?(Hash)
|
|
52
|
+
return value.map { |entry| deep_dup(entry) } if value.is_a?(Array)
|
|
53
|
+
|
|
54
|
+
value
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
data/lib/better_auth/rails.rb
CHANGED
|
@@ -8,43 +8,50 @@ BetterAuth::Rails.configure do |config|
|
|
|
8
8
|
|
|
9
9
|
config.base_url = ENV["BETTER_AUTH_URL"]
|
|
10
10
|
config.base_path = "/api/auth"
|
|
11
|
-
config.database = ->(options) { BetterAuth::Rails::ActiveRecordAdapter.new(options) }
|
|
12
11
|
config.trusted_origins = [
|
|
13
12
|
ENV["BETTER_AUTH_URL"]
|
|
14
13
|
].compact
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
config.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
15
|
+
# Rails apps use BetterAuth::Rails::ActiveRecordAdapter by default.
|
|
16
|
+
# For a custom adapter, assign config.database directly.
|
|
17
|
+
|
|
18
|
+
# config.email_and_password do |auth|
|
|
19
|
+
# auth.enabled = true
|
|
20
|
+
# auth.require_email_verification = true
|
|
21
|
+
# end
|
|
22
|
+
|
|
23
|
+
config.session do |session|
|
|
24
|
+
session.cookie_cache do |cookie|
|
|
25
|
+
cookie.enabled = true
|
|
26
|
+
cookie.max_age = 5 * 60
|
|
27
|
+
cookie.strategy = "jwe"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
config.advanced do |advanced|
|
|
32
|
+
advanced.ip_address do |ip|
|
|
33
|
+
ip.ip_address_headers = ["x-forwarded-for"]
|
|
34
|
+
ip.disable_ip_tracking = false
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
config.experimental do |experimental|
|
|
39
|
+
experimental.joins = false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
config.social_providers do |providers|
|
|
43
|
+
# providers.github = BetterAuth::SocialProviders.github(
|
|
37
44
|
# client_id: ENV.fetch("GITHUB_CLIENT_ID"),
|
|
38
45
|
# client_secret: ENV.fetch("GITHUB_CLIENT_SECRET")
|
|
39
46
|
# )
|
|
40
|
-
|
|
47
|
+
end
|
|
41
48
|
|
|
42
49
|
# Add Better Auth plugins here.
|
|
43
50
|
config.plugins = []
|
|
44
51
|
|
|
45
52
|
# Add Better Auth hooks here. Auth decisions still run through the core gem.
|
|
46
|
-
config.hooks
|
|
47
|
-
before
|
|
48
|
-
after
|
|
49
|
-
|
|
53
|
+
config.hooks do |hooks|
|
|
54
|
+
hooks.before = []
|
|
55
|
+
hooks.after = []
|
|
56
|
+
end
|
|
50
57
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: better_auth-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sebastian Sala
|
|
@@ -167,6 +167,20 @@ dependencies:
|
|
|
167
167
|
- - "~>"
|
|
168
168
|
- !ruby/object:Gem::Version
|
|
169
169
|
version: '1.5'
|
|
170
|
+
- !ruby/object:Gem::Dependency
|
|
171
|
+
name: mysql2
|
|
172
|
+
requirement: !ruby/object:Gem::Requirement
|
|
173
|
+
requirements:
|
|
174
|
+
- - "~>"
|
|
175
|
+
- !ruby/object:Gem::Version
|
|
176
|
+
version: '0.5'
|
|
177
|
+
type: :development
|
|
178
|
+
prerelease: false
|
|
179
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
180
|
+
requirements:
|
|
181
|
+
- - "~>"
|
|
182
|
+
- !ruby/object:Gem::Version
|
|
183
|
+
version: '0.5'
|
|
170
184
|
description: Rails integration for Better Auth Ruby. Provides middleware, controller
|
|
171
185
|
helpers, and generators.
|
|
172
186
|
email:
|
|
@@ -184,6 +198,7 @@ files:
|
|
|
184
198
|
- lib/better_auth/rails/controller_helpers.rb
|
|
185
199
|
- lib/better_auth/rails/migration.rb
|
|
186
200
|
- lib/better_auth/rails/mounted_app.rb
|
|
201
|
+
- lib/better_auth/rails/option_builder.rb
|
|
187
202
|
- lib/better_auth/rails/railtie.rb
|
|
188
203
|
- lib/better_auth/rails/routing.rb
|
|
189
204
|
- lib/better_auth/rails/version.rb
|