Better Auth Hanami

Hanami adapter for Better Auth Ruby. It mounts the core Rack auth object inside Hanami, uses Hanami's ROM/Sequel database gateway for persistence, renders ROM::SQL migrations, generates Hanami relations/repos for app queries, and provides action helpers plus generator tasks.

Installation

gem "better_auth-hanami"
bundle install

Setup

Load the task file from your app Rakefile if your app does not already load lib/tasks:

# Rakefile
require "better_auth/hanami"
load Gem.loaded_specs.fetch("better_auth-hanami").full_gem_path + "/lib/tasks/better_auth.rake"

Generate the provider, route wiring, task wrapper, settings, relations/repos, and base migration:

bundle exec rake better_auth:init

Run Hanami migrations:

bin/hanami db migrate

When you add plugins that introduce schema tables or fields, regenerate both the migration and the app query objects before migrating a new app:

bundle exec rake better_auth:generate:migration
bundle exec rake better_auth:generate:relations

Configuration

The install generator creates config/providers/better_auth.rb:

Hanami.app.register_provider(:better_auth) do
  prepare do
    require "better_auth/hanami"
  end

  start do
    BetterAuth::Hanami.configure do |config|
      config.secret = target["settings"].better_auth_secret
      config.base_url = target["settings"].better_auth_url
      config.base_path = "/api/auth"
      config.database = ->(options) {
        BetterAuth::Hanami::SequelAdapter.from_container(target, options)
      }
      config.trusted_origins = [target["settings"].better_auth_url].compact
      config.email_and_password = {enabled: true}
      config.plugins = []
    end

    auth = BetterAuth::Hanami.auth
    register "better_auth.auth", auth
    register "better_auth.rack_app", BetterAuth::Hanami::MountedApp.new(auth, mount_path: BetterAuth::Hanami.configuration.base_path)
  end
end

trusted_origins controls Better Auth origin and redirect URL validation. If your browser client calls the auth endpoints from another origin, configure Rack CORS middleware in your Hanami app as well so preflight requests and Access-Control-* response headers match your frontend origin and credentials policy. For the shared Rack/CORS/CSRF boundary, see host-app-responsibilities.md.

Do not rely on a Hanami-only empty trusted_origins list as a strict deny-all-origin policy; set real deployment URLs in app settings. Keep BetterAuth::Hanami::MountedApp behavior aligned with Hanami's router instead of copying Rails mount internals without integration tests. Be cautious with relation or inflector overrides generated for an app, because overwriting application-specific Hanami relations can be destructive.

Regenerating Migrations

The migration generator skips an existing *_create_better_auth_tables.rb file by default so user-edited migrations are not overwritten. To intentionally regenerate the base migration for a new app or after changing plugin schemas, call the Ruby API with force: true:

BetterAuth::Hanami::Generators::MigrationGenerator.new.run(force: true)

The generated rake task keeps the non-overwriting behavior:

bundle exec rake better_auth:generate:migration

Routes

The generated config/routes.rb includes:

require "better_auth/hanami"

module Bookshelf
  class Routes < Hanami::Routes
    include BetterAuth::Hanami::Routing

    better_auth
  end
end

By default this mounts Better Auth at /api/auth. Customize the path:

better_auth at: "/auth"

BetterAuth::Hanami::MountedApp expects PATH_INFO in the shape produced by Hanami's router; see spec/better_auth/hanami/routing_spec.rb. Custom Rack stacks with different SCRIPT_NAME conventions may need application-level path rewriting.

Action Helpers

Include helpers in your base action:

class Action < Hanami::Action
  include BetterAuth::Hanami::ActionHelpers
end

Use them from an action:

def handle(request, response)
  return unless require_authentication(request, response)

  response.body = current_user(request).fetch("email")
end

Relations And Repos

Better Auth uses BetterAuth::Hanami::SequelAdapter for its own reads and writes. The generated Hanami relations/repos are for your application code when you want to inspect or query Better Auth tables directly:

users = Hanami.app["relations.users"].to_a
user = Hanami.app["repos.user_repo"].users.by_pk(user_id).one

If you prefer a custom persistence implementation, configure it directly:

BetterAuth::Hanami.configure do |config|
  config.database = ->(options) { MyBetterAuthAdapter.new(options) }
end

Limitations

  • Supports Hanami 2.3+ only. Better Auth core depends on Rack 3, and Hanami 2.3 is the first Hanami line that allows Rack 3.
  • Hanami 1.x and Hanami 2.2/Rack 2 apps are out of scope for this adapter.
  • The stable command surface is Rake/generator based. A hanami better_auth ... command is not exposed because the current public guides do not document a stable third-party Hanami CLI extension API.
  • Apps created with --skip-db can use memory storage for development or tests, but production apps should configure Hanami DB or pass an explicit Better Auth adapter.

Development

cd packages/better_auth-hanami
rbenv exec bundle exec rspec
rbenv exec bundle exec standardrb