better_auth 0.3.0 → 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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -0
  3. data/README.md +24 -0
  4. data/lib/better_auth/adapters/internal_adapter.rb +10 -7
  5. data/lib/better_auth/adapters/memory.rb +57 -11
  6. data/lib/better_auth/adapters/sql.rb +123 -20
  7. data/lib/better_auth/api.rb +114 -9
  8. data/lib/better_auth/async.rb +70 -0
  9. data/lib/better_auth/configuration.rb +97 -7
  10. data/lib/better_auth/context.rb +165 -12
  11. data/lib/better_auth/cookies.rb +6 -4
  12. data/lib/better_auth/core.rb +2 -0
  13. data/lib/better_auth/crypto/jwe.rb +27 -5
  14. data/lib/better_auth/crypto.rb +32 -0
  15. data/lib/better_auth/database_hooks.rb +8 -8
  16. data/lib/better_auth/deprecate.rb +28 -0
  17. data/lib/better_auth/endpoint.rb +92 -5
  18. data/lib/better_auth/error.rb +8 -1
  19. data/lib/better_auth/host.rb +166 -0
  20. data/lib/better_auth/instrumentation.rb +74 -0
  21. data/lib/better_auth/logger.rb +31 -0
  22. data/lib/better_auth/middleware/origin_check.rb +2 -2
  23. data/lib/better_auth/oauth2.rb +94 -0
  24. data/lib/better_auth/plugins/admin/schema.rb +2 -2
  25. data/lib/better_auth/plugins/admin.rb +344 -16
  26. data/lib/better_auth/plugins/anonymous.rb +37 -3
  27. data/lib/better_auth/plugins/device_authorization.rb +102 -5
  28. data/lib/better_auth/plugins/dub.rb +148 -0
  29. data/lib/better_auth/plugins/email_otp.rb +261 -19
  30. data/lib/better_auth/plugins/expo.rb +17 -1
  31. data/lib/better_auth/plugins/generic_oauth.rb +67 -35
  32. data/lib/better_auth/plugins/jwt.rb +37 -4
  33. data/lib/better_auth/plugins/last_login_method.rb +2 -2
  34. data/lib/better_auth/plugins/magic_link.rb +66 -3
  35. data/lib/better_auth/plugins/mcp/authorization.rb +111 -0
  36. data/lib/better_auth/plugins/mcp/config.rb +51 -0
  37. data/lib/better_auth/plugins/mcp/consent.rb +31 -0
  38. data/lib/better_auth/plugins/mcp/legacy_aliases.rb +39 -0
  39. data/lib/better_auth/plugins/mcp/metadata.rb +81 -0
  40. data/lib/better_auth/plugins/mcp/registration.rb +31 -0
  41. data/lib/better_auth/plugins/mcp/resource_handler.rb +37 -0
  42. data/lib/better_auth/plugins/mcp/schema.rb +91 -0
  43. data/lib/better_auth/plugins/mcp/token.rb +108 -0
  44. data/lib/better_auth/plugins/mcp/userinfo.rb +37 -0
  45. data/lib/better_auth/plugins/mcp.rb +111 -263
  46. data/lib/better_auth/plugins/multi_session.rb +61 -3
  47. data/lib/better_auth/plugins/oauth_protocol.rb +173 -30
  48. data/lib/better_auth/plugins/oauth_proxy.rb +26 -6
  49. data/lib/better_auth/plugins/oidc_provider.rb +118 -14
  50. data/lib/better_auth/plugins/one_tap.rb +7 -2
  51. data/lib/better_auth/plugins/one_time_token.rb +42 -2
  52. data/lib/better_auth/plugins/open_api.rb +163 -318
  53. data/lib/better_auth/plugins/organization/schema.rb +6 -0
  54. data/lib/better_auth/plugins/organization.rb +186 -56
  55. data/lib/better_auth/plugins/phone_number.rb +141 -6
  56. data/lib/better_auth/plugins/siwe.rb +69 -3
  57. data/lib/better_auth/plugins/two_factor.rb +118 -41
  58. data/lib/better_auth/plugins/username.rb +57 -2
  59. data/lib/better_auth/rate_limiter.rb +38 -0
  60. data/lib/better_auth/request_state.rb +44 -0
  61. data/lib/better_auth/response.rb +42 -0
  62. data/lib/better_auth/router.rb +7 -1
  63. data/lib/better_auth/routes/account.rb +220 -42
  64. data/lib/better_auth/routes/email_verification.rb +98 -14
  65. data/lib/better_auth/routes/password.rb +126 -8
  66. data/lib/better_auth/routes/session.rb +128 -13
  67. data/lib/better_auth/routes/sign_in.rb +26 -2
  68. data/lib/better_auth/routes/sign_out.rb +13 -1
  69. data/lib/better_auth/routes/sign_up.rb +70 -4
  70. data/lib/better_auth/routes/social.rb +132 -7
  71. data/lib/better_auth/routes/user.rb +228 -20
  72. data/lib/better_auth/routes/validation.rb +50 -0
  73. data/lib/better_auth/secret_config.rb +115 -0
  74. data/lib/better_auth/session.rb +13 -2
  75. data/lib/better_auth/url_helpers.rb +206 -0
  76. data/lib/better_auth/version.rb +1 -1
  77. data/lib/better_auth.rb +12 -0
  78. metadata +23 -1
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ module Routes
5
+ REQUEST_EMAIL_PATTERN = /\A[^@\s]+@[^@\s]+\.[^@\s]+\z/
6
+
7
+ def self.request_body_schema(required_strings: [], required_nonempty_strings: [], email_strings: [], optional_strings: [])
8
+ ->(body) {
9
+ data = request_validation_hash(body)
10
+ return false unless required_strings.all? { |key| request_string?(data, key) }
11
+ return false unless required_nonempty_strings.all? { |key| request_string?(data, key) && !data[request_storage_key(key)].empty? }
12
+ return false unless email_strings.all? { |key| request_string?(data, key) && REQUEST_EMAIL_PATTERN.match?(data[request_storage_key(key)]) }
13
+ return false unless optional_strings.all? { |key| !data.key?(request_storage_key(key)) || request_string?(data, key) }
14
+
15
+ data
16
+ }
17
+ end
18
+
19
+ def self.request_query_schema(required_strings: [], optional_strings: [])
20
+ ->(query) {
21
+ data = request_validation_hash(query)
22
+ return false unless required_strings.all? { |key| request_string?(data, key) }
23
+ return false unless optional_strings.all? { |key| !data.key?(request_storage_key(key)) || request_string?(data, key) }
24
+
25
+ data
26
+ }
27
+ end
28
+
29
+ def self.request_validation_hash(value)
30
+ return {} unless value.is_a?(Hash)
31
+
32
+ value.each_with_object({}) do |(key, object_value), result|
33
+ result[request_storage_key(key)] = object_value
34
+ end
35
+ end
36
+
37
+ def self.request_string?(data, key)
38
+ data[request_storage_key(key)].is_a?(String)
39
+ end
40
+
41
+ def self.request_storage_key(key)
42
+ key.to_s
43
+ .gsub(/([a-z\d])([A-Z])/, "\\1_\\2")
44
+ .tr("-", "_")
45
+ .downcase
46
+ .split("_")
47
+ .then { |parts| ([parts.first] + parts.drop(1).map(&:capitalize)).join }
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ class SecretConfig
5
+ ENVELOPE_PREFIX = "$ba$"
6
+
7
+ attr_reader :keys, :current_version, :legacy_secret
8
+
9
+ def initialize(keys:, current_version:, legacy_secret: nil)
10
+ normalized_keys = keys.each_with_object({}) do |(version, value), result|
11
+ result[normalize_version!(version)] = value.to_s
12
+ end
13
+ @keys = normalized_keys.freeze
14
+ @current_version = normalize_version!(current_version)
15
+ @legacy_secret = legacy_secret unless legacy_secret.to_s.empty?
16
+ end
17
+
18
+ def current_secret
19
+ keys.fetch(current_version) do
20
+ raise Error, "Secret version #{current_version} not found in keys"
21
+ end
22
+ end
23
+
24
+ def all_secrets
25
+ entries = keys.map { |version, value| [version, value] }
26
+ entries << [-1, legacy_secret] if legacy_secret && !keys.value?(legacy_secret)
27
+ entries
28
+ end
29
+
30
+ def self.parse_env(value)
31
+ return nil if value.to_s.empty?
32
+
33
+ value.to_s.split(",").map do |entry|
34
+ entry = entry.strip
35
+ colon_index = entry.index(":")
36
+ raise Error, "Invalid BETTER_AUTH_SECRETS entry: \"#{entry}\". Expected format: \"<version>:<secret>\"" unless colon_index
37
+
38
+ version = entry[0...colon_index].strip
39
+ secret = entry[(colon_index + 1)..].to_s.strip
40
+ raise Error, "Empty secret value for version #{version} in BETTER_AUTH_SECRETS." if secret.empty?
41
+
42
+ {version: parse_version!(version, source: "BETTER_AUTH_SECRETS"), value: secret}
43
+ end
44
+ end
45
+
46
+ def self.validate_secrets!(secrets, logger: nil)
47
+ entries = Array(secrets)
48
+ raise Error, "`secrets` array must contain at least one entry." if entries.empty?
49
+
50
+ seen = {}
51
+ entries.each do |entry|
52
+ data = normalize_entry(entry)
53
+ version = parse_version!(data.fetch(:version), source: "`secrets`")
54
+ value = data.fetch(:value, nil).to_s
55
+ raise Error, "Empty secret value for version #{version} in `secrets`." if value.empty?
56
+ raise Error, "Duplicate version #{version} in `secrets`. Each version must be unique." if seen[version]
57
+
58
+ seen[version] = true
59
+ end
60
+
61
+ current = normalize_entry(entries.first)
62
+ current_version = parse_version!(current.fetch(:version), source: "`secrets`")
63
+ current_value = current.fetch(:value).to_s
64
+ warn(logger, "[better-auth] Warning: the current secret (version #{current_version}) should be at least 32 characters long for adequate security.") if current_value.length < 32
65
+ warn(logger, "[better-auth] Warning: the current secret appears low-entropy. Use a randomly generated secret for production.") if entropy(current_value) < 120
66
+ end
67
+
68
+ def self.build(secrets, legacy_secret, logger: nil)
69
+ validate_secrets!(secrets, logger: logger)
70
+ entries = Array(secrets).map { |entry| normalize_entry(entry) }
71
+ keys = entries.each_with_object({}) do |entry, result|
72
+ result[parse_version!(entry.fetch(:version), source: "`secrets`")] = entry.fetch(:value).to_s
73
+ end
74
+ current_version = parse_version!(entries.first.fetch(:version), source: "`secrets`")
75
+ legacy = (legacy_secret && legacy_secret != Configuration::DEFAULT_SECRET) ? legacy_secret : nil
76
+ new(keys: keys, current_version: current_version, legacy_secret: legacy)
77
+ end
78
+
79
+ def self.normalize_entry(entry)
80
+ raise Error, "Invalid `secrets` entry. Expected a hash with `version` and `value`." unless entry.is_a?(Hash)
81
+
82
+ entry.each_with_object({}) do |(key, value), result|
83
+ result[key.to_s.tr("-", "_").to_sym] = value
84
+ end
85
+ end
86
+
87
+ def self.parse_version!(value, source:)
88
+ text = value.to_s.strip
89
+ unless text.match?(/\A(?:0|[1-9]\d*)\z/)
90
+ raise Error, "Invalid version #{value} in #{source}. Version must be a non-negative integer."
91
+ end
92
+
93
+ text.to_i
94
+ end
95
+
96
+ def self.entropy(value)
97
+ unique = value.to_s.chars.uniq.length
98
+ return 0 if unique.zero?
99
+
100
+ Math.log2(unique**value.to_s.length)
101
+ end
102
+
103
+ def self.warn(logger, message)
104
+ if logger.respond_to?(:call)
105
+ logger.call(:warn, message)
106
+ elsif logger.respond_to?(:warn)
107
+ logger.warn(message)
108
+ end
109
+ end
110
+
111
+ def normalize_version!(version)
112
+ self.class.parse_version!(version, source: "`secrets`")
113
+ end
114
+ end
115
+ end
@@ -51,7 +51,7 @@ module BetterAuth
51
51
  return nil if payload["session"]["token"] && payload["session"]["token"] != token
52
52
 
53
53
  result = {session: payload["session"], user: payload["user"]}
54
- Cookies.set_cookie_cache(ctx, result, false) if should_refresh_cookie_cache?(config, payload)
54
+ result = refresh_cached_session(ctx, result) if should_refresh_cookie_cache?(config, payload)
55
55
  result
56
56
  end
57
57
 
@@ -89,6 +89,17 @@ module BetterAuth
89
89
  refreshed
90
90
  end
91
91
 
92
+ def refresh_cached_session(ctx, result)
93
+ now = Time.now
94
+ session = stringify_keys(result[:session]).merge(
95
+ "expiresAt" => now + ctx.context.session_config[:expires_in].to_i,
96
+ "updatedAt" => now
97
+ )
98
+ refreshed = {session: session, user: result[:user]}
99
+ Cookies.set_session_cookie(ctx, refreshed, Cookies.dont_remember?(ctx))
100
+ refreshed
101
+ end
102
+
92
103
  def should_refresh_cookie_cache?(config, payload)
93
104
  refresh_cache = config[:refresh_cache]
94
105
  return false if refresh_cache == false || refresh_cache.nil?
@@ -97,7 +108,7 @@ module BetterAuth
97
108
  update_age = if refresh_cache.is_a?(Hash)
98
109
  (refresh_cache[:update_age] || refresh_cache["updateAge"] || refresh_cache["update_age"]).to_i
99
110
  else
100
- (max_age * 0.8).to_i
111
+ (max_age * 0.2).to_i
101
112
  end
102
113
  updated_at = payload["updatedAt"].to_i
103
114
  updated_at.positive? && updated_at + (update_age * 1000) <= (Time.now.to_f * 1000).to_i
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ module BetterAuth
6
+ module URLHelpers
7
+ module_function
8
+
9
+ def valid_proxy_header?(header, type)
10
+ value = header.to_s
11
+ return false if value.strip.empty?
12
+
13
+ case type.to_sym
14
+ when :proto
15
+ ["http", "https"].include?(value)
16
+ when :host
17
+ return false if value.match?(/\.\.|\0|\s|\A[.]|[<>'"]|javascript:|file:|data:/i)
18
+ return false if value.match?(%r{[/\\]})
19
+
20
+ patterns = [
21
+ /\A[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*(:[0-9]{1,5})?\z/,
22
+ /\A(\d{1,3}\.){3}\d{1,3}(:[0-9]{1,5})?\z/,
23
+ /\A\[[0-9a-fA-F:]+\](:[0-9]{1,5})?\z/,
24
+ /\Alocalhost(:[0-9]{1,5})?\z/i
25
+ ]
26
+ patterns.any? { |pattern| value.match?(pattern) } && valid_port?(value)
27
+ else
28
+ false
29
+ end
30
+ end
31
+
32
+ def matches_host_pattern?(host, pattern)
33
+ return false if host.to_s.empty? || pattern.to_s.empty?
34
+
35
+ normalized_host = normalize_host_pattern_value(host)
36
+ normalized_pattern = normalize_host_pattern_value(pattern)
37
+ regex = Regexp.escape(normalized_pattern)
38
+ .gsub("\\*", ".*")
39
+ .gsub("\\?", ".")
40
+ !!normalized_host.match?(/\A#{regex}\z/i)
41
+ end
42
+
43
+ def host_from_source(source, trusted_proxy_headers: false)
44
+ headers = headers_from_source(source)
45
+ if trusted_proxy_headers
46
+ forwarded_host = header_value(headers, "x-forwarded-host")
47
+ return forwarded_host if forwarded_host && valid_proxy_header?(forwarded_host, :host)
48
+ end
49
+
50
+ host = header_value(headers, "host")
51
+ return host if host && valid_proxy_header?(host, :host)
52
+
53
+ uri_host(source_url(source))
54
+ end
55
+
56
+ def protocol_from_source(source, config_protocol: nil, trusted_proxy_headers: false)
57
+ return config_protocol if ["http", "https"].include?(config_protocol)
58
+
59
+ headers = headers_from_source(source)
60
+ if trusted_proxy_headers
61
+ forwarded_proto = header_value(headers, "x-forwarded-proto")
62
+ return forwarded_proto if forwarded_proto && valid_proxy_header?(forwarded_proto, :proto)
63
+ end
64
+
65
+ protocol = uri_scheme(source_url(source))
66
+ return protocol if ["http", "https"].include?(protocol)
67
+
68
+ host = host_from_source(source, trusted_proxy_headers: trusted_proxy_headers)
69
+ return "http" if host && loopback_for_dev_scheme?(host)
70
+
71
+ "https"
72
+ end
73
+
74
+ def resolve_base_url(config, base_path, source = nil, load_env: true, trusted_proxy_headers: false)
75
+ if dynamic_config?(config)
76
+ return resolve_dynamic_base_url(config, source, base_path, trusted_proxy_headers: trusted_proxy_headers) if source
77
+ return with_path(config[:fallback] || config["fallback"], base_path) if config[:fallback] || config["fallback"]
78
+
79
+ return env_base_url(base_path) if load_env
80
+ return nil
81
+ end
82
+
83
+ return with_path(config, base_path) if config.is_a?(String)
84
+ return env_base_url(base_path) if load_env
85
+ return with_path(origin(source_url(source)), base_path) if source
86
+
87
+ nil
88
+ end
89
+
90
+ def resolve_dynamic_base_url(config, source, base_path, trusted_proxy_headers: false)
91
+ host = host_from_source(source, trusted_proxy_headers: trusted_proxy_headers)
92
+ fallback = config[:fallback] || config["fallback"]
93
+ raise Error, "Could not determine host from request headers. Please provide a fallback URL in your baseURL config." unless host || fallback
94
+
95
+ allowed_hosts = config[:allowed_hosts] || config["allowed_hosts"] || config[:allowedHosts] || config["allowedHosts"] || []
96
+ if host && allowed_hosts.any? { |pattern| matches_host_pattern?(host, pattern) }
97
+ protocol = protocol_from_source(source, config_protocol: config[:protocol] || config["protocol"], trusted_proxy_headers: trusted_proxy_headers)
98
+ return with_path("#{protocol}://#{host}", base_path)
99
+ end
100
+
101
+ return with_path(fallback, base_path) if fallback
102
+
103
+ raise Error, "Host \"#{host}\" is not in the allowed hosts list."
104
+ end
105
+
106
+ def with_path(url, path = "/api/auth")
107
+ parsed = URI.parse(url.to_s)
108
+ raise Error, "Invalid base URL: #{url}. URL must include 'http://' or 'https://'" unless ["http", "https"].include?(parsed.scheme)
109
+
110
+ current_path = parsed.path.to_s.gsub(%r{/+\z}, "")
111
+ return url.to_s if !current_path.empty? && current_path != "/"
112
+
113
+ trimmed = url.to_s.gsub(%r{/+\z}, "")
114
+ return trimmed if path.to_s.empty? || path == "/"
115
+
116
+ suffix = path.start_with?("/") ? path : "/#{path}"
117
+ "#{trimmed}#{suffix}"
118
+ rescue URI::InvalidURIError
119
+ raise Error, "Invalid base URL: #{url}. Please provide a valid base URL."
120
+ end
121
+
122
+ def origin(url)
123
+ parsed = URI.parse(url.to_s)
124
+ return nil unless ["http", "https"].include?(parsed.scheme)
125
+
126
+ port = parsed.port
127
+ default_port = (parsed.scheme == "http" && port == 80) || (parsed.scheme == "https" && port == 443)
128
+ default_port ? "#{parsed.scheme}://#{parsed.host}" : "#{parsed.scheme}://#{parsed.host}:#{port}"
129
+ rescue URI::InvalidURIError
130
+ nil
131
+ end
132
+
133
+ def uri_host(url)
134
+ parsed = URI.parse(url.to_s)
135
+ return nil unless parsed.host
136
+
137
+ default_port = (parsed.scheme == "http" && parsed.port == 80) || (parsed.scheme == "https" && parsed.port == 443)
138
+ default_port ? parsed.host : "#{parsed.host}:#{parsed.port}"
139
+ rescue URI::InvalidURIError
140
+ nil
141
+ end
142
+
143
+ def uri_scheme(url)
144
+ URI.parse(url.to_s).scheme
145
+ rescue URI::InvalidURIError
146
+ nil
147
+ end
148
+
149
+ def normalize_host_pattern_value(value)
150
+ value.to_s.sub(%r{\Ahttps?://}i, "").split("/").first.to_s.downcase
151
+ end
152
+
153
+ def headers_from_source(source)
154
+ return {} unless source
155
+ return source.headers if source.respond_to?(:headers)
156
+ return rack_request_headers(source) if source.respond_to?(:get_header)
157
+ return source if source.is_a?(Hash)
158
+
159
+ {}
160
+ end
161
+
162
+ def header_value(headers, key)
163
+ if headers.respond_to?(:get)
164
+ headers.get(key)
165
+ else
166
+ headers[key] || headers[key.to_s] || headers[key.to_s.downcase] || headers[key.to_s.upcase] || headers[key.tr("-", "_").upcase]
167
+ end
168
+ end
169
+
170
+ def source_url(source)
171
+ return source.url if source.respond_to?(:url)
172
+
173
+ source.get_header("REQUEST_URI") if source.respond_to?(:get_header)
174
+ end
175
+
176
+ def dynamic_config?(config)
177
+ config.is_a?(Hash) && (config.key?(:allowed_hosts) || config.key?("allowed_hosts") || config.key?(:allowedHosts) || config.key?("allowedHosts"))
178
+ end
179
+
180
+ def env_base_url(base_path)
181
+ url = ENV["BETTER_AUTH_URL"] || ENV["NEXT_PUBLIC_BETTER_AUTH_URL"] || ENV["PUBLIC_BETTER_AUTH_URL"] || ENV["NUXT_PUBLIC_BETTER_AUTH_URL"] || ENV["NUXT_PUBLIC_AUTH_URL"]
182
+ url ||= ENV["BASE_URL"] if ENV["BASE_URL"] && ENV["BASE_URL"] != "/"
183
+ url ? with_path(url, base_path) : nil
184
+ end
185
+
186
+ def loopback_for_dev_scheme?(host)
187
+ hostname = host.to_s.sub(/:\d+\z/, "").sub(/\A\[/, "").sub(/\]\z/, "").downcase
188
+ hostname == "localhost" || hostname.end_with?(".localhost") || hostname == "::1" || hostname.start_with?("127.")
189
+ end
190
+
191
+ def valid_port?(host)
192
+ port = host[/:(\d{1,5})\z/, 1]
193
+ return true unless port
194
+
195
+ port.to_i.between?(1, 65_535)
196
+ end
197
+
198
+ def rack_request_headers(source)
199
+ {
200
+ "x-forwarded-host" => source.get_header("HTTP_X_FORWARDED_HOST"),
201
+ "x-forwarded-proto" => source.get_header("HTTP_X_FORWARDED_PROTO"),
202
+ "host" => source.get_header("HTTP_HOST")
203
+ }.compact
204
+ end
205
+ end
206
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BetterAuth
4
- VERSION = "0.3.0"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/better_auth.rb CHANGED
@@ -4,7 +4,17 @@ require_relative "better_auth/version"
4
4
  require_relative "better_auth/core"
5
5
  require_relative "better_auth/error"
6
6
  require_relative "better_auth/api_error"
7
+ require_relative "better_auth/secret_config"
7
8
  require_relative "better_auth/crypto"
9
+ require_relative "better_auth/host"
10
+ require_relative "better_auth/url_helpers"
11
+ require_relative "better_auth/request_state"
12
+ require_relative "better_auth/response"
13
+ require_relative "better_auth/async"
14
+ require_relative "better_auth/deprecate"
15
+ require_relative "better_auth/logger"
16
+ require_relative "better_auth/instrumentation"
17
+ require_relative "better_auth/oauth2"
8
18
  require_relative "better_auth/password"
9
19
  require_relative "better_auth/plugin"
10
20
  require_relative "better_auth/configuration"
@@ -40,6 +50,7 @@ require_relative "better_auth/plugins/one_time_token"
40
50
  require_relative "better_auth/plugins/one_tap"
41
51
  require_relative "better_auth/plugins/siwe"
42
52
  require_relative "better_auth/plugins/generic_oauth"
53
+ require_relative "better_auth/plugins/dub"
43
54
  require_relative "better_auth/plugins/oauth_proxy"
44
55
  require_relative "better_auth/plugins/passkey"
45
56
  require_relative "better_auth/plugins/organization/schema"
@@ -68,6 +79,7 @@ require_relative "better_auth/session_store"
68
79
  require_relative "better_auth/cookies"
69
80
  require_relative "better_auth/session"
70
81
  require_relative "better_auth/endpoint"
82
+ require_relative "better_auth/routes/validation"
71
83
  require_relative "better_auth/routes/ok"
72
84
  require_relative "better_auth/routes/error"
73
85
  require_relative "better_auth/routes/sign_up"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: better_auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Sala
@@ -268,6 +268,7 @@ files:
268
268
  - lib/better_auth/adapters/sqlite.rb
269
269
  - lib/better_auth/api.rb
270
270
  - lib/better_auth/api_error.rb
271
+ - lib/better_auth/async.rb
271
272
  - lib/better_auth/auth.rb
272
273
  - lib/better_auth/configuration.rb
273
274
  - lib/better_auth/context.rb
@@ -276,9 +277,14 @@ files:
276
277
  - lib/better_auth/crypto.rb
277
278
  - lib/better_auth/crypto/jwe.rb
278
279
  - lib/better_auth/database_hooks.rb
280
+ - lib/better_auth/deprecate.rb
279
281
  - lib/better_auth/endpoint.rb
280
282
  - lib/better_auth/error.rb
283
+ - lib/better_auth/host.rb
284
+ - lib/better_auth/instrumentation.rb
285
+ - lib/better_auth/logger.rb
281
286
  - lib/better_auth/middleware/origin_check.rb
287
+ - lib/better_auth/oauth2.rb
282
288
  - lib/better_auth/password.rb
283
289
  - lib/better_auth/plugin.rb
284
290
  - lib/better_auth/plugin_context.rb
@@ -294,6 +300,7 @@ files:
294
300
  - lib/better_auth/plugins/captcha.rb
295
301
  - lib/better_auth/plugins/custom_session.rb
296
302
  - lib/better_auth/plugins/device_authorization.rb
303
+ - lib/better_auth/plugins/dub.rb
297
304
  - lib/better_auth/plugins/email_otp.rb
298
305
  - lib/better_auth/plugins/expo.rb
299
306
  - lib/better_auth/plugins/generic_oauth.rb
@@ -302,6 +309,16 @@ files:
302
309
  - lib/better_auth/plugins/last_login_method.rb
303
310
  - lib/better_auth/plugins/magic_link.rb
304
311
  - lib/better_auth/plugins/mcp.rb
312
+ - lib/better_auth/plugins/mcp/authorization.rb
313
+ - lib/better_auth/plugins/mcp/config.rb
314
+ - lib/better_auth/plugins/mcp/consent.rb
315
+ - lib/better_auth/plugins/mcp/legacy_aliases.rb
316
+ - lib/better_auth/plugins/mcp/metadata.rb
317
+ - lib/better_auth/plugins/mcp/registration.rb
318
+ - lib/better_auth/plugins/mcp/resource_handler.rb
319
+ - lib/better_auth/plugins/mcp/schema.rb
320
+ - lib/better_auth/plugins/mcp/token.rb
321
+ - lib/better_auth/plugins/mcp/userinfo.rb
305
322
  - lib/better_auth/plugins/multi_session.rb
306
323
  - lib/better_auth/plugins/oauth_protocol.rb
307
324
  - lib/better_auth/plugins/oauth_provider.rb
@@ -322,6 +339,8 @@ files:
322
339
  - lib/better_auth/plugins/username.rb
323
340
  - lib/better_auth/rate_limiter.rb
324
341
  - lib/better_auth/request_ip.rb
342
+ - lib/better_auth/request_state.rb
343
+ - lib/better_auth/response.rb
325
344
  - lib/better_auth/router.rb
326
345
  - lib/better_auth/routes/account.rb
327
346
  - lib/better_auth/routes/email_verification.rb
@@ -334,8 +353,10 @@ files:
334
353
  - lib/better_auth/routes/sign_up.rb
335
354
  - lib/better_auth/routes/social.rb
336
355
  - lib/better_auth/routes/user.rb
356
+ - lib/better_auth/routes/validation.rb
337
357
  - lib/better_auth/schema.rb
338
358
  - lib/better_auth/schema/sql.rb
359
+ - lib/better_auth/secret_config.rb
339
360
  - lib/better_auth/session.rb
340
361
  - lib/better_auth/session_store.rb
341
362
  - lib/better_auth/social_providers.rb
@@ -375,6 +396,7 @@ files:
375
396
  - lib/better_auth/social_providers/vk.rb
376
397
  - lib/better_auth/social_providers/wechat.rb
377
398
  - lib/better_auth/social_providers/zoom.rb
399
+ - lib/better_auth/url_helpers.rb
378
400
  - lib/better_auth/version.rb
379
401
  homepage: https://github.com/sebasxsala/better-auth
380
402
  licenses: