auth
Embeddable authentication with plugins for email, password, phone, OAuth, passkeys, 2FA, API keys, JWT and organizations, stored in your own Postgres, MySQL or SQLite.
go get github.com/ubgo/auth
Each plugin and adapter is its own module, tagged v0.1.0 with the core. Add the ones you use, for example: go get github.com/ubgo/auth/plugin/password
- Language
- Go
- Go version
- 1.24.0
- Core deps
- 1 direct
- Latest tag
- v0.1.0
- License
- Apache-2.0
- Go modules
- 29
How it fits together
Highlights from the README
- Only what you use. An app imports only the sign-in methods it offers, and a plugin of your own gets the same powers as the built-in ones: operations, hooks, columns on the user table, its own tables and migrations.
- Your database, your joins. The auth tables sit beside yours. Read and join them with typed sqlb columns, so a renamed column is a compile error rather than a production bug. Only writes are closed, because the invariants live in Go.
- One pipeline for every entry point. Each feature is one typed operation. A Go call, an HTTP route and a GraphQL resolver all run the same code, so none can skip the rate limit, the CSRF origin check, the session requirement or a hook.
- Security pinned by tests. Constant-time comparison of every secret is a source guard that fails the build; one password hash on every sign-in path, known account or not, is a timing test; only verified identities ever match a person; tokens are single-use under concurrency.
- Three engines, one behaviour. Values are normalized in Go and compared byte for byte, and one integration suite runs on Postgres, MySQL and SQLite.
- A dependency-free core. The core module depends on the standard library and sqlb only, enforced by a test; argon2, WebAuthn and each framework adapter are separate modules you download only if you import them.
Code from the README
a, err := auth.New(
auth.WithDB(db, sqlb.PostgresDialect{}),
auth.WithBaseURL("https://app.example.com"),
auth.WithPlugins(
email.New(email.Login(), email.Recover(), email.WithMailer(mailer)),
password.New(),
),
)
if err != nil {
log.Fatal(err) // a broken plugin combination fails here, not in production
}
mux.Handle("/auth/", authhttp.Handler(a, authhttp.Prefix("/auth")))
mux.Handle("/me", authhttp.Middleware(a)(authhttp.RequireUser(meHandler)))type User struct {
auth.User // ID, Name, Image, CreatedAt
email.EmailColumns // PrimaryEmail, EmailVerifiedAt
Phone phone.Data // joined from auth_identity
}
func (u *User) Bindings() []sqlb.Binding {
return sqlb.Bindings(auth.UserBindings(&u.User), email.Bindings(&u.EmailColumns), phone.UserBindings(&u.Phone))
}From the auth README.
At a glance (from the README)
| Package | What it adds |
|---|---|
plugin/email | Email identities, verification links, change with undo, many addresses per user, sign-in codes |
plugin/password | argon2id passwords, sign-up, sign-in, reset, change; password/hibp checks breaches; a separate module |
plugin/phone | Phone identities, SMS codes, sign-in and sign-up with a code alone |
plugin/username | Handles, reserved names, availability |
plugin/ldap | Sign in with the username and password of an LDAP directory (OpenLDAP, Active Directory, FreeIPA, lldap), checked by the directory and never stored; TLS required, a replaceable Directory; a separate module |
plugin/siwe | Sign-In with Ethereum (ERC-4361): a single-use nonce, the message checked against your site, chain and time window, several wallets per user, a replaceable Verifier for smart-contract wallets; a separate module |
plugin/oauth | Google, GitHub, any OpenID Connect issuer; optional sealed provider tokens, refreshed before expiry |
plugin/twofactor | TOTP, mailed or texted codes and security keys, several per user, with recovery codes; asked after password sign-ins by default (better-auth’s rule), any other rule through WithPolicy; RequireEnrolment makes a factor mandatory through a session task |
plugin/passkey | Passkeys (WebAuthn); a separate module |
plugin/emailchallenge | Passwordless sign-in by one challenge a link and a code both complete, always signing in the browser that asked |
plugin/magiclink | Sign-in by a single-use link to a verified address or number |
plugin/apikey | API keys with scopes for scripts and integrations |
plugin/jwt | Short-lived JWTs (EdDSA, RS256 or ES256) from a session, a JWKS, a Go verifier, and named templates shaped for another service (presets for Supabase, Hasura, Convex and Firebase in jwtpreset) |
plugin/admin | Find and search users, ban and unban, revoke sessions, roles and permissions decided by a pluggable Authorizer (built-in admin and user roles; plugin/access roles, Casbin, OpenFGA or your own), impersonate (visible to the user, one hour at most) |
plugin/invite | Invitation codes with usage tracking, optionally invite-only sign-up |
plugin/referral | User-to-user referral codes and the steps that complete a referral; a separate module |
plugin/terms | Record which terms version each user accepted; block until the current one is |
plugin/anonymous | Guest sessions before sign-up, linked to the account by an app hook on every sign-in path |
plugin/lastlogin | Remember how and when each user last signed in |
plugin/metadata | Public, private and user-editable JSON metadata per user in a table of its own; admin routes decided by your authorizer |
plugin/sessiondata | Per-session app data in a table of its own, removed with the session; the core carries the same data inline on Session.Data |
plugin/handoff | Hand a session to a native app or another client with a three-minute single-use token |
plugin/captcha | Turnstile, reCAPTCHA or hCaptcha before sign-up, sign-in and reset, or only when a rate rule asks for a challenge; fails closed |
plugin/pow | Proof of work instead of a captcha: a rate or geo rule with the challenge action asks the client for a hashcash proof (X-Pow-Proof), with no third party |
plugin/stale | Mail the owners of accounts nobody has signed in to for a year (configurable) and, if you ask for it, delete the ones that never answer; run from your scheduler with auth maintain |
plugin/organization | Organizations, teams, members, invitations by email or any identifier kind matched only to verified addresses, roles decided by a pluggable Authorizer, an active organization per session, built-in, static and per-organization roles checked by permission, lifecycle hooks, verified email domains that invite, suggest or auto-join; orgjwt puts the active organization in a JWT; a separate module |
plugin/access | Permission statements and the roles built from them (better-auth’s access), used by the organization plugin, by the admin plugin through accessadmin, by API-key permissions through accessapikey, and by any app; a separate module |
plugin/audit | An audit log of security events, filterable by type and organization, with each user’s own history; any sink (a SIEM, a log pipeline) through Sink and Querier, and any operation mapped to an event type |
plugin/sso | Enterprise single sign-on: OpenID Connect and SAML 2.0 identity providers registered at run time per organization or email domain; a separate module |
plugin/samlidp | Your app as a SAML 2.0 identity provider for wikis, ticketing and vendor tools: metadata, SP-initiated and opt-in IdP-initiated sign-on, signed assertions; a separate module |
plugin/scim | A SCIM 2.0 server, so Okta, Entra ID and other IdPs provision and deprovision users and groups; a separate module |
plugin/deviceauth | The device authorization grant (RFC 8628) for CLIs and TVs; oauthdevice issues OAuth tokens through oauthprovider instead; separate modules |
plugin/oauthproxy | Provider sign-in on preview deployments through production’s callback; oauthpopup signs in from a popup for apps embedded on other sites; a separate module |
plugin/oauthprovider | Your app as an OAuth 2.1 authorization server and OpenID Connect provider, for other apps and MCP servers; a separate module |
plugin/webhook | Signed outgoing webhooks (Standard Webhooks) for user, session, identity and security events, with retries, dead letters, replay and an SSRF guard; webhook/signature verifies them; a separate module |
plugin/agentauth | AI agents sign in with their own keys and act for a user under the capabilities the user granted (Agent Auth Protocol); a separate module |