better_auth-grape 0.10.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cebdcf11ac8f2e83526cb61c135d129fd04c67118dd1c95d2d36b1ad47cf887a
4
+ data.tar.gz: 8a110152aa341e4a70e1621057e98cffd6902634a7d0b852b6471cbffe530f27
5
+ SHA512:
6
+ metadata.gz: 1423fd9801e4bad997878b453687d154e54aa81da8fa6628b9dcb1988b8d262c1f636fa03cedadf46f95ce7976600d290da2615c9e194b07e4da4eb3e2e9fbaf
7
+ data.tar.gz: dde1aba6f7e74a99a5d28cee11f6298923ba66d8b9536fc4559d1579e4feb25b932dc452f342fb22a1bd20088b7a66211bce7022bfd18ef2a32b7535f2b1f21a
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## 0.10.0
4
+
5
+ - Improved cookie handling for mounted Grape apps.
6
+ - Added migration planning support and broader integration coverage.
7
+
8
+ ## 0.8.0
9
+
10
+ - Initial Grape integration package.
data/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # better_auth-grape
2
+
3
+ Grape integration for Better Auth Ruby.
4
+
5
+ ## Installation
6
+
7
+ ```ruby
8
+ gem "better_auth-grape"
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ruby
14
+ require "grape"
15
+ require "better_auth/grape"
16
+
17
+ class API < Grape::API
18
+ include BetterAuth::Grape
19
+
20
+ format :json
21
+
22
+ better_auth at: "/api/auth" do |config|
23
+ config.secret = ENV.fetch("BETTER_AUTH_SECRET")
24
+ config.base_url = ENV.fetch("BETTER_AUTH_URL", "http://localhost:9292")
25
+ config.database = ->(options) {
26
+ BetterAuth::Adapters::Postgres.new(options, url: ENV.fetch("DATABASE_URL"))
27
+ }
28
+ config.email_and_password = {
29
+ enabled: true
30
+ }
31
+ config.plugins = []
32
+ end
33
+
34
+ get "/dashboard" do
35
+ require_authentication
36
+ {email: current_user.fetch("email")}
37
+ end
38
+ end
39
+ ```
40
+
41
+ When the API uses Grape path prefixes or path-based versioning, pass a relative
42
+ auth path and the adapter mounts below the effective Grape path:
43
+
44
+ ```ruby
45
+ class API < Grape::API
46
+ include BetterAuth::Grape
47
+
48
+ prefix :api
49
+ version "v1", using: :path
50
+
51
+ better_auth at: "/auth" do |config|
52
+ config.secret = ENV.fetch("BETTER_AUTH_SECRET")
53
+ config.base_url = ENV.fetch("BETTER_AUTH_URL")
54
+ config.database = :memory
55
+ end
56
+ end
57
+ ```
58
+
59
+ This exposes Better Auth at `/api/v1/auth`.
60
+
61
+ Load Rake tasks from your application Rakefile:
62
+
63
+ ```ruby
64
+ require "better_auth/grape/tasks"
65
+ ```
66
+
67
+ ```bash
68
+ rake better_auth:install
69
+ BETTER_AUTH_DIALECT=postgres rake better_auth:generate:migration
70
+ rake better_auth:migrate
71
+ rake better_auth:migrate:status
72
+ rake better_auth:doctor
73
+ ```
74
+
75
+ When a SQL adapter is configured, migration generation introspects the current
76
+ database and emits only missing Better Auth tables, columns, and indexes.
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ module Grape
5
+ class Configuration
6
+ AUTH_OPTION_NAMES = %i[
7
+ app_name
8
+ base_url
9
+ base_path
10
+ secret
11
+ secrets
12
+ database
13
+ plugins
14
+ trusted_origins
15
+ rate_limit
16
+ session
17
+ account
18
+ user
19
+ verification
20
+ advanced
21
+ email_and_password
22
+ password_hasher
23
+ email_verification
24
+ social_providers
25
+ experimental
26
+ secondary_storage
27
+ database_hooks
28
+ hooks
29
+ on_api_error
30
+ disabled_paths
31
+ logger
32
+ ].freeze
33
+
34
+ attr_accessor(*AUTH_OPTION_NAMES)
35
+
36
+ def initialize
37
+ @base_path = BetterAuth::Configuration::DEFAULT_BASE_PATH
38
+ @plugins = []
39
+ @trusted_origins = []
40
+ end
41
+
42
+ def to_auth_options
43
+ AUTH_OPTION_NAMES.each_with_object({}) do |name, options|
44
+ value = public_send(name)
45
+ next if value.nil?
46
+ next if value.respond_to?(:empty?) && value.empty?
47
+
48
+ options[name] = value
49
+ end
50
+ end
51
+
52
+ def copy
53
+ self.class.new.tap do |copy|
54
+ AUTH_OPTION_NAMES.each do |name|
55
+ value = public_send(name)
56
+ copy.public_send("#{name}=", deep_dup(value))
57
+ end
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def deep_dup(value)
64
+ case value
65
+ when Hash
66
+ value.transform_values { |entry| deep_dup(entry) }
67
+ when Array
68
+ value.map { |entry| deep_dup(entry) }
69
+ else
70
+ value
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ module Grape
5
+ module Extension
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ base.helpers Helpers if base.respond_to?(:helpers)
9
+ end
10
+
11
+ module ClassMethods
12
+ attr_reader :better_auth_auth, :better_auth_mount_path
13
+
14
+ def better_auth(at: BetterAuth::Configuration::DEFAULT_BASE_PATH, auth: nil, **overrides)
15
+ mount_path = effective_better_auth_mount_path(at)
16
+ if mount_path == "/"
17
+ raise ArgumentError,
18
+ "better_auth mount path cannot be '/' (it would capture every request). " \
19
+ "Use a prefix such as #{BetterAuth::Configuration::DEFAULT_BASE_PATH.inspect}."
20
+ end
21
+ raise ArgumentError, "better_auth is already configured for this API" if @better_auth_auth
22
+
23
+ config = BetterAuth::Grape.configuration.copy
24
+ yield config if block_given?
25
+ config.base_path = mount_path
26
+ options = config.to_auth_options.merge(overrides).merge(base_path: mount_path)
27
+ auth_instance = auth || BetterAuth.auth(options)
28
+ @better_auth_auth = auth_instance
29
+ @better_auth_mount_path = mount_path
30
+
31
+ mounted_app = BetterAuth::Grape::MountedApp.new(auth_instance, mount_path: mount_path)
32
+ helpers do
33
+ define_method(:better_auth_auth) { auth_instance }
34
+ end
35
+ mount({mounted_app => mount_path})
36
+ route_setting :better_auth_internal, true
37
+ route(:any, "/*better_auth_path") do
38
+ path_info = env.fetch("PATH_INFO", "").to_s
39
+ normalized_path_info = path_info.start_with?("/") ? path_info.squeeze("/") : "/#{path_info}".squeeze("/")
40
+ script_name = env.fetch("SCRIPT_NAME", "").to_s
41
+ normalized_script_name = script_name.start_with?("/") ? script_name.squeeze("/") : "/#{script_name}".squeeze("/")
42
+ unless normalized_path_info == mount_path ||
43
+ normalized_path_info.start_with?("#{mount_path}/") ||
44
+ normalized_script_name == mount_path ||
45
+ normalized_script_name.end_with?(mount_path)
46
+ error!({error: "Not Found"}, 404)
47
+ end
48
+
49
+ rack_status, rack_headers, rack_body = mounted_app.call(env)
50
+ status rack_status
51
+ rack_headers.each { |key, value| header key, value }
52
+ env[::Grape::Env::API_FORMAT] = :txt
53
+ body rack_body.each.to_a.join
54
+ nil
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def effective_better_auth_mount_path(path)
61
+ mount_path = normalize_better_auth_mount_path(path)
62
+ api_prefix = better_auth_api_prefix
63
+ return mount_path if api_prefix == "/"
64
+ return mount_path if mount_path == api_prefix || mount_path.start_with?("#{api_prefix}/")
65
+
66
+ normalize_better_auth_mount_path("#{api_prefix}/#{mount_path.delete_prefix("/")}")
67
+ end
68
+
69
+ def better_auth_api_prefix
70
+ parts = [better_auth_root_prefix]
71
+ version_prefix = better_auth_path_version_prefix
72
+ parts << version_prefix unless version_prefix == "/"
73
+ normalize_better_auth_mount_path(parts.reject { |part| part == "/" }.join("/"))
74
+ end
75
+
76
+ def better_auth_root_prefix
77
+ return "/" unless respond_to?(:prefix)
78
+
79
+ configured_prefix = prefix
80
+ return "/" if configured_prefix.nil? || configured_prefix.to_s.empty?
81
+
82
+ normalize_better_auth_mount_path(configured_prefix)
83
+ end
84
+
85
+ def better_auth_path_version_prefix
86
+ settings = inheritable_setting.namespace_inheritable if respond_to?(:inheritable_setting)
87
+ version_options = settings&.[](:version_options) || {}
88
+ return "/" unless version_options[:using]&.to_sym == :path
89
+
90
+ versions = settings&.[](:version)
91
+ version = Array(versions).first
92
+ return "/" if version.nil? || version.to_s.empty?
93
+
94
+ normalize_better_auth_mount_path(version)
95
+ end
96
+
97
+ def normalize_better_auth_mount_path(path)
98
+ normalized = path.to_s
99
+ normalized = "/#{normalized}" unless normalized.start_with?("/")
100
+ normalized = normalized.squeeze("/")
101
+ (normalized == "/") ? normalized : normalized.delete_suffix("/")
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module BetterAuth
6
+ module Grape
7
+ module Helpers
8
+ def current_session
9
+ data = better_auth_session_data
10
+ data&.fetch(:session, nil) || data&.fetch("session", nil)
11
+ end
12
+
13
+ def current_user
14
+ data = better_auth_session_data
15
+ data&.fetch(:user, nil) || data&.fetch("user", nil)
16
+ end
17
+
18
+ def authenticated?
19
+ !current_user.nil?
20
+ end
21
+
22
+ def require_authentication
23
+ return true if authenticated?
24
+
25
+ error = BetterAuth::APIError.new("UNAUTHORIZED")
26
+ if prefers_json_response?
27
+ error!(error.to_h, 401, {"content-type" => "application/json"})
28
+ else
29
+ error!("", 401)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def prefers_json_response?
36
+ accept = env["HTTP_ACCEPT"].to_s
37
+ return false if accept.empty? || accept == "*/*"
38
+
39
+ accept.split(",").any? do |entry|
40
+ media_type = entry.split(";", 2).first.to_s.strip
41
+ media_type == "application/json" || media_type.end_with?("+json")
42
+ end
43
+ end
44
+
45
+ def better_auth_session_data
46
+ return env["better_auth.session"] if env.key?("better_auth.session")
47
+
48
+ env["better_auth.session"] = resolve_better_auth_session
49
+ end
50
+
51
+ def resolve_better_auth_session
52
+ auth = better_auth_auth
53
+ result = auth.api.get_session(
54
+ request: Rack::Request.new(env),
55
+ method: "GET",
56
+ as_response: true
57
+ )
58
+ return resolve_better_auth_response(result) if result.respond_to?(:headers) && result.respond_to?(:body)
59
+
60
+ apply_better_auth_response_headers(result[:headers] || result["headers"] || {})
61
+ result[:response] || result["response"]
62
+ end
63
+
64
+ def resolve_better_auth_response(response)
65
+ apply_better_auth_response_headers(response.headers || {})
66
+ body = response.body.respond_to?(:join) ? response.body.join : response.body.to_s
67
+ payload = body.empty? ? nil : JSON.parse(body)
68
+ raise_better_auth_response_error(response, payload) if response.status.to_i >= 400
69
+
70
+ payload
71
+ end
72
+
73
+ def raise_better_auth_response_error(response, payload)
74
+ payload = payload.is_a?(Hash) ? payload : {}
75
+ status = BetterAuth::APIError::STATUS_CODES.key(response.status.to_i) || "INTERNAL_SERVER_ERROR"
76
+ raise BetterAuth::APIError.new(
77
+ status,
78
+ message: payload["message"],
79
+ code: payload["code"],
80
+ headers: response.headers || {}
81
+ )
82
+ end
83
+
84
+ def apply_better_auth_response_headers(headers)
85
+ set_cookie = headers["set-cookie"] || headers["Set-Cookie"] || headers[:set_cookie]
86
+ return if set_cookie.to_s.empty?
87
+
88
+ existing = header["Set-Cookie"].to_s
89
+ header "Set-Cookie", [existing, set_cookie.to_s].reject(&:empty?).join("\n")
90
+ env["better_auth.set_cookie"] = [env["better_auth.set_cookie"], set_cookie.to_s].compact.join("\n")
91
+ end
92
+
93
+ def better_auth_auth
94
+ self.class.respond_to?(:better_auth_auth) ? self.class.better_auth_auth : BetterAuth::Grape.auth
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "better_auth/sql_migration"
4
+
5
+ module BetterAuth
6
+ module Grape
7
+ module Migration
8
+ DEFAULT_MIGRATIONS_PATH = BetterAuth::SQLMigration::DEFAULT_MIGRATIONS_PATH
9
+ UnsupportedAdapterError = BetterAuth::SQLMigration::UnsupportedAdapterError
10
+ GENERATOR = "better_auth-grape"
11
+
12
+ module_function
13
+
14
+ def render(options, dialect:)
15
+ BetterAuth::SQLMigration.render(options, dialect: dialect, generator: GENERATOR)
16
+ end
17
+
18
+ def generate(options, dialect:, migrations_path: DEFAULT_MIGRATIONS_PATH, timestamp: Time.now.utc.strftime("%Y%m%d%H%M%S"), connection: nil)
19
+ BetterAuth::SQLMigration.generate(
20
+ options,
21
+ dialect: dialect,
22
+ generator: GENERATOR,
23
+ migrations_path: migrations_path,
24
+ timestamp: timestamp,
25
+ connection: connection
26
+ )
27
+ end
28
+
29
+ def migrate(auth_or_options, migrations_path: DEFAULT_MIGRATIONS_PATH)
30
+ BetterAuth::SQLMigration.migrate(auth_or_options, migrations_path: migrations_path)
31
+ end
32
+
33
+ def statements(sql)
34
+ BetterAuth::SQLMigration.statements(sql)
35
+ end
36
+
37
+ def normalize_dialect(value)
38
+ BetterAuth::SQLMigration.normalize_dialect(value)
39
+ end
40
+
41
+ def method_missing(name, *args, **kwargs, &block)
42
+ return BetterAuth::SQLMigration.public_send(name, *args, **kwargs, &block) if BetterAuth::SQLMigration.respond_to?(name)
43
+
44
+ super
45
+ end
46
+
47
+ def respond_to_missing?(name, include_private = false)
48
+ BetterAuth::SQLMigration.respond_to?(name, include_private) || super
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module BetterAuth
6
+ module Grape
7
+ class MountedApp
8
+ def initialize(auth, mount_path:)
9
+ @auth = auth
10
+ @mount_path = normalize_path(mount_path)
11
+ end
12
+
13
+ def call(env)
14
+ rewritten_path = mounted_path_info(env)
15
+ next_env = env.merge("PATH_INFO" => rewritten_path)
16
+ next_env["SCRIPT_NAME"] = "" if shared_mount_rewrite?(env, rewritten_path)
17
+ @auth.call(next_env)
18
+ rescue BetterAuth::APIError, JSON::ParserError
19
+ raise
20
+ rescue => error
21
+ handle_unexpected_error(error, env)
22
+ end
23
+
24
+ private
25
+
26
+ def mounted_path_info(env)
27
+ path_info = normalize_path(env["PATH_INFO"], trim: false)
28
+ comparable_path = normalize_path(env["PATH_INFO"], trim: true)
29
+ return path_info if comparable_path == @mount_path || comparable_path.start_with?("#{@mount_path}/")
30
+
31
+ script_name = normalize_path(env["SCRIPT_NAME"], trim: true)
32
+ return normalize_path("#{@mount_path}/#{path_info.delete_prefix("/")}", trim: false) if script_mount_matches?(script_name)
33
+
34
+ prefix = (script_name == "/") ? @mount_path : script_name
35
+ return path_info if comparable_path == prefix || comparable_path.start_with?("#{prefix}/")
36
+
37
+ normalize_path("#{prefix}/#{path_info.delete_prefix("/")}", trim: false)
38
+ end
39
+
40
+ def script_mount_matches?(script_name)
41
+ script_name == @mount_path || script_name.end_with?(@mount_path.to_s)
42
+ end
43
+
44
+ def shared_mount_rewrite?(env, rewritten_path)
45
+ script_name = normalize_path(env["SCRIPT_NAME"], trim: true)
46
+ original_path = normalize_path(env["PATH_INFO"], trim: true)
47
+ script_name != "/" &&
48
+ !original_path.start_with?("#{@mount_path}/") &&
49
+ rewritten_path.start_with?("#{@mount_path}/")
50
+ end
51
+
52
+ def normalize_path(path, trim: true)
53
+ normalized = path.to_s
54
+ normalized = "/#{normalized}" unless normalized.start_with?("/")
55
+ normalized = normalized.squeeze("/")
56
+ normalized = normalized.delete_suffix("/") if trim && normalized != "/"
57
+ normalized.empty? ? "/" : normalized
58
+ end
59
+
60
+ def handle_unexpected_error(error, env)
61
+ options = @auth.options
62
+ on_api_error = options.on_api_error || {}
63
+ raise error if on_api_error[:throw] || on_api_error["throw"]
64
+
65
+ callback = on_api_error[:on_error] || on_api_error[:onError] || on_api_error["on_error"] || on_api_error["onError"]
66
+ callback.call(error, error_context(env)) if callback.respond_to?(:call)
67
+
68
+ api_error = BetterAuth::APIError.new("INTERNAL_SERVER_ERROR")
69
+ [
70
+ api_error.status_code,
71
+ {"content-type" => "application/json"},
72
+ [JSON.generate(api_error.to_h)]
73
+ ]
74
+ end
75
+
76
+ def error_context(env)
77
+ path = mounted_path_info(env)
78
+ route_path = if path == @mount_path
79
+ "/"
80
+ else
81
+ path.delete_prefix(@mount_path)
82
+ end
83
+ Struct.new(:path, :env).new(normalize_path(route_path), env)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "rake"
5
+ require "better_auth/grape"
6
+
7
+ namespace :better_auth do
8
+ desc "Create the Better Auth Grape config and migration directory"
9
+ task :install do
10
+ config_path = "config/better_auth.rb"
11
+ FileUtils.mkdir_p(File.dirname(config_path))
12
+ if File.exist?(config_path)
13
+ puts "skip #{config_path} already exists"
14
+ else
15
+ File.write(config_path, BetterAuth::Grape.default_config_template)
16
+ puts "create #{config_path}"
17
+ end
18
+
19
+ FileUtils.mkdir_p(BetterAuth::Grape::Migration::DEFAULT_MIGRATIONS_PATH)
20
+ end
21
+
22
+ namespace :generate do
23
+ desc "Create the Better Auth SQL migration"
24
+ task :migration do
25
+ BetterAuth::Grape.load_app_config!
26
+ dialect = BetterAuth::Grape::Migration.normalize_dialect(BetterAuth::Env.get("BETTER_AUTH_DIALECT") || BetterAuth::Env.get("BETTER_AUTH_DATABASE_DIALECT") || "postgres")
27
+ config = BetterAuth::Grape.migration_configuration
28
+ adapter = begin
29
+ BetterAuth::Grape.auth.context.adapter
30
+ rescue
31
+ nil
32
+ end
33
+ connection = if adapter&.respond_to?(:connection) && adapter.respond_to?(:dialect) && BetterAuth::Grape::Migration.normalize_dialect(adapter.dialect) == dialect
34
+ adapter.connection
35
+ end
36
+ path = BetterAuth::Grape::Migration.generate(config, dialect: dialect, connection: connection)
37
+ puts(path ? "create #{path}" : "no migrations needed")
38
+ end
39
+ end
40
+
41
+ desc "Run pending Better Auth SQL migrations"
42
+ task :migrate do
43
+ BetterAuth::Grape.load_app_config!
44
+ BetterAuth::Grape::Migration.migrate(BetterAuth::Grape.auth)
45
+ end
46
+
47
+ namespace :migrate do
48
+ desc "Print pending Better Auth SQL migration status"
49
+ task :status do
50
+ BetterAuth::Grape.load_app_config!
51
+ auth = BetterAuth::Grape.auth
52
+ adapter = auth.context.adapter
53
+ unless adapter.respond_to?(:connection) && adapter.respond_to?(:dialect)
54
+ raise BetterAuth::Grape::Migration::UnsupportedAdapterError, "Better Auth SQL migrations require core SQL adapters with connection and dialect support"
55
+ end
56
+ plan = BetterAuth::Grape::Migration.plan(auth.options, connection: adapter.connection, dialect: adapter.dialect)
57
+ if plan.empty?
58
+ puts "No migrations needed."
59
+ else
60
+ plan.to_create.each { |change| puts "create table #{change.table_name}" }
61
+ plan.to_add.each { |change| puts "add #{change.fields.keys.join(", ")} to #{change.table_name}" }
62
+ plan.to_index.each { |change| puts "create index #{change.name}" }
63
+ plan.warnings.each { |warning| puts "warning: #{warning}" }
64
+ end
65
+ end
66
+ end
67
+
68
+ desc "Check Better Auth configuration and schema health"
69
+ task :doctor do
70
+ BetterAuth::Grape.load_app_config!
71
+ exit_code = BetterAuth::Doctor.print(BetterAuth::Doctor.check(BetterAuth::Grape.migration_configuration), stdout: $stdout, stderr: $stderr)
72
+ abort if exit_code != 0
73
+ end
74
+
75
+ desc "Print Better Auth Grape mount information"
76
+ task :routes do
77
+ BetterAuth::Grape.load_app_config!
78
+ mount_path = BetterAuth::Grape.configuration.base_path
79
+ puts "#{mount_path}/* -> BetterAuth.auth"
80
+ puts "Core routes are handled by Better Auth; use the OpenAPI plugin or HTTP API docs for endpoint details."
81
+ end
82
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ module Grape
5
+ VERSION = "0.10.0"
6
+ end
7
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "better_auth"
4
+ require "grape"
5
+
6
+ require_relative "grape/version"
7
+ require_relative "grape/configuration"
8
+ require_relative "grape/mounted_app"
9
+ require_relative "grape/helpers"
10
+ require_relative "grape/extension"
11
+ require_relative "grape/migration"
12
+
13
+ module BetterAuth
14
+ module Grape
15
+ def self.included(base)
16
+ Extension.included(base)
17
+ end
18
+
19
+ class << self
20
+ def configuration
21
+ @configuration ||= Configuration.new
22
+ end
23
+
24
+ def configure
25
+ yield configuration
26
+ @auth = nil
27
+ self
28
+ end
29
+
30
+ def reset!
31
+ @configuration = nil
32
+ @auth = nil
33
+ end
34
+
35
+ def auth(overrides = nil)
36
+ options = configuration.to_auth_options
37
+ return @auth ||= BetterAuth.auth(options) if overrides.nil? || overrides.empty?
38
+
39
+ BetterAuth.auth(options.merge(overrides))
40
+ end
41
+
42
+ def migration_configuration
43
+ options = configuration.to_auth_options
44
+ options[:secret] ||= BetterAuth::Configuration::DEFAULT_SECRET
45
+ BetterAuth::Configuration.new(options)
46
+ end
47
+
48
+ def app_config_path(path = nil)
49
+ path || BetterAuth::Env.get("BETTER_AUTH_CONFIG") || "config/better_auth.rb"
50
+ end
51
+
52
+ def load_app_config(path = nil)
53
+ config_path = app_config_path(path)
54
+ return false unless File.exist?(config_path)
55
+
56
+ load config_path
57
+ true
58
+ end
59
+
60
+ def load_app_config!(path = nil)
61
+ config_path = app_config_path(path)
62
+ return true if load_app_config(config_path)
63
+
64
+ raise ArgumentError,
65
+ "Better Auth Grape config not found at #{config_path.inspect}. " \
66
+ "Run `rake better_auth:install` or set BETTER_AUTH_CONFIG to a shared config file."
67
+ end
68
+
69
+ def default_config_template
70
+ <<~RUBY
71
+ # frozen_string_literal: true
72
+
73
+ require "better_auth/grape"
74
+
75
+ BetterAuth::Grape.configure do |config|
76
+ config.secret = BetterAuth::Env.fetch("BETTER_AUTH_SECRET", "change-me-grape-secret-12345678901234567890")
77
+ config.base_url = BetterAuth::Env.get("BETTER_AUTH_URL")
78
+ config.base_path = "/api/auth"
79
+
80
+ config.database = ->(options) do
81
+ case BetterAuth::Env.fetch("BETTER_AUTH_DATABASE_DIALECT", "postgres")
82
+ when "postgres", "postgresql"
83
+ BetterAuth::Adapters::Postgres.new(options, url: ENV.fetch("DATABASE_URL"))
84
+ when "mysql"
85
+ BetterAuth::Adapters::MySQL.new(options, url: ENV.fetch("DATABASE_URL"))
86
+ when "sqlite", "sqlite3"
87
+ BetterAuth::Adapters::SQLite.new(options, path: ENV.fetch("DATABASE_URL", "db/better_auth.sqlite3"))
88
+ else
89
+ raise "Unsupported BETTER_AUTH_DATABASE_DIALECT for better_auth-grape"
90
+ end
91
+ end
92
+
93
+ config.email_and_password = {
94
+ enabled: true
95
+ }
96
+
97
+ config.plugins = []
98
+ end
99
+ RUBY
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "better_auth/grape"
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: better_auth-grape
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.10.0
5
+ platform: ruby
6
+ authors:
7
+ - Sebastian Sala
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: better_auth
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: grape
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ - - "<"
34
+ - !ruby/object:Gem::Version
35
+ version: '4'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '3.0'
43
+ - - "<"
44
+ - !ruby/object:Gem::Version
45
+ version: '4'
46
+ - !ruby/object:Gem::Dependency
47
+ name: bundler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '2.5'
53
+ type: :development
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '2.5'
60
+ - !ruby/object:Gem::Dependency
61
+ name: rack-test
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '2.2'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '2.2'
74
+ - !ruby/object:Gem::Dependency
75
+ name: rake
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '13.2'
81
+ type: :development
82
+ prerelease: false
83
+ version_requirements: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '13.2'
88
+ - !ruby/object:Gem::Dependency
89
+ name: rspec
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '3.13'
95
+ type: :development
96
+ prerelease: false
97
+ version_requirements: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '3.13'
102
+ - !ruby/object:Gem::Dependency
103
+ name: standardrb
104
+ requirement: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '1.0'
109
+ type: :development
110
+ prerelease: false
111
+ version_requirements: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '1.0'
116
+ description: Grape integration for Better Auth Ruby. Better Auth Ruby is an independent
117
+ modern authentication framework for Ruby inspired by Better Auth. Provides mounting
118
+ helpers, request helpers, and SQL migration tasks.
119
+ email:
120
+ - sebastian.sala.tech@gmail.com
121
+ executables: []
122
+ extensions: []
123
+ extra_rdoc_files: []
124
+ files:
125
+ - CHANGELOG.md
126
+ - README.md
127
+ - lib/better_auth/grape.rb
128
+ - lib/better_auth/grape/configuration.rb
129
+ - lib/better_auth/grape/extension.rb
130
+ - lib/better_auth/grape/helpers.rb
131
+ - lib/better_auth/grape/migration.rb
132
+ - lib/better_auth/grape/mounted_app.rb
133
+ - lib/better_auth/grape/tasks.rb
134
+ - lib/better_auth/grape/version.rb
135
+ - lib/better_auth_grape.rb
136
+ homepage: https://github.com/sebasxsala/better-auth-rb
137
+ licenses:
138
+ - MIT
139
+ metadata:
140
+ homepage_uri: https://github.com/sebasxsala/better-auth-rb
141
+ source_code_uri: https://github.com/sebasxsala/better-auth-rb
142
+ changelog_uri: https://github.com/sebasxsala/better-auth-rb/blob/main/packages/better_auth-grape/CHANGELOG.md
143
+ bug_tracker_uri: https://github.com/sebasxsala/better-auth-rb/issues
144
+ rdoc_options: []
145
+ require_paths:
146
+ - lib
147
+ required_ruby_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: 3.2.0
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ requirements: []
158
+ rubygems_version: 3.6.9
159
+ specification_version: 4
160
+ summary: Grape adapter for Better Auth
161
+ test_files: []