Better Auth Ruby
The Ruby/Rack port of Better Auth
Learn more about upstream Better Auth
Website
·
Issues
About
Better Auth Ruby is a Ruby port of Better Auth, the framework-agnostic authentication and authorization library from the TypeScript ecosystem.
The goal is upstream-compatible HTTP behavior with idiomatic Ruby internals: Rack first, Rails friendly, and tested against the upstream Better Auth source and test suite as the source of truth. This repository keeps the upstream project as a submodule under upstream/ and tracks port status in .docs/features/ and .docs/plans/.
This port is active work. Many server-side flows are implemented, but not every upstream edge case, adapter dialect, or TypeScript-only API has full parity yet.
Packages
| Gem | Description | Install |
|---|---|---|
better_auth |
Framework-agnostic Rack core. Auth routes, sessions, cookies, adapters, and core plugin shims live here. | gem "better_auth" |
better_auth-redis-storage |
External Redis secondary storage package for session payloads, active-session indexes, and secondary-storage-backed rate limiting. | gem "better_auth-redis-storage" |
better_auth-api-key |
External API key plugin package with creation, verification, quotas, metadata, permissions, storage modes, and API-key sessions. | gem "better_auth-api-key" |
better_auth-rails |
Rails adapter with mounting helpers, ActiveRecord adapter, controller helpers, and generators. | gem "better_auth-rails" |
better_auth-sinatra |
Sinatra adapter with Rack mounting, request helpers, and SQL migration Rake tasks. | gem "better_auth-sinatra" |
better_auth-sso |
External SSO plugin package with OIDC and SAML SSO route support. | gem "better_auth-sso" |
better_auth-scim |
External SCIM v2 provisioning plugin package. | gem "better_auth-scim" |
better_auth-stripe |
External Stripe billing plugin package with subscription, checkout, portal, and webhook support. | gem "better_auth-stripe" |
better_auth-oauth-provider |
External OAuth 2.0 provider plugin package. | gem "better_auth-oauth-provider" |
Quick Start
Rails
# Gemfile
gem "better_auth-rails"bundle install
bin/rails generate better_auth:install# config/routes.rb
Rails.application.routes.draw do
better_auth
endRack
# Gemfile
gem "better_auth"require "better_auth"
auth = BetterAuth.auth(
secret: ENV.fetch("BETTER_AUTH_SECRET"),
base_url: "http://localhost:3000",
database: BetterAuth::Adapters::Memory.new
)
run authSinatra
# Gemfile
gem "better_auth-sinatra"require "sinatra/base"
require "better_auth/sinatra"
class App < Sinatra::Base
register BetterAuth::Sinatra
better_auth at: "/api/auth" do |config|
config.secret = ENV.fetch("BETTER_AUTH_SECRET")
config.base_url = ENV.fetch("BETTER_AUTH_URL")
config.database = ->(options) {
BetterAuth::Adapters::Postgres.new(options, url: ENV.fetch("DATABASE_URL"))
}
end
endCompatibility Status
Legend:
- Supported: implemented in Ruby with local tests.
- Not supported: not implemented or intentionally outside the Ruby server scope.
- Partial: implemented for the main Ruby server path, with documented upstream parity gaps.
Plugin support is tracked against Ruby server parity: TypeScript inference, browser helper packages, and native mobile client behavior are treated as out of scope only when the row says so explicitly.
Core
| Area | Status | Notes |
|---|---|---|
| Auth factory and Rack handler | [x] Supported |
BetterAuth.auth(...) returns a Rack-callable auth object with direct server API access. |
| Endpoint/router/API pipeline | Partial | Rack routing, direct API calls, hooks, redirects, cookies, origin checks, and rate limiting exist; some upstream edge-case matrices remain future work. |
| Email/password auth | Partial | Sign-up, sign-in, password reset, password verify, set/change password, and email verification exist; some upstream edge cases are still being hardened. |
| Social OAuth flow | Partial |
/sign-in/social, /callback/:providerId, linking, unlinking, token refresh, and account info exist; upstream account-cookie and some linking-rule details are still future polish. |
| Sessions | Partial | Signed session cookies, session routes, revocation, cookie cache, secondary storage, Redis secondary-storage package, and sensitive-route lookup exist; full upstream session/cache matrix is not complete. |
| Cookies | Partial | Prefixing, signing, chunking, deletion, cache cookies, and advanced attributes exist; filtering and some account-cookie parity gaps are documented. |
| CSRF/trusted origins | Partial | Origin checks and trusted origins exist; callback-bearing GET route parity is still being tightened. |
| Rate limiting | Partial | Memory/custom/secondary-storage style rate limiting exists; full database-backed upstream matrix remains future work. |
| Hooks and database hooks | [x] Supported | Before/after endpoint hooks, plugin hooks, and adapter database hooks are implemented. |
| Plugin system | [x] Supported | Ruby plugins can add endpoints, schema, hooks, middleware, rate limits, error codes, and option defaults. |
| Database schema | [x] Supported | Core schema plus plugin schema merge, logical Better Auth field names, and SQL/Rails migration generation exist. |
| Memory adapter | [x] Supported | Default development/test adapter. |
| PostgreSQL adapter | Partial | Direct SQL adapter and DDL generation exist; full upstream adapter contract coverage is still expanding. |
| MySQL adapter | Partial | Direct SQL adapter and DDL generation exist; full upstream adapter contract coverage is still expanding. |
| Rails ActiveRecord adapter | Partial | ActiveRecord persistence, migrations, mounting, helpers, and generators exist; full adapter contract parity is still expanding. |
| Sinatra adapter | Partial | Rack mounting, helpers, SQL migration Rake tasks, and docs exist. ActiveRecord-backed Sinatra migrations are not supported yet. |
| Secondary storage | Partial | Session and verification-style storage behavior exists; full edge-case parity remains future work. |
| Experimental joins | Partial |
experimental: { joins: true } is accepted with adapter fallback behavior; the exhaustive join matrix is not complete. |
| OpenAPI generation | Partial | OpenAPI 3.1.1 metadata, route/model inventory, security schemes, servers, default responses, selected upstream request bodies, path params, disabled paths, reference HTML, theme, nonce, and disabled reference handling exist. Exact upstream snapshot parity remains open: upstream has 30 base paths and roughly 25 still need rich request/response schemas. |
Social Providers
Upstream Better Auth exposes many provider factories. The Ruby port currently ships the most common factories and the generic OAuth plugin can cover custom providers.
| Provider | Status | Notes |
|---|---|---|
| Apple | Partial | Factory and OAuth profile mapping exist. |
| Discord | Partial | Factory and OAuth profile mapping exist. |
| GitHub | Partial | Factory, token exchange, and email/profile lookup exist. |
| GitLab | Partial | Factory and OAuth profile mapping exist. |
| Partial | Factory and OpenID profile mapping exist. | |
| Microsoft Entra ID | Partial | Factory and OpenID profile mapping exist. |
| Atlassian, Cognito, Dropbox, Facebook, Figma, Hugging Face, Kakao, Kick, Line, Linear, LinkedIn, Naver, Notion, Paybin, PayPal, Polar, Reddit, Roblox, Salesforce, Slack, Spotify, TikTok, Twitch, Twitter/X, Vercel, VK, Zoom | [ ] Not supported as built-in factories | Use BetterAuth::Plugins.generic_oauth or add a Ruby provider factory. |
Plugins
| Plugin | Status | Notes |
|---|---|---|
| Access control | [x] Supported | Runtime roles, statements, permissions, resource/action connectors, and upstream access checks are implemented; TypeScript inference is outside Ruby scope. |
| Additional fields | [x] Supported | Schema extension and route integration exist. |
| Admin | [x] Supported | Ruby server parity covers user management, list/search/filter/sort/count, role validation, bans including social callback rejection and expiry cleanup, impersonation/admin-session restoration, session administration, password setting, destructive endpoints, and permission checks. |
| Anonymous | [x] Supported | Anonymous sign-in/delete, generator fallbacks, repeat-session rejection, and email/social link cleanup are implemented. |
| API key | [x] Supported | External package: install better_auth-api-key. Creation, verification, hashing, expiration bounds, usage/refill/rate limits, metadata migration, permissions, storage modes, deferred updates, and API-key sessions are implemented. |
| Bearer | [x] Supported | Bearer session resolution, signed-token exposure, unsigned-token fallback, signature requirement, list-session auth, and valid-cookie fallback are implemented. |
| Captcha | [x] Supported | reCAPTCHA, hCaptcha, Turnstile, CaptchaFox, protected endpoint checks, provider payloads, score checks, and failure responses are implemented. |
| Custom session | [x] Supported | Custom /get-session shaping with parsed/filtered payloads, unauthenticated nil responses, Set-Cookie preservation, OpenAPI metadata, and optional multi-session list mutation are implemented. |
| Device authorization | [x] Supported | Device/user code issuance, option validation, client validation, OAuth error responses, polling/slow-down, approval/denial, token exchange hooks, expiry, schema overrides, and verification URI behavior are implemented. |
| Email OTP | [x] Supported | Send/check/verify/sign-in/password-reset/change-email flows, attempts, latest/reused OTP, no-enumeration sends, override hooks, token storage modes, additional sign-up fields, and plugin rate limits are implemented. Client aliases are outside Ruby server scope. |
| Generic OAuth | [x] Supported | Custom OAuth sign-in/callback/link flows, DB and cookie state strategies with mismatch cleanup, dynamic authorization params, response mode, issuer checks, sign-up controls, custom token/user-info callables, standard HTTP token/userinfo exchange, provider helper factories, account-info/refresh integration, encrypted OAuth tokens, account cookies, and account linking are implemented. |
| Have I Been Pwned | [x] Supported | SHA-1 k-anonymity range lookup, default password-route protection, custom paths/messages, and injectable lookup tests are implemented. |
| JWT/JWKS | [x] Supported | EdDSA default signing, RS256/PS256/ES256/ES512 key generation, JWKS publication/custom path, key rotation/grace periods, kid selection, token expiry, remote JWKS verification, API-only sign/verify helpers, and set-auth-jwt are implemented. Symmetric client-secret algorithms such as HS256 are intentionally outside the JWKS server surface. |
| Last login method | [x] Supported | Successful email, SIWE, social OAuth, and generic OAuth logins update the readable cookie and optional lastLoginMethod user field; failed auth is suppressed and custom cookie names/prefixes/cross-origin attributes are covered. |
| Magic link | [x] Supported | Send/verify, redirects/errors, new-user signup, existing-user verification, latest-token verification, callback origin validation, and token storage modes are implemented. |
| MCP | [x] Supported | OAuth/protected-resource metadata, registration, authorization-code PKCE, token refresh, userinfo, JWKS publication, login-prompt cookie restoration, and helper challenge headers are implemented. |
| Multi-session | [x] Supported | Device sessions, active switching, same-user replacement, active-session authorization, revocation, sign-out cleanup, and invalid-token errors are implemented. |
| OAuth proxy | [x] Supported | Callback rewriting, same-origin unwrap, encrypted cross-origin cookie forwarding, timestamp/trusted-callback validation, malformed payload handling, stateless state-cookie package restoration, and DB-less provider callback flow are implemented. |
| OAuth provider | Partial | External package: install better_auth-oauth-provider. OAuth/OIDC metadata, client registration, consent, authorization-code/client-credentials tokens, introspection, revocation, and userinfo exist; organization reference, logout, encrypted client-secret, and rate-limit matrices remain. |
| OIDC provider | [x] Supported | Metadata, prompt/max-age handling, dynamic registration, consent-page and HTML consent flows, auth-code/refresh/revoke/introspection token handling, userinfo including custom claims, RP logout, client-secret storage modes, and JWT plugin ID-token signing are implemented. |
| One tap | [x] Supported | Google ID-token callback, account reuse/linking, trusted/verified account linking, disabled signup, client ID handling, invalid-token handling, and session cookies are implemented. Browser/FedCM helpers are outside Ruby server scope. |
| One-time token | [x] Supported | Generate/verify, single-use, expiration, expired-session rejection, cookie behavior, storage modes, server-only generation, and set-ott session headers are implemented. |
| OpenAPI | Partial | OpenAPI 3.1.1 metadata, route/model inventory, security schemes, servers, default responses, selected upstream request bodies, path params, disabled paths, reference HTML, theme, nonce, and disabled reference handling exist. Exact upstream snapshot parity remains open: upstream has 30 base paths and roughly 25 still need rich request/response schemas. |
| Organization | [x] Supported | Organization/member CRUD, invitations including multi-team acceptance, team flows, active org/team session fields, dynamic role CRUD safeguards, hooks, additional fields, permissions, and SQL/Rails plugin schema migrations are implemented. Browser client hooks and TypeScript inference are outside Ruby server scope. |
| Passkey | [x] Supported | External package: install better_auth-passkey. WebAuthn registration/authentication, passkey-first registration, WebAuthn extensions, verification callbacks, upstream option shapes, challenge expiration, allow/exclude credential transports, array origins, not-found delete behavior, management routes, session creation, and SQL/Rails schema output are implemented through the package-owned webauthn dependency. Browser client package aliases are outside Ruby server scope. |
| Phone number | [x] Supported | OTP send/verify, sign-in/sign-up, phone updates, password reset safety, attempt limits, uniqueness, additional fields, custom validation, and custom OTP verification are implemented. |
| SIWE | [x] Supported | Nonce, wallet sign-in, callback verification, ENS hook, account/session creation, EIP-55 checksum casing, duplicate wallet reuse, multi-chain wallets, and custom schema merging are implemented. |
| SSO | [x] Supported | External package: install better_auth-sso. Supports provider CRUD/access, OIDC discovery/callback, SAML ACS/callback/metadata, RelayState safety, replay protection, domain verification, organization assignment, and real SAML XML validation through the package-owned ruby-saml dependency. |
| SCIM | [x] Supported | External package: install better_auth-scim. Supports token envelopes, token storage modes, Bearer middleware, metadata, user CRUD, provider/org scoping, mappings, filters, PATCH operations, and organization enforcement. |
| Stripe | [x] Supported | External package: install better_auth-stripe. Fully supported for Ruby server parity after direct upstream Stripe source/test review: official stripe gem client support, injected-client checkout/portal flows, reference authorization, plan/seat/trial abuse protection, trial-start callbacks, billing event webhooks, subscription state transitions, organization subscriptions, metadata helpers, customer callbacks, checkout params/options, lookup keys, and webhook construction. |
| Two-factor | [x] Supported | TOTP, OTP, backup codes, trusted devices, cookie max-age options, disable/recovery flows, rememberMe: false preservation, and post-login verification are implemented. |
| Username | [x] Supported | Username sign-up/sign-in, availability, normalization, display username, validation order, duplicate/update behavior, and leak-prevention behavior are implemented. |
| Expo server integration | [x] Supported | Ruby server parity covers authorization proxy cookies, optional OAuth state cookie, expo-origin override/preservation, disabled override, development-only trusted exp://, wildcard trusted origins, and full trusted deep-link cookie transfer. Native Expo secure storage, cookie cache, focus/online managers, browser-opening flow, and React Native behavior tests are client-only. |
Development
Clone And Verify
git clone --recursive https://github.com/sebasxsala/better-auth.git
cd better-auth
make install
make ciOne Package
cd packages/better_auth
bundle install
bundle exec rake testExternal Plugin Package Tests
cd packages/better_auth-sso
rbenv exec bundle exec rake
cd ../better_auth-api-key
rbenv exec bundle exec rake
cd ../better_auth-scim
rbenv exec bundle exec rake
cd ../better_auth-stripe
rbenv exec bundle exec rake
cd ../better_auth-oauth-provider
rbenv exec bundle exec rakeDocumentation
The upstream docs app has been copied into docs/ and is being adapted for Ruby/Rack/Rails. Pages that still contain upstream TypeScript examples include a warning callout at the top.
Ruby-first starter pages are available under docs/content/docs/introduction.mdx, docs/content/docs/installation.mdx, docs/content/docs/basic-usage.mdx, docs/content/docs/concepts/database.mdx, docs/content/docs/integrations/rack.mdx, docs/content/docs/integrations/rails.mdx, and docs/content/docs/integrations/sinatra.mdx.
Monorepo Layout
better-auth/
├── upstream/ # Submodule: upstream TypeScript Better Auth
├── docs/ # Adapted upstream docs app
├── packages/
│ ├── better_auth/ # Core gem, Minitest
│ ├── better_auth-api-key/ # External API key plugin gem
│ ├── better_auth-rails/ # Rails adapter, RSpec
│ ├── better_auth-sinatra/ # Sinatra adapter, RSpec
│ ├── better_auth-sso/ # External SSO plugin gem
│ ├── better_auth-scim/ # External SCIM plugin gem
│ ├── better_auth-stripe/ # External Stripe plugin gem
│ └── better_auth-oauth-provider/ # External OAuth provider plugin gem
├── .docs/
│ ├── features/ # Feature parity notes
│ └── plans/ # Port implementation plans
├── Gemfile
├── Rakefile
└── MakefileGit Workflow
-
canary: day-to-day development; open PRs here. -
main: stable line; releases and CI publish run from here when versions bump. -
upstream/: git submodule and reference only.
git checkout canary
git pull origin canary
git checkout -b feat/my-change
# ... commit ...
git push -u origin feat/my-change
# Open PR to canaryRelease
Releases are automated with GitHub Actions on push to main when version.rb changes. The Rails adapter is published as both better_auth-rails and better_auth_rails as a compatibility alias; the Sinatra adapter publishes as better_auth-sinatra.
Details: RELEASING.md.
Dry-run locally:
make release-checkContributing
- Fork the repo.
- Branch from
canary. - Read AGENTS.md and the relevant package instructions.
- Run
make cibefore pushing. - Open a PR to
canary.
Security
Report vulnerabilities to security@openparcel.dev. See SECURITY.md.