A self-hosted, open-source alternative to Auth0, built on Better Auth.
One sign-in for all your apps: OAuth 2.1 and OpenID Connect provider, passkeys, enterprise SSO,
organizations and an admin console. Your users, your database, no per-user pricing.
Website · Live demo · Deploy · Connect an app · Docs
In the Middle Ages the ostiarius was the doorkeeper: the one who held the keys and decided who came in.
Hosted identity platforms are great until the bill scales with your users or you need your user data in your own database. Ostiary gives you the same building blocks as a deployable Next.js app you own:
- Standard protocols. Any app that speaks OpenID Connect signs in with Ostiary: Next.js, Remix, mobile apps, CLIs, APIs.
- Your data. Everything lives in your Postgres database. No vendor lock-in.
- No per-user pricing. It costs what your hosting costs.
- Readable code. A small TypeScript monorepo on top of Better Auth, not a black box.
For your users
- Email and password with verification, username sign-in, password reset
- Passwordless sign-in with a 6-digit code sent by email (works across devices and inside OAuth sign-ins)
- Optional captcha (Cloudflare Turnstile, hCaptcha or reCAPTCHA v2) on sign-up, password sign-in, password reset and sign-in codes
- Passkeys (WebAuthn), plus a recent sign-in required to add one
- Two-factor authentication: authenticator app (TOTP) and backup codes, with "trust this device"
- Social sign-in with every Better Auth provider (Google, Apple, Microsoft, GitHub and 32 more), set up from the admin console, with brand buttons and connected accounts, plus Google One Tap
- Enterprise SSO with OIDC or SAML 2.0 (Okta, Entra ID, Google Workspace, JumpCloud), with DNS domain verification
- Account dashboard: profile, email change (approved from the current inbox), sessions, passkeys, two-factor authentication, connected accounts, connected apps (with their icons, permissions in plain words, when they were connected and last used), and Your data: export everything as JSON or delete the account
- Several accounts in one browser (up to 5): switch from the account menu, or pick one when an app asks with
prompt=select_account - 20 locales, light and dark themes
For your apps
- OAuth 2.1 / OpenID Connect provider: discovery, PKCE, refresh tokens, consent, UserInfo, introspection, JWKS
- Machine-to-machine tokens (client credentials) with per-client scopes
- API keys for scripts: users (or organizations) create keys for one of your APIs and some of its scopes; your API verifies them with Ostiary
- Self-registration for MCP clients and AI agents: Dynamic Client Registration and Client ID Metadata Documents, off by default and controlled from the admin console. Anonymous registration is limited to public PKCE clients (no secret leaves without a sign-in) — see MCP and AI agents
- Device sign-in (RFC 8628) for CLIs, TVs and other apps without a browser
- Protected resources: JWT access tokens scoped to your APIs, with the user's role as a claim
- Organizations with members, roles and invitations
- SCIM 2.0 provisioning per organization: Okta, Entra ID and other identity providers create and deactivate accounts
For you (admin console)
- Users: search, roles, bans, sessions, impersonation, two-factor reset
- Sign-in providers: turn social sign-in providers on and off, order them and paste their credentials (stored encrypted) without a redeploy
- Sign-in branding per application: display name, logo, accent color (with WCAG contrast checks), tagline, side panel and which social buttons show, with a live preview
- OAuth clients with their icons, an App usage page (tokens, users and consents per app over 30 days), consents, organizations, SSO providers
- API keys: turn them on, set their maximum lifetime, see and revoke every key
- Audit log of every admin action, sign-in activity and failed sign-in monitoring
- Signing keys: automatic rotation on a schedule, or rotate now, with a grace period during which old tokens keep verifying
- Rate limits per client IP on sign-in, codes, password reset and token endpoints, counted in Postgres so they hold on serverless
| Ostiary | Auth0 | |
|---|---|---|
| Hosting | Your Vercel account and Postgres | Auth0 cloud |
| Pricing | Your infrastructure | Per monthly active user |
| OIDC / OAuth 2.1 provider | Yes | Yes |
| Passkeys, two-factor authentication, social sign-in, organizations | Yes | Yes |
| Enterprise SSO | OIDC and SAML 2.0 | OIDC and SAML |
| Admin console and audit log | Yes | Yes |
| SCIM provisioning, breached-password detection | Yes | Yes |
| Compliance certifications and SLA | No | Yes |
| Source code | Yours, MIT | Closed |
Ostiary is a good fit when you want to own your identity layer. If you need a managed service with compliance certifications and an SLA, a hosted platform is the better choice.
-
Click Deploy with Vercel above. Vercel creates a Neon Postgres database for you and asks for:
BETTER_AUTH_SECRET: 32+ random characters (openssl rand -base64 32)ADMIN_EMAILS: your email address, so your account becomes an admin when you sign up and verify itRESEND_API_KEYandRESEND_FROM: from resend.com, to send verification emails
-
The build applies the database migrations. When it finishes, open your deployment and sign up with the email you put in
ADMIN_EMAILS, then open the verification link sent to it: the account becomes an admin once the address is verified. (Signing up with a social provider works too when the provider reports the address as verified; SSO never grants the admin role.) The admin console asks you to turn on two-factor authentication before you use it. -
Optional: deploy the admin console as a second project with the same database and secret:
For a single sign-in across both apps, give them subdomains of one domain (for example
auth.example.comandadmin.example.com) and setCOOKIE_DOMAIN=.example.comon both. Read COOKIE_DOMAIN first: the session cookie is then sent to every subdomain of that domain.
Ostiary is a standard OpenID Connect provider. Register a client in the admin console (Applications), then point your app at the discovery document:
https://<your-ostiary-domain>/api/auth/.well-known/openid-configuration
For example, with Auth.js in a Next.js app:
import NextAuth from "next-auth";
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
{
id: "ostiary",
name: "Ostiary",
type: "oidc",
issuer: "https://auth.example.com/api/auth",
clientId: process.env.OSTIARY_CLIENT_ID,
clientSecret: process.env.OSTIARY_CLIENT_SECRET,
},
],
});Any OIDC library works the same way (Better Auth's generic OAuth, openid-client, AppAuth on mobile): give it the issuer, client ID and secret.
The full guide covers protecting your APIs, signing key rotation, device sign-in for CLIs and TVs, and branding the sign-in page per app.
| Guide | What it covers |
|---|---|
| Connect an app | OIDC setup, APIs and scopes, signing keys, device flow, per-app branding, Google One Tap |
| Social sign-in | Turning on Google, Apple, GitHub and 33 more providers from the admin console |
| Enterprise SSO | OIDC and SAML 2.0 identity providers (Okta, Entra ID, Google Workspace), domain verification |
| SCIM provisioning | Okta and Entra ID creating and deactivating accounts per organization |
| Webhooks | Events, Standard Webhooks signatures, retries and the delivery log |
| MCP and AI agents | Dynamic Client Registration, Client ID Metadata Documents, protecting an MCP server |
| API keys | Personal and organization keys for your APIs, and how your API verifies them |
| Your data | Data export and account deletion (GDPR), what is kept and why |
| Configuration | Every environment variable |
| Security | Security model, rate limiting, client IP, COOKIE_DOMAIN |
| Testing | Unit tests and the Playwright end-to-end suite |
Requirements: Node.js 20+, pnpm 11, a Postgres database.
pnpm install
cp apps/auth/.env.example apps/auth/.env.local # fill in DATABASE_URL and BETTER_AUTH_SECRET
cp apps/admin/.env.example apps/admin/.env.local # same DATABASE_URL and BETTER_AUTH_SECRET
pnpm db:migrate
pnpm dev:auth # http://localhost:3000
pnpm dev:admin # http://localhost:3001Without Resend configured, development prints verification and reset links, and sign-in codes, to the console.
apps/auth Sign-in, sign-up, consent, OIDC endpoints, account dashboard
apps/admin Admin console (optional): users, clients, organizations, SSO, audit
packages/core Better Auth configuration, database schema and migrations, emails, UI, translations
Both apps run the same Better Auth configuration against one database. The admin app only exposes an allowlist of auth endpoints; sign-in and the OIDC protocol stay on the auth app.
Cyber Auth, the single sign-on for the Cyber ecosystem (CyberCTF, Cyber Courses, Cyber Bench), runs on Ostiary.
Built on Better Auth (MIT). Ostiary is a community project and is not affiliated with Better Auth. Type: Instrument Sans and Instrument Serif and Geist Mono (SIL Open Font License).
MIT © Florian Amette
