This is page 1 of 67. Use http://codebase.md/better-auth/better-auth?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .gitattributes ├── .github │ ├── CODEOWNERS │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.yml │ │ └── feature_request.yml │ ├── renovate.json5 │ └── workflows │ ├── ci.yml │ ├── e2e.yml │ ├── preview.yml │ └── release.yml ├── .gitignore ├── .npmrc ├── .nvmrc ├── .vscode │ └── settings.json ├── banner-dark.png ├── banner.png ├── biome.json ├── bump.config.ts ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── demo │ ├── expo-example │ │ ├── .env.example │ │ ├── .gitignore │ │ ├── app.config.ts │ │ ├── assets │ │ │ ├── bg-image.jpeg │ │ │ ├── fonts │ │ │ │ └── SpaceMono-Regular.ttf │ │ │ ├── icon.png │ │ │ └── images │ │ │ ├── adaptive-icon.png │ │ │ ├── favicon.png │ │ │ ├── logo.png │ │ │ ├── partial-react-logo.png │ │ │ ├── react-logo.png │ │ │ ├── [email protected] │ │ │ ├── [email protected] │ │ │ └── splash.png │ │ ├── babel.config.js │ │ ├── components.json │ │ ├── expo-env.d.ts │ │ ├── index.ts │ │ ├── metro.config.js │ │ ├── nativewind-env.d.ts │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── app │ │ │ │ ├── _layout.tsx │ │ │ │ ├── api │ │ │ │ │ └── auth │ │ │ │ │ └── [...route]+api.ts │ │ │ │ ├── dashboard.tsx │ │ │ │ ├── forget-password.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── sign-up.tsx │ │ │ ├── components │ │ │ │ ├── icons │ │ │ │ │ └── google.tsx │ │ │ │ └── ui │ │ │ │ ├── avatar.tsx │ │ │ │ ├── button.tsx │ │ │ │ ├── card.tsx │ │ │ │ ├── dialog.tsx │ │ │ │ ├── input.tsx │ │ │ │ ├── separator.tsx │ │ │ │ └── text.tsx │ │ │ ├── global.css │ │ │ └── lib │ │ │ ├── auth-client.ts │ │ │ ├── auth.ts │ │ │ ├── icons │ │ │ │ ├── iconWithClassName.ts │ │ │ │ └── X.tsx │ │ │ └── utils.ts │ │ ├── tailwind.config.js │ │ └── tsconfig.json │ └── nextjs │ ├── .env.example │ ├── .gitignore │ ├── app │ │ ├── (auth) │ │ │ ├── forget-password │ │ │ │ └── page.tsx │ │ │ ├── reset-password │ │ │ │ └── page.tsx │ │ │ ├── sign-in │ │ │ │ ├── loading.tsx │ │ │ │ └── page.tsx │ │ │ └── two-factor │ │ │ ├── otp │ │ │ │ └── page.tsx │ │ │ └── page.tsx │ │ ├── accept-invitation │ │ │ └── [id] │ │ │ ├── invitation-error.tsx │ │ │ └── page.tsx │ │ ├── admin │ │ │ └── page.tsx │ │ ├── api │ │ │ └── auth │ │ │ └── [...all] │ │ │ └── route.ts │ │ ├── apps │ │ │ └── register │ │ │ └── page.tsx │ │ ├── client-test │ │ │ └── page.tsx │ │ ├── dashboard │ │ │ ├── change-plan.tsx │ │ │ ├── client.tsx │ │ │ ├── organization-card.tsx │ │ │ ├── page.tsx │ │ │ ├── upgrade-button.tsx │ │ │ └── user-card.tsx │ │ ├── device │ │ │ ├── approve │ │ │ │ └── page.tsx │ │ │ ├── denied │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── page.tsx │ │ │ └── success │ │ │ └── page.tsx │ │ ├── favicon.ico │ │ ├── features.tsx │ │ ├── fonts │ │ │ ├── GeistMonoVF.woff │ │ │ └── GeistVF.woff │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── oauth │ │ │ └── authorize │ │ │ ├── concet-buttons.tsx │ │ │ └── page.tsx │ │ ├── page.tsx │ │ └── pricing │ │ └── page.tsx │ ├── components │ │ ├── account-switch.tsx │ │ ├── blocks │ │ │ └── pricing.tsx │ │ ├── logo.tsx │ │ ├── one-tap.tsx │ │ ├── sign-in-btn.tsx │ │ ├── sign-in.tsx │ │ ├── sign-up.tsx │ │ ├── theme-provider.tsx │ │ ├── theme-toggle.tsx │ │ ├── tier-labels.tsx │ │ ├── ui │ │ │ ├── accordion.tsx │ │ │ ├── alert-dialog.tsx │ │ │ ├── alert.tsx │ │ │ ├── aspect-ratio.tsx │ │ │ ├── avatar.tsx │ │ │ ├── badge.tsx │ │ │ ├── breadcrumb.tsx │ │ │ ├── button.tsx │ │ │ ├── calendar.tsx │ │ │ ├── card.tsx │ │ │ ├── carousel.tsx │ │ │ ├── chart.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── command.tsx │ │ │ ├── context-menu.tsx │ │ │ ├── copy-button.tsx │ │ │ ├── dialog.tsx │ │ │ ├── drawer.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── form.tsx │ │ │ ├── hover-card.tsx │ │ │ ├── input-otp.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── menubar.tsx │ │ │ ├── navigation-menu.tsx │ │ │ ├── pagination.tsx │ │ │ ├── password-input.tsx │ │ │ ├── popover.tsx │ │ │ ├── progress.tsx │ │ │ ├── radio-group.tsx │ │ │ ├── resizable.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── select.tsx │ │ │ ├── separator.tsx │ │ │ ├── sheet.tsx │ │ │ ├── skeleton.tsx │ │ │ ├── slider.tsx │ │ │ ├── sonner.tsx │ │ │ ├── switch.tsx │ │ │ ├── table.tsx │ │ │ ├── tabs.tsx │ │ │ ├── tabs2.tsx │ │ │ ├── textarea.tsx │ │ │ ├── toast.tsx │ │ │ ├── toaster.tsx │ │ │ ├── toggle-group.tsx │ │ │ ├── toggle.tsx │ │ │ └── tooltip.tsx │ │ └── wrapper.tsx │ ├── components.json │ ├── hooks │ │ └── use-toast.ts │ ├── lib │ │ ├── auth-client.ts │ │ ├── auth-types.ts │ │ ├── auth.ts │ │ ├── email │ │ │ ├── invitation.tsx │ │ │ ├── resend.ts │ │ │ └── reset-password.tsx │ │ ├── metadata.ts │ │ ├── shared.ts │ │ └── utils.ts │ ├── middleware.ts │ ├── next.config.ts │ ├── package.json │ ├── postcss.config.mjs │ ├── public │ │ ├── __og.png │ │ ├── _og.png │ │ ├── favicon │ │ │ ├── android-chrome-192x192.png │ │ │ ├── android-chrome-512x512.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon.ico │ │ │ ├── light │ │ │ │ ├── android-chrome-192x192.png │ │ │ │ ├── android-chrome-512x512.png │ │ │ │ ├── apple-touch-icon.png │ │ │ │ ├── favicon-16x16.png │ │ │ │ ├── favicon-32x32.png │ │ │ │ ├── favicon.ico │ │ │ │ └── site.webmanifest │ │ │ └── site.webmanifest │ │ ├── logo.svg │ │ └── og.png │ ├── README.md │ ├── tailwind.config.ts │ ├── tsconfig.json │ └── turbo.json ├── docker-compose.yml ├── docs │ ├── .env.example │ ├── .gitignore │ ├── app │ │ ├── api │ │ │ ├── ai-chat │ │ │ │ └── route.ts │ │ │ ├── analytics │ │ │ │ ├── conversation │ │ │ │ │ └── route.ts │ │ │ │ ├── event │ │ │ │ │ └── route.ts │ │ │ │ └── feedback │ │ │ │ └── route.ts │ │ │ ├── chat │ │ │ │ └── route.ts │ │ │ ├── og │ │ │ │ └── route.tsx │ │ │ ├── og-release │ │ │ │ └── route.tsx │ │ │ ├── search │ │ │ │ └── route.ts │ │ │ └── support │ │ │ └── route.ts │ │ ├── blog │ │ │ ├── _components │ │ │ │ ├── _layout.tsx │ │ │ │ ├── blog-list.tsx │ │ │ │ ├── changelog-layout.tsx │ │ │ │ ├── default-changelog.tsx │ │ │ │ ├── fmt-dates.tsx │ │ │ │ ├── icons.tsx │ │ │ │ ├── stat-field.tsx │ │ │ │ └── support.tsx │ │ │ ├── [[...slug]] │ │ │ │ └── page.tsx │ │ │ └── layout.tsx │ │ ├── changelogs │ │ │ ├── _components │ │ │ │ ├── _layout.tsx │ │ │ │ ├── changelog-layout.tsx │ │ │ │ ├── default-changelog.tsx │ │ │ │ ├── fmt-dates.tsx │ │ │ │ ├── grid-pattern.tsx │ │ │ │ ├── icons.tsx │ │ │ │ └── stat-field.tsx │ │ │ ├── [[...slug]] │ │ │ │ └── page.tsx │ │ │ └── layout.tsx │ │ ├── community │ │ │ ├── _components │ │ │ │ ├── header.tsx │ │ │ │ └── stats.tsx │ │ │ └── page.tsx │ │ ├── docs │ │ │ ├── [[...slug]] │ │ │ │ ├── page.client.tsx │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ └── lib │ │ │ └── get-llm-text.ts │ │ ├── global.css │ │ ├── layout.config.tsx │ │ ├── layout.tsx │ │ ├── llms.txt │ │ │ ├── [...slug] │ │ │ │ └── route.ts │ │ │ └── route.ts │ │ ├── not-found.tsx │ │ ├── page.tsx │ │ ├── reference │ │ │ └── route.ts │ │ ├── sitemap.xml │ │ ├── static.json │ │ │ └── route.ts │ │ └── v1 │ │ ├── _components │ │ │ └── v1-text.tsx │ │ ├── bg-line.tsx │ │ └── page.tsx │ ├── assets │ │ ├── Geist.ttf │ │ └── GeistMono.ttf │ ├── components │ │ ├── ai-chat-modal.tsx │ │ ├── anchor-scroll-fix.tsx │ │ ├── api-method-tabs.tsx │ │ ├── api-method.tsx │ │ ├── banner.tsx │ │ ├── blocks │ │ │ └── features.tsx │ │ ├── builder │ │ │ ├── beam.tsx │ │ │ ├── code-tabs │ │ │ │ ├── code-editor.tsx │ │ │ │ ├── code-tabs.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── tab-bar.tsx │ │ │ │ └── theme.ts │ │ │ ├── index.tsx │ │ │ ├── sign-in.tsx │ │ │ ├── sign-up.tsx │ │ │ ├── social-provider.tsx │ │ │ ├── store.ts │ │ │ └── tabs.tsx │ │ ├── display-techstack.tsx │ │ ├── divider-text.tsx │ │ ├── docs │ │ │ ├── docs.client.tsx │ │ │ ├── docs.tsx │ │ │ ├── layout │ │ │ │ ├── nav.tsx │ │ │ │ ├── theme-toggle.tsx │ │ │ │ ├── toc-thumb.tsx │ │ │ │ └── toc.tsx │ │ │ ├── page.client.tsx │ │ │ ├── page.tsx │ │ │ ├── shared.tsx │ │ │ └── ui │ │ │ ├── button.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── popover.tsx │ │ │ └── scroll-area.tsx │ │ ├── endpoint.tsx │ │ ├── features.tsx │ │ ├── floating-ai-search.tsx │ │ ├── fork-button.tsx │ │ ├── generate-apple-jwt.tsx │ │ ├── generate-secret.tsx │ │ ├── github-stat.tsx │ │ ├── icons.tsx │ │ ├── landing │ │ │ ├── gradient-bg.tsx │ │ │ ├── grid-pattern.tsx │ │ │ ├── hero.tsx │ │ │ ├── section-svg.tsx │ │ │ ├── section.tsx │ │ │ ├── spotlight.tsx │ │ │ └── testimonials.tsx │ │ ├── logo-context-menu.tsx │ │ ├── logo.tsx │ │ ├── markdown-renderer.tsx │ │ ├── markdown.tsx │ │ ├── mdx │ │ │ ├── add-to-cursor.tsx │ │ │ └── database-tables.tsx │ │ ├── message-feedback.tsx │ │ ├── mobile-search-icon.tsx │ │ ├── nav-bar.tsx │ │ ├── nav-link.tsx │ │ ├── nav-mobile.tsx │ │ ├── promo-card.tsx │ │ ├── resource-card.tsx │ │ ├── resource-grid.tsx │ │ ├── resource-section.tsx │ │ ├── ripple.tsx │ │ ├── search-dialog.tsx │ │ ├── side-bar.tsx │ │ ├── sidebar-content.tsx │ │ ├── techstack-icons.tsx │ │ ├── theme-provider.tsx │ │ ├── theme-toggler.tsx │ │ └── ui │ │ ├── accordion.tsx │ │ ├── alert-dialog.tsx │ │ ├── alert.tsx │ │ ├── aside-link.tsx │ │ ├── aspect-ratio.tsx │ │ ├── avatar.tsx │ │ ├── background-beams.tsx │ │ ├── background-boxes.tsx │ │ ├── badge.tsx │ │ ├── breadcrumb.tsx │ │ ├── button.tsx │ │ ├── calendar.tsx │ │ ├── callout.tsx │ │ ├── card.tsx │ │ ├── carousel.tsx │ │ ├── chart.tsx │ │ ├── checkbox.tsx │ │ ├── code-block.tsx │ │ ├── collapsible.tsx │ │ ├── command.tsx │ │ ├── context-menu.tsx │ │ ├── dialog.tsx │ │ ├── drawer.tsx │ │ ├── dropdown-menu.tsx │ │ ├── dynamic-code-block.tsx │ │ ├── fade-in.tsx │ │ ├── form.tsx │ │ ├── hover-card.tsx │ │ ├── input-otp.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── menubar.tsx │ │ ├── navigation-menu.tsx │ │ ├── pagination.tsx │ │ ├── popover.tsx │ │ ├── progress.tsx │ │ ├── radio-group.tsx │ │ ├── resizable.tsx │ │ ├── scroll-area.tsx │ │ ├── select.tsx │ │ ├── separator.tsx │ │ ├── sheet.tsx │ │ ├── sidebar.tsx │ │ ├── skeleton.tsx │ │ ├── slider.tsx │ │ ├── sonner.tsx │ │ ├── sparkles.tsx │ │ ├── switch.tsx │ │ ├── table.tsx │ │ ├── tabs.tsx │ │ ├── textarea.tsx │ │ ├── toggle-group.tsx │ │ ├── toggle.tsx │ │ ├── tooltip-docs.tsx │ │ ├── tooltip.tsx │ │ └── use-copy-button.tsx │ ├── components.json │ ├── content │ │ ├── blogs │ │ │ ├── 0-supabase-auth-to-planetscale-migration.mdx │ │ │ ├── 1-3.mdx │ │ │ ├── authjs-joins-better-auth.mdx │ │ │ └── seed-round.mdx │ │ ├── changelogs │ │ │ ├── 1-2.mdx │ │ │ └── 1.0.mdx │ │ └── docs │ │ ├── adapters │ │ │ ├── community-adapters.mdx │ │ │ ├── drizzle.mdx │ │ │ ├── mongo.mdx │ │ │ ├── mssql.mdx │ │ │ ├── mysql.mdx │ │ │ ├── other-relational-databases.mdx │ │ │ ├── postgresql.mdx │ │ │ ├── prisma.mdx │ │ │ └── sqlite.mdx │ │ ├── authentication │ │ │ ├── apple.mdx │ │ │ ├── atlassian.mdx │ │ │ ├── cognito.mdx │ │ │ ├── discord.mdx │ │ │ ├── dropbox.mdx │ │ │ ├── email-password.mdx │ │ │ ├── facebook.mdx │ │ │ ├── figma.mdx │ │ │ ├── github.mdx │ │ │ ├── gitlab.mdx │ │ │ ├── google.mdx │ │ │ ├── huggingface.mdx │ │ │ ├── kakao.mdx │ │ │ ├── kick.mdx │ │ │ ├── line.mdx │ │ │ ├── linear.mdx │ │ │ ├── linkedin.mdx │ │ │ ├── microsoft.mdx │ │ │ ├── naver.mdx │ │ │ ├── notion.mdx │ │ │ ├── other-social-providers.mdx │ │ │ ├── paypal.mdx │ │ │ ├── reddit.mdx │ │ │ ├── roblox.mdx │ │ │ ├── salesforce.mdx │ │ │ ├── slack.mdx │ │ │ ├── spotify.mdx │ │ │ ├── tiktok.mdx │ │ │ ├── twitch.mdx │ │ │ ├── twitter.mdx │ │ │ ├── vk.mdx │ │ │ └── zoom.mdx │ │ ├── basic-usage.mdx │ │ ├── comparison.mdx │ │ ├── concepts │ │ │ ├── api.mdx │ │ │ ├── cli.mdx │ │ │ ├── client.mdx │ │ │ ├── cookies.mdx │ │ │ ├── database.mdx │ │ │ ├── email.mdx │ │ │ ├── hooks.mdx │ │ │ ├── oauth.mdx │ │ │ ├── plugins.mdx │ │ │ ├── rate-limit.mdx │ │ │ ├── session-management.mdx │ │ │ ├── typescript.mdx │ │ │ └── users-accounts.mdx │ │ ├── examples │ │ │ ├── astro.mdx │ │ │ ├── next-js.mdx │ │ │ ├── nuxt.mdx │ │ │ ├── remix.mdx │ │ │ └── svelte-kit.mdx │ │ ├── guides │ │ │ ├── auth0-migration-guide.mdx │ │ │ ├── browser-extension-guide.mdx │ │ │ ├── clerk-migration-guide.mdx │ │ │ ├── create-a-db-adapter.mdx │ │ │ ├── next-auth-migration-guide.mdx │ │ │ ├── optimizing-for-performance.mdx │ │ │ ├── saml-sso-with-okta.mdx │ │ │ ├── supabase-migration-guide.mdx │ │ │ └── your-first-plugin.mdx │ │ ├── installation.mdx │ │ ├── integrations │ │ │ ├── astro.mdx │ │ │ ├── convex.mdx │ │ │ ├── elysia.mdx │ │ │ ├── expo.mdx │ │ │ ├── express.mdx │ │ │ ├── fastify.mdx │ │ │ ├── hono.mdx │ │ │ ├── lynx.mdx │ │ │ ├── nestjs.mdx │ │ │ ├── next.mdx │ │ │ ├── nitro.mdx │ │ │ ├── nuxt.mdx │ │ │ ├── remix.mdx │ │ │ ├── solid-start.mdx │ │ │ ├── svelte-kit.mdx │ │ │ ├── tanstack.mdx │ │ │ └── waku.mdx │ │ ├── introduction.mdx │ │ ├── meta.json │ │ ├── plugins │ │ │ ├── 2fa.mdx │ │ │ ├── admin.mdx │ │ │ ├── anonymous.mdx │ │ │ ├── api-key.mdx │ │ │ ├── autumn.mdx │ │ │ ├── bearer.mdx │ │ │ ├── captcha.mdx │ │ │ ├── community-plugins.mdx │ │ │ ├── device-authorization.mdx │ │ │ ├── dodopayments.mdx │ │ │ ├── dub.mdx │ │ │ ├── email-otp.mdx │ │ │ ├── generic-oauth.mdx │ │ │ ├── have-i-been-pwned.mdx │ │ │ ├── jwt.mdx │ │ │ ├── last-login-method.mdx │ │ │ ├── magic-link.mdx │ │ │ ├── mcp.mdx │ │ │ ├── multi-session.mdx │ │ │ ├── oauth-proxy.mdx │ │ │ ├── oidc-provider.mdx │ │ │ ├── one-tap.mdx │ │ │ ├── one-time-token.mdx │ │ │ ├── open-api.mdx │ │ │ ├── organization.mdx │ │ │ ├── passkey.mdx │ │ │ ├── phone-number.mdx │ │ │ ├── polar.mdx │ │ │ ├── siwe.mdx │ │ │ ├── sso.mdx │ │ │ ├── stripe.mdx │ │ │ └── username.mdx │ │ └── reference │ │ ├── contributing.mdx │ │ ├── faq.mdx │ │ ├── options.mdx │ │ ├── resources.mdx │ │ ├── security.mdx │ │ └── telemetry.mdx │ ├── hooks │ │ └── use-mobile.ts │ ├── ignore-build.sh │ ├── lib │ │ ├── blog.ts │ │ ├── chat │ │ │ └── inkeep-qa-schema.ts │ │ ├── constants.ts │ │ ├── export-search-indexes.ts │ │ ├── inkeep-analytics.ts │ │ ├── is-active.ts │ │ ├── metadata.ts │ │ ├── source.ts │ │ └── utils.ts │ ├── next.config.js │ ├── package.json │ ├── postcss.config.js │ ├── proxy.ts │ ├── public │ │ ├── avatars │ │ │ └── beka.jpg │ │ ├── blogs │ │ │ ├── authjs-joins.png │ │ │ ├── seed-round.png │ │ │ └── supabase-ps.png │ │ ├── branding │ │ │ ├── better-auth-brand-assets.zip │ │ │ ├── better-auth-logo-dark.png │ │ │ ├── better-auth-logo-dark.svg │ │ │ ├── better-auth-logo-light.png │ │ │ ├── better-auth-logo-light.svg │ │ │ ├── better-auth-logo-wordmark-dark.png │ │ │ ├── better-auth-logo-wordmark-dark.svg │ │ │ ├── better-auth-logo-wordmark-light.png │ │ │ └── better-auth-logo-wordmark-light.svg │ │ ├── extension-id.png │ │ ├── favicon │ │ │ ├── android-chrome-192x192.png │ │ │ ├── android-chrome-512x512.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon.ico │ │ │ ├── light │ │ │ │ ├── android-chrome-192x192.png │ │ │ │ ├── android-chrome-512x512.png │ │ │ │ ├── apple-touch-icon.png │ │ │ │ ├── favicon-16x16.png │ │ │ │ ├── favicon-32x32.png │ │ │ │ ├── favicon.ico │ │ │ │ └── site.webmanifest │ │ │ └── site.webmanifest │ │ ├── images │ │ │ └── blogs │ │ │ └── better auth (1).png │ │ ├── logo.png │ │ ├── logo.svg │ │ ├── LogoDark.webp │ │ ├── LogoLight.webp │ │ ├── og.png │ │ ├── open-api-reference.png │ │ ├── people-say │ │ │ ├── code-with-antonio.jpg │ │ │ ├── dagmawi-babi.png │ │ │ ├── dax.png │ │ │ ├── dev-ed.png │ │ │ ├── egoist.png │ │ │ ├── guillermo-rauch.png │ │ │ ├── jonathan-wilke.png │ │ │ ├── josh-tried-coding.jpg │ │ │ ├── kitze.jpg │ │ │ ├── lazar-nikolov.png │ │ │ ├── nizzy.png │ │ │ ├── omar-mcadam.png │ │ │ ├── ryan-vogel.jpg │ │ │ ├── saltyatom.jpg │ │ │ ├── sebastien-chopin.png │ │ │ ├── shreyas-mididoddi.png │ │ │ ├── tech-nerd.png │ │ │ ├── theo.png │ │ │ ├── vybhav-bhargav.png │ │ │ └── xavier-pladevall.jpg │ │ ├── plus.svg │ │ ├── release-og │ │ │ ├── 1-2.png │ │ │ ├── 1-3.png │ │ │ └── changelog-og.png │ │ └── v1-og.png │ ├── README.md │ ├── scripts │ │ ├── endpoint-to-doc │ │ │ ├── index.ts │ │ │ ├── input.ts │ │ │ ├── output.mdx │ │ │ └── readme.md │ │ └── sync-orama.ts │ ├── source.config.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ └── turbo.json ├── e2e │ ├── integration │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── solid-vinxi │ │ │ ├── .gitignore │ │ │ ├── app.config.ts │ │ │ ├── e2e │ │ │ │ ├── test.spec.ts │ │ │ │ └── utils.ts │ │ │ ├── package.json │ │ │ ├── public │ │ │ │ └── favicon.ico │ │ │ ├── src │ │ │ │ ├── app.tsx │ │ │ │ ├── entry-client.tsx │ │ │ │ ├── entry-server.tsx │ │ │ │ ├── global.d.ts │ │ │ │ ├── lib │ │ │ │ │ ├── auth-client.ts │ │ │ │ │ └── auth.ts │ │ │ │ └── routes │ │ │ │ ├── [...404].tsx │ │ │ │ ├── api │ │ │ │ │ └── auth │ │ │ │ │ └── [...all].ts │ │ │ │ └── index.tsx │ │ │ └── tsconfig.json │ │ ├── test-utils │ │ │ ├── package.json │ │ │ └── src │ │ │ └── playwright.ts │ │ └── vanilla-node │ │ ├── e2e │ │ │ ├── app.ts │ │ │ ├── domain.spec.ts │ │ │ ├── postgres-js.spec.ts │ │ │ ├── test.spec.ts │ │ │ └── utils.ts │ │ ├── index.html │ │ ├── package.json │ │ ├── src │ │ │ ├── main.ts │ │ │ └── vite-env.d.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ └── smoke │ ├── package.json │ ├── test │ │ ├── bun.spec.ts │ │ ├── cloudflare.spec.ts │ │ ├── deno.spec.ts │ │ ├── fixtures │ │ │ ├── bun-simple.ts │ │ │ ├── cloudflare │ │ │ │ ├── .gitignore │ │ │ │ ├── drizzle │ │ │ │ │ ├── 0000_clean_vector.sql │ │ │ │ │ └── meta │ │ │ │ │ ├── _journal.json │ │ │ │ │ └── 0000_snapshot.json │ │ │ │ ├── drizzle.config.ts │ │ │ │ ├── package.json │ │ │ │ ├── src │ │ │ │ │ ├── auth-schema.ts │ │ │ │ │ ├── db.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── test │ │ │ │ │ ├── apply-migrations.ts │ │ │ │ │ ├── env.d.ts │ │ │ │ │ └── index.test.ts │ │ │ │ ├── tsconfig.json │ │ │ │ ├── vitest.config.ts │ │ │ │ ├── worker-configuration.d.ts │ │ │ │ └── wrangler.json │ │ │ ├── deno-simple.ts │ │ │ ├── tsconfig-exact-optional-property-types │ │ │ │ ├── package.json │ │ │ │ ├── src │ │ │ │ │ ├── index.ts │ │ │ │ │ └── user-additional-fields.ts │ │ │ │ └── tsconfig.json │ │ │ ├── tsconfig-verbatim-module-syntax-node10 │ │ │ │ ├── package.json │ │ │ │ ├── src │ │ │ │ │ └── index.ts │ │ │ │ └── tsconfig.json │ │ │ └── vite │ │ │ ├── package.json │ │ │ ├── src │ │ │ │ ├── client.ts │ │ │ │ └── server.ts │ │ │ ├── tsconfig.json │ │ │ └── vite.config.ts │ │ ├── ssr.ts │ │ ├── typecheck.spec.ts │ │ └── vite.spec.ts │ └── tsconfig.json ├── LICENSE.md ├── package.json ├── packages │ ├── better-auth │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── __snapshots__ │ │ │ │ └── init.test.ts.snap │ │ │ ├── adapters │ │ │ │ ├── adapter-factory │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── test │ │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ │ └── adapter-factory.test.ts.snap │ │ │ │ │ │ └── adapter-factory.test.ts │ │ │ │ │ └── types.ts │ │ │ │ ├── create-test-suite.ts │ │ │ │ ├── drizzle-adapter │ │ │ │ │ ├── drizzle-adapter.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── test │ │ │ │ │ ├── .gitignore │ │ │ │ │ ├── adapter.drizzle.mysql.test.ts │ │ │ │ │ ├── adapter.drizzle.pg.test.ts │ │ │ │ │ ├── adapter.drizzle.sqlite.test.ts │ │ │ │ │ └── generate-schema.ts │ │ │ │ ├── index.ts │ │ │ │ ├── kysely-adapter │ │ │ │ │ ├── bun-sqlite-dialect.ts │ │ │ │ │ ├── dialect.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── kysely-adapter.ts │ │ │ │ │ ├── node-sqlite-dialect.ts │ │ │ │ │ ├── test │ │ │ │ │ │ ├── adapter.kysely.mssql.test.ts │ │ │ │ │ │ ├── adapter.kysely.mysql.test.ts │ │ │ │ │ │ ├── adapter.kysely.pg.test.ts │ │ │ │ │ │ ├── adapter.kysely.sqlite.test.ts │ │ │ │ │ │ └── node-sqlite-dialect.test.ts │ │ │ │ │ └── types.ts │ │ │ │ ├── memory-adapter │ │ │ │ │ ├── adapter.memory.test.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── memory-adapter.ts │ │ │ │ ├── mongodb-adapter │ │ │ │ │ ├── adapter.mongo-db.test.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── mongodb-adapter.ts │ │ │ │ ├── prisma-adapter │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── prisma-adapter.ts │ │ │ │ │ └── test │ │ │ │ │ ├── .gitignore │ │ │ │ │ ├── base.prisma │ │ │ │ │ ├── generate-auth-config.ts │ │ │ │ │ ├── generate-prisma-schema.ts │ │ │ │ │ ├── get-prisma-client.ts │ │ │ │ │ ├── prisma.mysql.test.ts │ │ │ │ │ ├── prisma.pg.test.ts │ │ │ │ │ ├── prisma.sqlite.test.ts │ │ │ │ │ └── push-prisma-schema.ts │ │ │ │ ├── test-adapter.ts │ │ │ │ ├── test.ts │ │ │ │ ├── tests │ │ │ │ │ ├── auth-flow.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── normal.ts │ │ │ │ │ ├── number-id.ts │ │ │ │ │ ├── performance.ts │ │ │ │ │ └── transactions.ts │ │ │ │ └── utils.ts │ │ │ ├── api │ │ │ │ ├── check-endpoint-conflicts.test.ts │ │ │ │ ├── index.test.ts │ │ │ │ ├── index.ts │ │ │ │ ├── middlewares │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── origin-check.test.ts │ │ │ │ │ └── origin-check.ts │ │ │ │ ├── rate-limiter │ │ │ │ │ ├── index.ts │ │ │ │ │ └── rate-limiter.test.ts │ │ │ │ ├── routes │ │ │ │ │ ├── account.test.ts │ │ │ │ │ ├── account.ts │ │ │ │ │ ├── callback.ts │ │ │ │ │ ├── email-verification.test.ts │ │ │ │ │ ├── email-verification.ts │ │ │ │ │ ├── error.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── ok.ts │ │ │ │ │ ├── reset-password.test.ts │ │ │ │ │ ├── reset-password.ts │ │ │ │ │ ├── session-api.test.ts │ │ │ │ │ ├── session.ts │ │ │ │ │ ├── sign-in.test.ts │ │ │ │ │ ├── sign-in.ts │ │ │ │ │ ├── sign-out.test.ts │ │ │ │ │ ├── sign-out.ts │ │ │ │ │ ├── sign-up.test.ts │ │ │ │ │ ├── sign-up.ts │ │ │ │ │ ├── update-user.test.ts │ │ │ │ │ └── update-user.ts │ │ │ │ ├── to-auth-endpoints.test.ts │ │ │ │ └── to-auth-endpoints.ts │ │ │ ├── auth.test.ts │ │ │ ├── auth.ts │ │ │ ├── call.test.ts │ │ │ ├── client │ │ │ │ ├── client-ssr.test.ts │ │ │ │ ├── client.test.ts │ │ │ │ ├── config.ts │ │ │ │ ├── fetch-plugins.ts │ │ │ │ ├── index.ts │ │ │ │ ├── lynx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── lynx-store.ts │ │ │ │ ├── parser.ts │ │ │ │ ├── path-to-object.ts │ │ │ │ ├── plugins │ │ │ │ │ ├── index.ts │ │ │ │ │ └── infer-plugin.ts │ │ │ │ ├── proxy.ts │ │ │ │ ├── query.ts │ │ │ │ ├── react │ │ │ │ │ ├── index.ts │ │ │ │ │ └── react-store.ts │ │ │ │ ├── session-atom.ts │ │ │ │ ├── solid │ │ │ │ │ ├── index.ts │ │ │ │ │ └── solid-store.ts │ │ │ │ ├── svelte │ │ │ │ │ └── index.ts │ │ │ │ ├── test-plugin.ts │ │ │ │ ├── types.ts │ │ │ │ ├── url.test.ts │ │ │ │ ├── vanilla.ts │ │ │ │ └── vue │ │ │ │ ├── index.ts │ │ │ │ └── vue-store.ts │ │ │ ├── cookies │ │ │ │ ├── check-cookies.ts │ │ │ │ ├── cookie-utils.ts │ │ │ │ ├── cookies.test.ts │ │ │ │ └── index.ts │ │ │ ├── crypto │ │ │ │ ├── buffer.ts │ │ │ │ ├── hash.ts │ │ │ │ ├── index.ts │ │ │ │ ├── jwt.ts │ │ │ │ ├── password.test.ts │ │ │ │ ├── password.ts │ │ │ │ └── random.ts │ │ │ ├── db │ │ │ │ ├── db.test.ts │ │ │ │ ├── field.ts │ │ │ │ ├── get-migration.ts │ │ │ │ ├── get-schema.ts │ │ │ │ ├── get-tables.test.ts │ │ │ │ ├── get-tables.ts │ │ │ │ ├── index.ts │ │ │ │ ├── internal-adapter.test.ts │ │ │ │ ├── internal-adapter.ts │ │ │ │ ├── schema.ts │ │ │ │ ├── secondary-storage.test.ts │ │ │ │ ├── to-zod.ts │ │ │ │ ├── utils.ts │ │ │ │ └── with-hooks.ts │ │ │ ├── index.ts │ │ │ ├── init.test.ts │ │ │ ├── init.ts │ │ │ ├── integrations │ │ │ │ ├── next-js.ts │ │ │ │ ├── node.ts │ │ │ │ ├── react-start.ts │ │ │ │ ├── solid-start.ts │ │ │ │ └── svelte-kit.ts │ │ │ ├── oauth2 │ │ │ │ ├── index.ts │ │ │ │ ├── link-account.test.ts │ │ │ │ ├── link-account.ts │ │ │ │ ├── state.ts │ │ │ │ └── utils.ts │ │ │ ├── plugins │ │ │ │ ├── access │ │ │ │ │ ├── access.test.ts │ │ │ │ │ ├── access.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── types.ts │ │ │ │ ├── additional-fields │ │ │ │ │ ├── additional-fields.test.ts │ │ │ │ │ └── client.ts │ │ │ │ ├── admin │ │ │ │ │ ├── access │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── statement.ts │ │ │ │ │ ├── admin.test.ts │ │ │ │ │ ├── admin.ts │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── error-codes.ts │ │ │ │ │ ├── has-permission.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── schema.ts │ │ │ │ │ └── types.ts │ │ │ │ ├── anonymous │ │ │ │ │ ├── anon.test.ts │ │ │ │ │ ├── client.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── api-key │ │ │ │ │ ├── api-key.test.ts │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── rate-limit.ts │ │ │ │ │ ├── routes │ │ │ │ │ │ ├── create-api-key.ts │ │ │ │ │ │ ├── delete-all-expired-api-keys.ts │ │ │ │ │ │ ├── delete-api-key.ts │ │ │ │ │ │ ├── get-api-key.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── list-api-keys.ts │ │ │ │ │ │ ├── update-api-key.ts │ │ │ │ │ │ └── verify-api-key.ts │ │ │ │ │ ├── schema.ts │ │ │ │ │ └── types.ts │ │ │ │ ├── bearer │ │ │ │ │ ├── bearer.test.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── captcha │ │ │ │ │ ├── captcha.test.ts │ │ │ │ │ ├── constants.ts │ │ │ │ │ ├── error-codes.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ ├── utils.ts │ │ │ │ │ └── verify-handlers │ │ │ │ │ ├── captchafox.ts │ │ │ │ │ ├── cloudflare-turnstile.ts │ │ │ │ │ ├── google-recaptcha.ts │ │ │ │ │ ├── h-captcha.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── custom-session │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── custom-session.test.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── device-authorization │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── device-authorization.test.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── schema.ts │ │ │ │ ├── email-otp │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── email-otp.test.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── generic-oauth │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── generic-oauth.test.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── haveibeenpwned │ │ │ │ │ ├── haveibeenpwned.test.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── jwt │ │ │ │ │ ├── adapter.ts │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── jwt.test.ts │ │ │ │ │ ├── schema.ts │ │ │ │ │ ├── sign.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── last-login-method │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── custom-prefix.test.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── last-login-method.test.ts │ │ │ │ ├── magic-link │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── magic-link.test.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── mcp │ │ │ │ │ ├── authorize.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── mcp.test.ts │ │ │ │ ├── multi-session │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── multi-session.test.ts │ │ │ │ ├── oauth-proxy │ │ │ │ │ ├── index.ts │ │ │ │ │ └── oauth-proxy.test.ts │ │ │ │ ├── oidc-provider │ │ │ │ │ ├── authorize.ts │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── oidc.test.ts │ │ │ │ │ ├── schema.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ ├── ui.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── one-tap │ │ │ │ │ ├── client.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── one-time-token │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── one-time-token.test.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── open-api │ │ │ │ │ ├── generator.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── logo.ts │ │ │ │ │ └── open-api.test.ts │ │ │ │ ├── organization │ │ │ │ │ ├── access │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── statement.ts │ │ │ │ │ ├── adapter.ts │ │ │ │ │ ├── call.ts │ │ │ │ │ ├── client.test.ts │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── error-codes.ts │ │ │ │ │ ├── has-permission.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── organization-hook.test.ts │ │ │ │ │ ├── organization.test.ts │ │ │ │ │ ├── organization.ts │ │ │ │ │ ├── permission.ts │ │ │ │ │ ├── routes │ │ │ │ │ │ ├── crud-access-control.test.ts │ │ │ │ │ │ ├── crud-access-control.ts │ │ │ │ │ │ ├── crud-invites.ts │ │ │ │ │ │ ├── crud-members.test.ts │ │ │ │ │ │ ├── crud-members.ts │ │ │ │ │ │ ├── crud-org.test.ts │ │ │ │ │ │ ├── crud-org.ts │ │ │ │ │ │ └── crud-team.ts │ │ │ │ │ ├── schema.ts │ │ │ │ │ ├── team.test.ts │ │ │ │ │ └── types.ts │ │ │ │ ├── passkey │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── passkey.test.ts │ │ │ │ ├── phone-number │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── phone-number-error.ts │ │ │ │ │ └── phone-number.test.ts │ │ │ │ ├── siwe │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── schema.ts │ │ │ │ │ ├── siwe.test.ts │ │ │ │ │ └── types.ts │ │ │ │ ├── sso │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── sso.test.ts │ │ │ │ ├── two-factor │ │ │ │ │ ├── backup-codes │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── constant.ts │ │ │ │ │ ├── error-code.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── otp │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── schema.ts │ │ │ │ │ ├── totp │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── two-factor.test.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ ├── utils.ts │ │ │ │ │ └── verify-two-factor.ts │ │ │ │ └── username │ │ │ │ ├── client.ts │ │ │ │ ├── error-codes.ts │ │ │ │ ├── index.ts │ │ │ │ ├── schema.ts │ │ │ │ └── username.test.ts │ │ │ ├── social-providers │ │ │ │ └── index.ts │ │ │ ├── social.test.ts │ │ │ ├── test-utils │ │ │ │ ├── headers.ts │ │ │ │ ├── index.ts │ │ │ │ ├── state.ts │ │ │ │ └── test-instance.ts │ │ │ ├── types │ │ │ │ ├── adapter.ts │ │ │ │ ├── api.ts │ │ │ │ ├── helper.ts │ │ │ │ ├── index.ts │ │ │ │ ├── models.ts │ │ │ │ ├── plugins.ts │ │ │ │ └── types.test.ts │ │ │ └── utils │ │ │ ├── await-object.ts │ │ │ ├── boolean.ts │ │ │ ├── clone.ts │ │ │ ├── constants.ts │ │ │ ├── date.ts │ │ │ ├── ensure-utc.ts │ │ │ ├── get-request-ip.ts │ │ │ ├── hashing.ts │ │ │ ├── hide-metadata.ts │ │ │ ├── id.ts │ │ │ ├── import-util.ts │ │ │ ├── index.ts │ │ │ ├── is-atom.ts │ │ │ ├── is-promise.ts │ │ │ ├── json.ts │ │ │ ├── merger.ts │ │ │ ├── middleware-response.ts │ │ │ ├── misc.ts │ │ │ ├── password.ts │ │ │ ├── plugin-helper.ts │ │ │ ├── shim.ts │ │ │ ├── time.ts │ │ │ ├── url.ts │ │ │ └── wildcard.ts │ │ ├── tsconfig.json │ │ ├── tsdown.config.ts │ │ └── vitest.config.ts │ ├── cli │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── commands │ │ │ │ ├── generate.ts │ │ │ │ ├── info.ts │ │ │ │ ├── init.ts │ │ │ │ ├── login.ts │ │ │ │ ├── mcp.ts │ │ │ │ ├── migrate.ts │ │ │ │ └── secret.ts │ │ │ ├── generators │ │ │ │ ├── auth-config.ts │ │ │ │ ├── drizzle.ts │ │ │ │ ├── index.ts │ │ │ │ ├── kysely.ts │ │ │ │ ├── prisma.ts │ │ │ │ └── types.ts │ │ │ ├── index.ts │ │ │ └── utils │ │ │ ├── add-svelte-kit-env-modules.ts │ │ │ ├── check-package-managers.ts │ │ │ ├── format-ms.ts │ │ │ ├── get-config.ts │ │ │ ├── get-package-info.ts │ │ │ ├── get-tsconfig-info.ts │ │ │ └── install-dependencies.ts │ │ ├── test │ │ │ ├── __snapshots__ │ │ │ │ ├── auth-schema-mysql-enum.txt │ │ │ │ ├── auth-schema-mysql-number-id.txt │ │ │ │ ├── auth-schema-mysql-passkey-number-id.txt │ │ │ │ ├── auth-schema-mysql-passkey.txt │ │ │ │ ├── auth-schema-mysql.txt │ │ │ │ ├── auth-schema-number-id.txt │ │ │ │ ├── auth-schema-pg-enum.txt │ │ │ │ ├── auth-schema-pg-passkey.txt │ │ │ │ ├── auth-schema-sqlite-enum.txt │ │ │ │ ├── auth-schema-sqlite-number-id.txt │ │ │ │ ├── auth-schema-sqlite-passkey-number-id.txt │ │ │ │ ├── auth-schema-sqlite-passkey.txt │ │ │ │ ├── auth-schema-sqlite.txt │ │ │ │ ├── auth-schema.txt │ │ │ │ ├── migrations.sql │ │ │ │ ├── schema-mongodb.prisma │ │ │ │ ├── schema-mysql-custom.prisma │ │ │ │ ├── schema-mysql.prisma │ │ │ │ ├── schema-numberid.prisma │ │ │ │ └── schema.prisma │ │ │ ├── generate-all-db.test.ts │ │ │ ├── generate.test.ts │ │ │ ├── get-config.test.ts │ │ │ ├── info.test.ts │ │ │ └── migrate.test.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.test.json │ │ └── tsdown.config.ts │ ├── core │ │ ├── package.json │ │ ├── src │ │ │ ├── async_hooks │ │ │ │ └── index.ts │ │ │ ├── context │ │ │ │ ├── index.ts │ │ │ │ └── transaction.ts │ │ │ ├── db │ │ │ │ ├── adapter │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── plugin.ts │ │ │ │ ├── schema │ │ │ │ │ ├── account.ts │ │ │ │ │ ├── rate-limit.ts │ │ │ │ │ ├── session.ts │ │ │ │ │ ├── shared.ts │ │ │ │ │ ├── user.ts │ │ │ │ │ └── verification.ts │ │ │ │ └── type.ts │ │ │ ├── env │ │ │ │ ├── color-depth.ts │ │ │ │ ├── env-impl.ts │ │ │ │ ├── index.ts │ │ │ │ ├── logger.test.ts │ │ │ │ └── logger.ts │ │ │ ├── error │ │ │ │ ├── codes.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── middleware │ │ │ │ └── index.ts │ │ │ ├── oauth2 │ │ │ │ ├── client-credentials-token.ts │ │ │ │ ├── create-authorization-url.ts │ │ │ │ ├── index.ts │ │ │ │ ├── oauth-provider.ts │ │ │ │ ├── refresh-access-token.ts │ │ │ │ ├── utils.ts │ │ │ │ └── validate-authorization-code.ts │ │ │ ├── social-providers │ │ │ │ ├── apple.ts │ │ │ │ ├── atlassian.ts │ │ │ │ ├── cognito.ts │ │ │ │ ├── discord.ts │ │ │ │ ├── dropbox.ts │ │ │ │ ├── facebook.ts │ │ │ │ ├── figma.ts │ │ │ │ ├── github.ts │ │ │ │ ├── gitlab.ts │ │ │ │ ├── google.ts │ │ │ │ ├── huggingface.ts │ │ │ │ ├── index.ts │ │ │ │ ├── kakao.ts │ │ │ │ ├── kick.ts │ │ │ │ ├── line.ts │ │ │ │ ├── linear.ts │ │ │ │ ├── linkedin.ts │ │ │ │ ├── microsoft-entra-id.ts │ │ │ │ ├── naver.ts │ │ │ │ ├── notion.ts │ │ │ │ ├── paypal.ts │ │ │ │ ├── reddit.ts │ │ │ │ ├── roblox.ts │ │ │ │ ├── salesforce.ts │ │ │ │ ├── slack.ts │ │ │ │ ├── spotify.ts │ │ │ │ ├── tiktok.ts │ │ │ │ ├── twitch.ts │ │ │ │ ├── twitter.ts │ │ │ │ ├── vk.ts │ │ │ │ └── zoom.ts │ │ │ ├── types │ │ │ │ ├── context.ts │ │ │ │ ├── cookie.ts │ │ │ │ ├── helper.ts │ │ │ │ ├── index.ts │ │ │ │ ├── init-options.ts │ │ │ │ ├── plugin-client.ts │ │ │ │ └── plugin.ts │ │ │ └── utils │ │ │ ├── error-codes.ts │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── tsdown.config.ts │ ├── expo │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── client.ts │ │ │ ├── expo.test.ts │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── tsdown.config.ts │ ├── sso │ │ ├── package.json │ │ ├── src │ │ │ ├── client.ts │ │ │ ├── index.ts │ │ │ ├── oidc.test.ts │ │ │ └── saml.test.ts │ │ ├── tsconfig.json │ │ └── tsdown.config.ts │ ├── stripe │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── src │ │ │ ├── client.ts │ │ │ ├── hooks.ts │ │ │ ├── index.ts │ │ │ ├── schema.ts │ │ │ ├── stripe.test.ts │ │ │ ├── types.ts │ │ │ └── utils.ts │ │ ├── tsconfig.json │ │ ├── tsdown.config.ts │ │ └── vitest.config.ts │ └── telemetry │ ├── package.json │ ├── src │ │ ├── detectors │ │ │ ├── detect-auth-config.ts │ │ │ ├── detect-database.ts │ │ │ ├── detect-framework.ts │ │ │ ├── detect-project-info.ts │ │ │ ├── detect-runtime.ts │ │ │ └── detect-system-info.ts │ │ ├── index.ts │ │ ├── project-id.ts │ │ ├── telemetry.test.ts │ │ ├── types.ts │ │ └── utils │ │ ├── hash.ts │ │ ├── id.ts │ │ ├── import-util.ts │ │ └── package-json.ts │ ├── tsconfig.json │ └── tsdown.config.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── README.md ├── SECURITY.md ├── tsconfig.json └── turbo.json ``` # Files -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- ``` 1 | 22.18.0 ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/drizzle-adapter/test/.gitignore: -------------------------------------------------------------------------------- ``` 1 | .tmp 2 | drizzle ``` -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- ``` 1 | * text=auto eol=lf ``` -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- ``` 1 | link-workspace-packages=true 2 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/prisma-adapter/test/.gitignore: -------------------------------------------------------------------------------- ``` 1 | auth.ts 2 | schema-mysql.prisma 3 | schema-sqlite.prisma 4 | schema-postgresql.prisma 5 | .tmp ``` -------------------------------------------------------------------------------- /demo/expo-example/.env.example: -------------------------------------------------------------------------------- ``` 1 | GITHUB_CLIENT_ID= 2 | GITHUB_CLIENT_SECRET= 3 | GOOGLE_CLIENT_ID= 4 | GOOGLE_CLIENT_SECRET= 5 | BETTER_AUTH_SECRET= 6 | DATABASE_URL= 7 | BETTER_AUTH_URL=http://localohst:8081 ``` -------------------------------------------------------------------------------- /docs/.env.example: -------------------------------------------------------------------------------- ``` 1 | NEXT_PUBLIC_URL=http://localhost:3000 2 | 3 | # Orama Search Configuration 4 | ORAMA_PRIVATE_API_KEY= 5 | NEXT_PUBLIC_ORAMA_PUBLIC_API_KEY= 6 | NEXT_PUBLIC_ORAMA_ENDPOINT= 7 | ORAMA_INDEX_ID= 8 | ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/.gitignore: -------------------------------------------------------------------------------- ``` 1 | dist 2 | .wrangler 3 | .output 4 | .vercel 5 | .netlify 6 | .vinxi 7 | app.config.timestamp_*.js 8 | 9 | # Environment 10 | .env 11 | .env*.local 12 | 13 | # dependencies 14 | /node_modules 15 | 16 | # IDEs and editors 17 | /.idea 18 | .project 19 | .classpath 20 | *.launch 21 | .settings/ 22 | 23 | # Temp 24 | gitignore 25 | 26 | # System Files 27 | .DS_Store 28 | Thumbs.db 29 | ``` -------------------------------------------------------------------------------- /demo/expo-example/.gitignore: -------------------------------------------------------------------------------- ``` 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | 16 | # @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb 17 | # The following patterns were generated by expo-cli 18 | 19 | expo-env.d.ts 20 | # @end expo-cli ``` -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- ``` 1 | # deps 2 | /node_modules 3 | 4 | # generated content 5 | .contentlayer 6 | .content-collections 7 | .source 8 | 9 | # test & build 10 | /coverage 11 | /.next/ 12 | /out/ 13 | /build 14 | *.tsbuildinfo 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | /.pnp 20 | .pnp.js 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # others 26 | .env*.local 27 | .vercel 28 | next-env.d.ts ``` -------------------------------------------------------------------------------- /demo/nextjs/.gitignore: -------------------------------------------------------------------------------- ``` 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | 32 | # env files (can opt-in for commiting if needed) 33 | .env 34 | 35 | # vercel 36 | .vercel 37 | 38 | # typescript 39 | *.tsbuildinfo 40 | next-env.d.ts 41 | 42 | certificates ``` -------------------------------------------------------------------------------- /demo/nextjs/.env.example: -------------------------------------------------------------------------------- ``` 1 | GOOGLE_CLIENT_SECRET= 2 | NEXT_PUBLIC_GOOGLE_CLIENT_ID= 3 | BETTER_AUTH_URL= 4 | BETTER_AUTH_SECRET= 5 | TURSO_DATABASE_URL= 6 | TURSO_AUTH_TOKEN= 7 | GITHUB_CLIENT_ID= 8 | GITHUB_CLIENT_SECRET= 9 | RESEND_API_KEY= 10 | TEST_EMAIL= 11 | DISCORD_CLIENT_ID= 12 | DISCORD_CLIENT_SECRET= 13 | TWITTER_CLIENT_ID= 14 | TWITTER_CLIENT_SECRET= 15 | MICROSOFT_CLIENT_ID= 16 | MICROSOFT_CLIENT_SECRET= 17 | TWITCH_CLIENT_ID= 18 | TWITCH_CLIENT_SECRET= 19 | FACEBOOK_CLIENT_ID= 20 | FACEBOOK_CLIENT_SECRET= 21 | NODE_ENV= 22 | STRIPE_KEY= 23 | STRIPE_WEBHOOK_SECRET= 24 | PAYPAL_CLIENT_ID= 25 | PAYPAL_CLIENT_SECRET= 26 | 27 | ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore 2 | 3 | # Logs 4 | 5 | logs 6 | _.log 7 | npm-debug.log_ 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | .pnpm-debug.log* 12 | 13 | # Caches 14 | 15 | .cache 16 | 17 | # Diagnostic reports (https://nodejs.org/api/report.html) 18 | 19 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 20 | 21 | # Runtime data 22 | 23 | pids 24 | _.pid 25 | _.seed 26 | *.pid.lock 27 | 28 | # Directory for instrumented libs generated by jscoverage/JSCover 29 | 30 | lib-cov 31 | 32 | # Coverage directory used by tools like istanbul 33 | 34 | coverage 35 | *.lcov 36 | 37 | # nyc test coverage 38 | 39 | .nyc_output 40 | 41 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 42 | 43 | .grunt 44 | 45 | # Bower dependency directory (https://bower.io/) 46 | 47 | bower_components 48 | 49 | # node-waf configuration 50 | 51 | .lock-wscript 52 | 53 | # Compiled binary addons (https://nodejs.org/api/addons.html) 54 | 55 | build/Release 56 | 57 | # Dependency directories 58 | 59 | node_modules/ 60 | jspm_packages/ 61 | 62 | # Snowpack dependency directory (https://snowpack.dev/) 63 | 64 | web_modules/ 65 | 66 | # TypeScript cache 67 | 68 | *.tsbuildinfo 69 | 70 | # Optional npm cache directory 71 | 72 | .npm 73 | 74 | # Optional eslint cache 75 | 76 | .eslintcache 77 | 78 | # Optional stylelint cache 79 | 80 | .stylelintcache 81 | 82 | # Microbundle cache 83 | 84 | .rpt2_cache/ 85 | .rts2_cache_cjs/ 86 | .rts2_cache_es/ 87 | .rts2_cache_umd/ 88 | 89 | # Optional REPL history 90 | 91 | .node_repl_history 92 | 93 | # Output of 'npm pack' 94 | 95 | *.tgz 96 | 97 | # Yarn Integrity file 98 | 99 | .yarn-integrity 100 | 101 | # dotenv environment variable files 102 | 103 | .env 104 | .env.development.local 105 | .env.test.local 106 | .env.production.local 107 | .env.local 108 | 109 | # parcel-bundler cache (https://parceljs.org/) 110 | 111 | .parcel-cache 112 | 113 | # Next.js build output 114 | 115 | .next 116 | out 117 | 118 | # Nuxt.js build / generate output 119 | 120 | .nuxt 121 | dist 122 | 123 | # Gatsby files 124 | 125 | # Comment in the public line in if your project uses Gatsby and not Next.js 126 | 127 | # https://nextjs.org/blog/next-9-1#public-directory-support 128 | 129 | # public 130 | 131 | # vuepress build output 132 | 133 | .vuepress/dist 134 | 135 | # vuepress v2.x temp and cache directory 136 | 137 | .temp 138 | 139 | # Docusaurus cache and generated files 140 | 141 | .docusaurus 142 | 143 | # Serverless directories 144 | 145 | .serverless/ 146 | 147 | # FuseBox cache 148 | 149 | .fusebox/ 150 | 151 | # DynamoDB Local files 152 | 153 | .dynamodb/ 154 | 155 | # TernJS port file 156 | 157 | .tern-port 158 | 159 | # Stores VSCode versions used for testing VSCode extensions 160 | 161 | .vscode-test 162 | 163 | # yarn v2 164 | 165 | .yarn/cache 166 | .yarn/unplugged 167 | .yarn/build-state.yml 168 | .yarn/install-state.gz 169 | .pnp.* 170 | 171 | # IntelliJ based IDEs 172 | .idea 173 | 174 | # Finder (MacOS) folder config 175 | .DS_Store 176 | 177 | .notes/ 178 | 179 | .db/ 180 | 181 | *.sqlite 182 | *.db 183 | 184 | .turbo/ 185 | .tsup 186 | .todo 187 | 188 | 189 | android/ 190 | .expo/ 191 | 192 | 193 | .vercel 194 | .vinxi 195 | # Turborepo 196 | .turbo 197 | 198 | playwright-report/ 199 | test-results/ 200 | 201 | state.txt 202 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Created by https://www.toptal.com/developers/gitignore/api/node,macos 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=node,macos 3 | 4 | ### macOS ### 5 | # General 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # Icon must end with two \r 11 | Icon 12 | 13 | # Thumbnails 14 | ._* 15 | 16 | # Files that might appear in the root of a volume 17 | .DocumentRevisions-V100 18 | .fseventsd 19 | .Spotlight-V100 20 | .TemporaryItems 21 | .Trashes 22 | .VolumeIcon.icns 23 | .com.apple.timemachine.donotpresent 24 | 25 | # Directories potentially created on remote AFP share 26 | .AppleDB 27 | .AppleDesktop 28 | Network Trash Folder 29 | Temporary Items 30 | .apdisk 31 | 32 | ### Node ### 33 | # Logs 34 | logs 35 | *.log 36 | npm-debug.log* 37 | yarn-debug.log* 38 | yarn-error.log* 39 | lerna-debug.log* 40 | .pnpm-debug.log* 41 | 42 | # Diagnostic reports (https://nodejs.org/api/report.html) 43 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 44 | 45 | # Runtime data 46 | pids 47 | *.pid 48 | *.seed 49 | *.pid.lock 50 | 51 | # Directory for instrumented libs generated by jscoverage/JSCover 52 | lib-cov 53 | 54 | # Coverage directory used by tools like istanbul 55 | coverage 56 | *.lcov 57 | 58 | # nyc test coverage 59 | .nyc_output 60 | 61 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 62 | .grunt 63 | 64 | # Bower dependency directory (https://bower.io/) 65 | bower_components 66 | 67 | # node-waf configuration 68 | .lock-wscript 69 | 70 | # Compiled binary addons (https://nodejs.org/api/addons.html) 71 | build/Release 72 | 73 | # Dependency directories 74 | node_modules/ 75 | jspm_packages/ 76 | 77 | # Moved from ./templates for ignoring all locks in templates 78 | templates/**/*-lock.* 79 | templates/**/*.lock 80 | 81 | # Snowpack dependency directory (https://snowpack.dev/) 82 | web_modules/ 83 | 84 | # TypeScript cache 85 | *.tsbuildinfo 86 | 87 | # Optional npm cache directory 88 | .npm 89 | 90 | # Optional eslint cache 91 | .eslintcache 92 | 93 | # Optional stylelint cache 94 | .stylelintcache 95 | 96 | # Microbundle cache 97 | .rpt2_cache/ 98 | .rts2_cache_cjs/ 99 | .rts2_cache_es/ 100 | .rts2_cache_umd/ 101 | 102 | # Optional REPL history 103 | .node_repl_history 104 | 105 | # Output of 'npm pack' 106 | *.tgz 107 | 108 | # Yarn Integrity file 109 | .yarn-integrity 110 | 111 | # dotenv environment variable files 112 | .env 113 | .env.development.local 114 | .env.test.local 115 | .env.production.local 116 | .env.local 117 | 118 | # parcel-bundler cache (https://parceljs.org/) 119 | .cache 120 | .parcel-cache 121 | 122 | # Next.js build output 123 | .next 124 | out 125 | 126 | # Nuxt.js build / generate output 127 | .nuxt 128 | dist 129 | 130 | # Gatsby files 131 | .cache/ 132 | # Comment in the public line in if your project uses Gatsby and not Next.js 133 | # https://nextjs.org/blog/next-9-1#public-directory-support 134 | # public 135 | 136 | # vuepress build output 137 | .vuepress/dist 138 | 139 | # vuepress v2.x temp and cache directory 140 | .temp 141 | 142 | # Docusaurus cache and generated files 143 | .docusaurus 144 | 145 | # Serverless directories 146 | .serverless/ 147 | 148 | # FuseBox cache 149 | .fusebox/ 150 | 151 | # DynamoDB Local files 152 | .dynamodb/ 153 | 154 | # TernJS port file 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | .vscode-test 159 | 160 | # yarn v2 161 | .yarn/cache 162 | .yarn/unplugged 163 | .yarn/build-state.yml 164 | .yarn/install-state.gz 165 | .pnp.* 166 | 167 | ### Node Patch ### 168 | # Serverless Webpack directories 169 | .webpack/ 170 | 171 | # Optional stylelint cache 172 | 173 | # SvelteKit build / generate output 174 | .svelte-kit 175 | 176 | # End of https://www.toptal.com/developers/gitignore/api/node,macos 177 | 178 | # Wrangler output 179 | .wrangler/ 180 | build/ 181 | 182 | # Turbo output 183 | .turbo/ 184 | 185 | .dev.vars* 186 | !.dev.vars.example 187 | .env* 188 | !.env.example 189 | ``` -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Better Auth Docs 2 | 3 | This is the documentation site for Better Auth. 4 | 5 | ## Contributing 6 | 7 | To contribute to the docs, you can clone the repo and run the docs site locally. 8 | 9 | ```bash 10 | pnpm install 11 | pnpm run dev 12 | ``` 13 | 14 | This will start the docs site on [http://localhost:3000](http://localhost:3000). ``` -------------------------------------------------------------------------------- /demo/expo-example/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Better Auth Expo Example 2 | 3 | This is an example of how to use Better Auth with Expo. It uses Expo's new API Router to host the auth server. 4 | 5 | ## How to run 6 | 7 | 1. Clone the code sandbox (or the repo) and open it in your code editor 8 | 2. Move and Provide environment variable 9 | 3. Run the following commands 10 | ```bash 11 | pnpm install 12 | pnpm start 13 | ```s 14 | 15 | Checkout the [expo guide](https://www.better-auth.com/docs/integrations/expo) to learn more. ``` -------------------------------------------------------------------------------- /packages/better-auth/README.md: -------------------------------------------------------------------------------- ```markdown 1 | <p align="center"> 2 | <picture> 3 | <source srcset="https://github.com/better-auth/better-auth/blob/main/banner-dark.png?raw=true" media="(prefers-color-scheme: dark)"> 4 | <source srcset="https://github.com/better-auth/better-auth/blob/main/banner.png?raw=true" media="(prefers-color-scheme: light)"> 5 | <img src="https://github.com/better-auth/better-auth/blob/main/banner.png?raw=true" alt="Better Auth Logo"> 6 | </picture> 7 | <h2 align="center"> 8 | Better Auth 9 | </h2> 10 | 11 | <p align="center"> 12 | The most comprehensive authentication library for TypeScript 13 | <br /> 14 | <a href="https://better-auth.com"><strong>Learn more »</strong></a> 15 | <br /> 16 | <br /> 17 | <a href="https://discord.gg/better-auth">Discord</a> 18 | · 19 | <a href="https://better-auth.com">Website</a> 20 | · 21 | <a href="https://github.com/better-auth/better-auth/issues">Issues</a> 22 | </p> 23 | </p> 24 | 25 | 26 | 27 | 28 | ## Getting Started 29 | 30 | ```bash 31 | pnpm install better-auth 32 | ``` 33 | 34 | Read the [Installation Guide](https://better-auth.com/docs/installation) to learn more. 35 | ``` -------------------------------------------------------------------------------- /packages/cli/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Better Auth CLI 2 | 3 | Better Auth comes with a built-in CLI to help you manage the database schema needed for both core functionality and plugins. 4 | 5 | 6 | ### **Init** 7 | 8 | The CLI includes an `init` command to add Better Auth to your project. 9 | 10 | ```bash title="terminal" 11 | npx @better-auth/cli@latest init 12 | ``` 13 | 14 | ### **Generate** 15 | 16 | The `generate` command creates the schema required by Better Auth. If you're using a database adapter like Prisma or Drizzle, this command will generate the right schema for your ORM. If you're using the built-in Kysely adapter, it will generate an SQL file you can run directly on your database. 17 | 18 | ```bash title="terminal" 19 | npx @better-auth/cli@latest generate 20 | ``` 21 | 22 | ### **Migrate** 23 | 24 | The `migrate` command applies the Better Auth schema directly to your database. This is available if you’re using the built-in Kysely adapter. For other adapters, you'll need to apply the schema using your ORM's migration tool. 25 | 26 | ```bash title="terminal" 27 | npx @better-auth/cli@latest migrate 28 | ``` 29 | 30 | ### **Secret** 31 | 32 | The CLI also provides a way to generate a secret key for your Better Auth instance. 33 | 34 | ```bash title="terminal" 35 | npx @better-auth/cli@latest secret 36 | ``` 37 | 38 | 39 | ## License 40 | 41 | MIT 42 | ``` -------------------------------------------------------------------------------- /docs/scripts/endpoint-to-doc/readme.md: -------------------------------------------------------------------------------- ```markdown 1 | # Endpoint To Documentation 2 | 3 | This script allows you to copy the code of what you would normally pass into `createAuthEndpoint`, and it will automatically convert it into a `APIMethod` component which you can use in the Better-Auth documentation 4 | to easily document the details of a given endpoint. 5 | 6 | This script will also generate JSDoc which you can then place above each endpoint code. 7 | 8 | ## Requirements 9 | 10 | This does however require Bun since we're running typescript code without transpiling to JS before executing. 11 | 12 | ## How to run 13 | 14 | Head into the `docs/scripts/endpoint-to-doc/input.ts` file, 15 | and copy over the desired `createAuthEndpoint` properties. 16 | 17 | Note: The file has `//@ts-nocheck` at the start of the file, so that we can ignore type errors that may be within the handler param. 18 | Since we don't run the handler, we can safely ignore those types. 19 | However, it's possible that the options param may be using a middleware indicated by the `use` prop, and likely using a variable undefined in this context. So remember to remove any `use` props in the options. 20 | 21 | Then, make sure you're in the `docs` directory within your terminal. 22 | 23 | and run: 24 | 25 | ```bash 26 | bun scripts:endpoint-to-doc 27 | ``` 28 | 29 | This will read and execute that `input.ts` file which you have recently edited. It may prompt you to answer a few questions, and after it will output a `output.mdx` file which you can then copy it's contents to the Better-Auth docs. ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown 1 | <p align="center"> 2 | <picture> 3 | <source srcset="./banner-dark.png" media="(prefers-color-scheme: dark)"> 4 | <source srcset="./banner.png" media="(prefers-color-scheme: light)"> 5 | <img src="./banner.png" alt="Better Auth Logo"> 6 | </picture> 7 | <h2 align="center"> 8 | Better Auth 9 | </h2> 10 | 11 | <p align="center"> 12 | The most comprehensive authentication library for TypeScript 13 | <br /> 14 | <a href="https://better-auth.com"><strong>Learn more »</strong></a> 15 | <br /> 16 | <br /> 17 | <a href="https://discord.gg/better-auth">Discord</a> 18 | · 19 | <a href="https://better-auth.com">Website</a> 20 | · 21 | <a href="https://github.com/better-auth/better-auth/issues">Issues</a> 22 | </p> 23 | 24 | [](https://npm.chart.dev/better-auth?primary=neutral&gray=neutral&theme=dark) 25 | [](https://www.npmjs.com/package/better-auth) 26 | [](https://github.com/better-auth/better-auth/stargazers) 27 | </p> 28 | 29 | ## About the Project 30 | 31 | Better Auth is framework-agnostic authentication (and authorization) library for TypeScript. It provides a comprehensive set of features out of the box and includes a plugin ecosystem that simplifies adding advanced functionalities with minimal code in short amount of time. Whether you need 2FA, multi-tenant support, or other complex features. It lets you focus on building your actual application instead of reinventing the wheel. 32 | 33 | ### Why Better Auth 34 | 35 | Authentication in the TypeScript ecosystem is a half-solved problem. Other open-source libraries often require a lot of additional code for anything beyond basic authentication. Rather than just pushing third-party services as the solution, I believe we can do better as a community—hence, Better Auth. 36 | 37 | ## Contribution 38 | 39 | Better Auth is free and open source project licensed under the [MIT License](./LICENSE.md). You are free to do whatever you want with it. 40 | 41 | You could help continuing its development by: 42 | 43 | - [Contribute to the source code](./CONTRIBUTING.md) 44 | - [Suggest new features and report issues](https://github.com/better-auth/better-auth/issues) 45 | 46 | ## Security 47 | If you discover a security vulnerability within Better Auth, please send an e-mail to [email protected]. 48 | 49 | All reports will be promptly addressed, and you'll be credited accordingly. 50 | ``` -------------------------------------------------------------------------------- /packages/expo/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Better Auth Expo Plugin 2 | 3 | This plugin integrates Better Auth with Expo, allowing you to easily add authentication to your Expo (React Native) applications. It supports both Expo native and web apps. 4 | 5 | ## Installation 6 | 7 | To get started, install the necessary packages: 8 | 9 | ```bash 10 | # Using npm 11 | npm install better-auth @better-auth/expo 12 | 13 | # Using yarn 14 | yarn add better-auth @better-auth/expo 15 | 16 | # Using pnpm 17 | pnpm add better-auth @better-auth/expo 18 | 19 | # Using bun 20 | bun add better-auth @better-auth/expo 21 | ``` 22 | 23 | You will also need to install `expo-secure-store` for secure session and cookie storage in your Expo app: 24 | 25 | ```bash 26 | npm install expo-secure-store 27 | # or 28 | yarn add expo-secure-store 29 | # or 30 | pnpm add expo-secure-store 31 | # or 32 | bun add expo-secure-store 33 | ``` 34 | 35 | ## Basic Usage 36 | 37 | ### Configure the Better Auth Backend 38 | 39 | Ensure you have a Better Auth backend set up. You can follow the main [Installation Guide](https://www.better-auth.com/docs/installation). 40 | 41 | Then, add the Expo plugin to your Better Auth server configuration (e.g., in your `auth.ts` or `lib/auth.ts` file): 42 | 43 | ```typescript 44 | // lib/auth.ts 45 | import { betterAuth } from "better-auth"; 46 | import { expo } from "@better-auth/expo"; // Import the server plugin 47 | 48 | export const auth = betterAuth({ 49 | // ...your other Better Auth options 50 | baseURL: "http://localhost:8081", // The base URL of your application server where the routes are mounted. 51 | plugins: [expo()], // Add the Expo server plugin 52 | emailAndPassword: { 53 | enabled: true, 54 | }, 55 | // Add other configurations like trustedOrigins 56 | trustedOrigins: ["myapp://"] // Replace "myapp" with your app's scheme 57 | }); 58 | ``` 59 | 60 | ### Initialize the Better Auth Client in Expo 61 | 62 | In your Expo app, initialize the client (e.g., in `lib/auth-client.ts`): 63 | 64 | ```typescript 65 | // lib/auth-client.ts 66 | import { createAuthClient } from "better-auth/react"; 67 | import { expoClient } from "@better-auth/expo/client"; // Import the client plugin 68 | import * as SecureStore from "expo-secure-store"; 69 | 70 | export const authClient = createAuthClient({ 71 | baseURL: "http://localhost:8081", // Your Better Auth backend URL 72 | plugins: [ 73 | expoClient({ 74 | scheme: "myapp", // Your app's scheme (defined in app.json) 75 | storagePrefix: "myapp", // A prefix for storage keys 76 | storage: SecureStore, // Pass SecureStore for token storage 77 | }) 78 | ] 79 | }); 80 | 81 | // You can also export specific methods if you prefer: 82 | // export const { signIn, signUp, useSession } = authClient; 83 | ``` 84 | Make sure your app's scheme (e.g., "myapp") is defined in your `app.json`. 85 | 86 | ## Documentation 87 | 88 | For more detailed information and advanced configurations, please refer to the documentation: 89 | 90 | - **Main Better Auth Installation:** [https://www.better-auth.com/docs/installation](https://www.better-auth.com/docs/installation) 91 | - **Expo Integration Guide:** [https://www.better-auth.com/docs/integrations/expo](https://www.better-auth.com/docs/integrations/expo) 92 | 93 | ## License 94 | 95 | MIT 96 | ``` -------------------------------------------------------------------------------- /demo/nextjs/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Better Auth Demo App 2 | 3 | Welcome to the Better Auth demo app! This project is built with [Next.js](https://nextjs.org) using [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). 4 | 5 | ## Getting Started 6 | 7 | Here’s how you can get the app running locally: 8 | 9 | ### Prerequisites 10 | 11 | 1. **Clone the repo**: 12 | 13 | ```bash 14 | git clone https://github.com/better-auth/better-auth 15 | cd better-auth/demo/nextjs 16 | ``` 17 | 18 | 2. **Install the dependencies**: 19 | 20 | ```bash 21 | npm install 22 | # or 23 | yarn install 24 | # or 25 | pnpm install 26 | ``` 27 | 28 | 3. **Set up your environment variables**: 29 | 30 | - Rename the `.env.example` file to `.env`: 31 | 32 | ```bash 33 | mv .env.example .env 34 | ``` 35 | 36 | - Open `.env` and fill in the required details. These will include things like API URLs, client IDs, and secrets needed to connect to the Better Auth service. 37 | 38 | Make sure `TURSO_DATABASE_URL=your_turso_url` and `TURSO_AUTH_TOKEN=your_turso_token` is set or `USE_MYSQL=true` and `MYSQL_DATABASE_URL=your_mysql_url` is set. 39 | 40 | ### Start the Development Server 41 | 42 | Once everything is set up, start the development server with: 43 | 44 | ```bash 45 | npm run dev 46 | # or 47 | yarn dev 48 | # or 49 | pnpm dev 50 | # or 51 | bun dev 52 | ``` 53 | 54 | The app will be live at [http://localhost:3000](http://localhost:3000). Open it in your browser, and you’re good to go! 55 | 56 | Feel free to jump in and edit the app by modifying `app/page.tsx`. Any changes you make will update automatically in the browser. 57 | 58 | ## Features 59 | 60 | Here’s what this app supports out of the box: 61 | 62 | - **[Email & Password](https://www.better-auth.com/docs/basic-usage#email-password)**: Simple and secure authentication. 63 | - **[Organization / Teams](https://www.better-auth.com/docs/plugins/organization)**: Manage users within organizations or teams. 64 | - **[Passkeys](https://www.better-auth.com/docs/plugins/passkey)**: Passwordless login using modern authentication standards. 65 | - **[Multi-Factor Authentication (MFA)](https://www.better-auth.com/docs/plugins/2fa)**: Add an extra layer of security. 66 | - **[Password Reset](https://www.better-auth.com/docs/concepts/email#password-reset-email)**: Let users reset their passwords if they forget them. 67 | - **[Email Verification](https://www.better-auth.com/docs/concepts/email#email-verification)**: Ensure users verify their email addresses. 68 | - **[Roles & Permissions](https://www.better-auth.com/docs/plugins/admin#role)**: Define and manage who can do what. 69 | - **[Rate Limiting](https://www.better-auth.com/docs/concepts/rate-limit)**: Protect your app from abuse with smart limits. 70 | - **[Session Management](https://www.better-auth.com/docs/concepts/session-management)**: Handle user sessions seamlessly. 71 | - **[Stripe Plugin](https://www.better-auth.com/docs/plugins/stripe)**: Integrate Stripe for customer management, subscriptions, and webhooks. 72 | 73 | ## Learn More 74 | 75 | Here are some helpful links if you want to dive deeper: 76 | 77 | - [Better Auth Documentation](https://better-auth.com/docs) - Everything you need to know to integrate Better Auth. 78 | - [Next.js Documentation](https://nextjs.org/docs) - Learn about the framework we used to build this app. 79 | - [Learn Next.js](https://nextjs.org/learn) - A hands-on tutorial for Next.js. 80 | 81 | --- 82 | 83 | If you run into issues or have suggestions, feel free to open an issue or submit a pull request on the [GitHub repo](https://github.com/better-auth/better-auth). 84 | 85 | Happy coding! 86 | ``` -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Security Policy 2 | 3 | ### Reporting a Vulnerability 4 | 5 | If you believe you've found a security vulnerability, please follow these steps: 6 | 7 | 1. Do not disclose the vulnerability publicly until it has been addressed by our team. 8 | 2. Email your findings to `[email protected]` Include: 9 | - A description of the vulnerability 10 | - Steps to reproduce the vulnerability 11 | - Potential impact of the vulnerability 12 | - Any suggestions for mitigation 13 | - Any other relevant information 14 | 3. We will respond to your report within 72 hours. 15 | 4. If the issue is confirmed, we will release a patch as soon as possible. 16 | 17 | ### Disclosure Policy 18 | 19 | If the issue is confirmed, we will release a patch as soon as possible. Once a patch is released, we will disclose the issue publicly. If 90 days has elapsed and we still don't have a fix, we will disclose the issue publicly. 20 | 21 | ## Supported Versions 22 | 23 | We only support the latest version of Better Auth. Older versions are not supported. ``` -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- ```markdown 1 | The MIT License (MIT) 2 | Copyright (c) 2024 - present, Bereket Engida 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 7 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or 11 | substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 14 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 15 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 16 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- ```markdown 1 | # Contributing to Better Auth 2 | 3 | Thank you for your interest in contributing to Better Auth. This guide will help you get started with the contribution process. 4 | 5 | ## Code of Conduct 6 | 7 | This project and everyone participating in it is governed by our [Code of Conduct](/CODE_OF_CONDUCT.md) By participating, you are expected to uphold this code. 8 | 9 | ## Project Structure 10 | 11 | The Better Auth monorepo is organized as follows: 12 | 13 | - `/packages/better-auth` - Core authentication library 14 | - `/packages/cli` - Command-line interface tools 15 | - `/packages/expo` - Expo integration 16 | - `/packages/stripe` - Stripe payment integration 17 | - `/packages/sso` - SSO plugin with SAML and OIDC support 18 | - `/docs` - Documentation website 19 | - `/examples` - Example applications 20 | - `/demo` - Demo applications 21 | 22 | ## Development Guidelines 23 | 24 | When contributing to Better Auth: 25 | 26 | - Keep changes focused. Large PRs are harder to review and unlikely to be accepted. We recommend opening an issue and discussing it with us first. 27 | - Ensure all code is type-safe and takes full advantage of TypeScript features. 28 | - Write clear, self-explanatory code. Use comments only when truly necessary. 29 | - Maintain a consistent and predictable API across all supported frameworks. 30 | - Follow the existing code style and conventions. 31 | - We aim for stability, so avoid changes that would require users to run a migration or update their config... 32 | 33 | ## Getting Started 34 | 35 | 1. Fork the repository to your GitHub account 36 | 2. Clone your fork locally: 37 | ```bash 38 | git clone https://github.com/your-username/better-auth.git 39 | cd better-auth 40 | ``` 41 | 3. Install Node.js (LTS version recommended) 42 | 4. Install pnpm if you haven't already: 43 | ```bash 44 | npm install -g pnpm 45 | ``` 46 | 5. Install project dependencies: 47 | ```bash 48 | pnpm install 49 | ``` 50 | 51 | 6. Create a `.env` file from the example: 52 | - On Unix-based systems: 53 | ```bash 54 | cp -n ./docs/.env.example ./docs/.env 55 | ``` 56 | - On Windows: 57 | ```cmd 58 | copy /Y .\docs\.env.example .\docs\.env 59 | ``` 60 | 61 | 7. Build the project: 62 | ```bash 63 | pnpm build 64 | ``` 65 | 66 | 8. Run the documentation locally: 67 | ```bash 68 | pnpm -F docs dev 69 | ``` 70 | 71 | 72 | ## Code Formatting with BiomeJS 73 | 74 | We use [BiomeJS](https://biomejs.dev/) for code formatting and linting. Before committing, please ensure your code is properly formatted: 75 | 76 | ```bash 77 | # Format all code 78 | pnpm format 79 | 80 | # Check for linting issues 81 | pnpm lint 82 | 83 | # Fix auto-fixable issues 84 | pnpm lint:fix 85 | ``` 86 | 87 | ## Development Workflow 88 | 89 | 1. Create a new branch for your changes: 90 | ```bash 91 | git checkout -b type/description 92 | # Example: git checkout -b feat/oauth-provider 93 | ``` 94 | 95 | Branch type prefixes: 96 | - `feat/` - New features 97 | - `fix/` - Bug fixes 98 | - `docs/` - Documentation changes 99 | - `refactor/` - Code refactoring 100 | - `test/` - Test-related changes 101 | - `chore/` - Build process or tooling changes 102 | 103 | 2. Make your changes following the code style guidelines 104 | 3. Add tests for your changes 105 | 4. Run the test suite: 106 | ```bash 107 | # Run all tests 108 | pnpm test 109 | 110 | # Run tests for a specific package 111 | pnpm -F "{packagename}" test 112 | ``` 113 | 5. Ensure all tests pass and the code is properly formatted 114 | 6. Commit your changes with a descriptive message following this format: 115 | For changes that need to be included in the changelog (excluding docs or chore changes), use the `fix` or `feat` format with a specific scope: 116 | ``` 117 | fix(organization): fix incorrect member role assignment 118 | 119 | feat(two-factor): add support for TOTP authentication 120 | ``` 121 | 122 | For core library changes that don't have a specific plugin or scope, you can use `fix` and `feat` without a scope: 123 | ``` 124 | fix: resolve memory leak in session handling 125 | 126 | feat: add support for custom error messages 127 | ``` 128 | 129 | For documentation changes, use `docs`: 130 | ```bash 131 | docs: improve authentication flow explanation 132 | docs: fix typos in API reference 133 | ``` 134 | 135 | For changes that refactor or don't change the functionality of the library or docs, use `chore`: 136 | ```bash 137 | chore(refactor): reorganize authentication middleware 138 | chore: update dependencies to latest versions 139 | ``` 140 | 141 | Each commit message should be clear and descriptive, explaining what the change does. For features and fixes, include context about what was added or resolved. 142 | 7. Push your branch to your fork 143 | 8. Open a pull request against the **canary** branch. In your PR description: 144 | - Clearly describe what changes you made and why 145 | - Include any relevant context or background 146 | - List any breaking changes or deprecations 147 | - Add screenshots for UI changes 148 | - Reference related issues or discussions 149 | 150 | ## Testing 151 | 152 | All contributions must include appropriate tests. Follow these guidelines: 153 | 154 | - Write unit tests for new features 155 | - Ensure all tests pass before submitting a pull request 156 | - Update existing tests if your changes affect their behavior 157 | - Follow the existing test patterns and structure 158 | - Test across different environments when applicable 159 | 160 | ## Pull Request Process 161 | 162 | 1. Create a draft pull request early to facilitate discussion 163 | 2. Reference any related issues in your PR description (e.g., 'Closes #123') 164 | 3. Ensure all tests pass and the build is successful 165 | 4. Update documentation as needed 166 | 5. Keep your PR focused on a single feature or bug fix 167 | 6. Be responsive to code review feedback 168 | 7. Update the CHANGELOG.md if your changes are user-facing 169 | 170 | ## Code Style 171 | 172 | - Follow the existing code style 173 | - Use TypeScript types and interfaces effectively 174 | - Keep functions small and focused 175 | - Use meaningful variable and function names 176 | - Add comments for complex logic 177 | - Update relevant documentation when making API changes 178 | - Follow the BiomeJS formatting rules 179 | - Avoid using Classes 180 | 181 | ## Component-Specific Guidelines 182 | 183 | ### Core Library (`/packages/better-auth`) 184 | - Keep the core library focused on essential authentication functionality 185 | - Plugins in the core generally are made by core members. If you have a plugin idea consider open sourcing it yourself instead. 186 | - Ensure all public APIs are well-documented with JSDoc comments 187 | - Maintain backward compatibility. If it's super necessary, provide a clear migration path 188 | - Follow the existing patterns for error handling and logging 189 | 190 | ### Documentation (`/docs`) 191 | 192 | - Keep documentation up-to-date with code changes 193 | - Use clear, concise language 194 | - Include code examples for common use cases 195 | - Document any breaking changes in the migration guide 196 | - Follow the existing documentation style and structure 197 | 198 | ## Security Issues 199 | 200 | For security-related issues, please email [email protected]. Include a detailed description of the vulnerability and steps to reproduce it. All reports will be reviewed and addressed promptly. For more information, see our [security documentation](/docs/reference/security). ``` -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- ```markdown 1 | 2 | # Contributor Covenant 3.0 3 | 4 | ## Our Pledge 5 | 6 | We pledge to make our community welcoming, safe, and equitable for all. 7 | 8 | We are committed to fostering an environment that respects and promotes the dignity, rights, and contributions of all individuals, regardless of characteristics including race, ethnicity, caste, color, age, physical characteristics, neurodiversity, disability, sex or gender, gender identity or expression, sexual orientation, language, philosophy or religion, national or social origin, socio-economic position, level of education, or other status. The same privileges of participation are extended to everyone who participates in good faith and in accordance with this Covenant. 9 | 10 | ## Encouraged Behaviors 11 | 12 | While acknowledging differences in social norms, we all strive to meet our community's expectations for positive behavior. We also understand that our words and actions may be interpreted differently than we intend based on culture, background, or native language. 13 | 14 | With these considerations in mind, we agree to behave mindfully toward each other and act in ways that center our shared values, including: 15 | 16 | 1. Respecting the **purpose of our community**, our activities, and our ways of gathering. 17 | 2. Engaging **kindly and honestly** with others. 18 | 3. Respecting **different viewpoints** and experiences. 19 | 4. **Taking responsibility** for our actions and contributions. 20 | 5. Gracefully giving and accepting **constructive feedback**. 21 | 6. Committing to **repairing harm** when it occurs. 22 | 7. Behaving in other ways that promote and sustain the **well-being of our community**. 23 | 24 | 25 | ## Restricted Behaviors 26 | 27 | We agree to restrict the following behaviors in our community. Instances, threats, and promotion of these behaviors are violations of this Code of Conduct. 28 | 29 | 1. **Harassment.** Violating explicitly expressed boundaries or engaging in unnecessary personal attention after any clear request to stop. 30 | 2. **Character attacks.** Making insulting, demeaning, or pejorative comments directed at a community member or group of people. 31 | 3. **Stereotyping or discrimination.** Characterizing anyone’s personality or behavior on the basis of immutable identities or traits. 32 | 4. **Sexualization.** Behaving in a way that would generally be considered inappropriately intimate in the context or purpose of the community. 33 | 5. **Violating confidentiality**. Sharing or acting on someone's personal or private information without their permission. 34 | 6. **Endangerment.** Causing, encouraging, or threatening violence or other harm toward any person or group. 35 | 7. Behaving in other ways that **threaten the well-being** of our community. 36 | 37 | ### Other Restrictions 38 | 39 | 1. **Misleading identity.** Impersonating someone else for any reason, or pretending to be someone else to evade enforcement actions. 40 | 2. **Failing to credit sources.** Not properly crediting the sources of content you contribute. 41 | 3. **Promotional materials**. Sharing marketing or other commercial content in a way that is outside the norms of the community. 42 | 4. **Irresponsible communication.** Failing to responsibly present content which includes, links or describes any other restricted behaviors. 43 | 44 | 45 | ## Reporting an Issue 46 | 47 | Tensions can occur between community members even when they are trying their best to collaborate. Not every conflict represents a code of conduct violation, and this Code of Conduct reinforces encouraged behaviors and norms that can help avoid conflicts and minimize harm. 48 | 49 | When an incident does occur, it is important to report it promptly. To report a possible violation, **email [email protected]** 50 | 51 | Community Moderators take reports of violations seriously and will make every effort to respond in a timely manner. They will investigate all reports of code of conduct violations, reviewing messages, logs, and recordings, or interviewing witnesses and other participants. Community Moderators will keep investigation and enforcement actions as transparent as possible while prioritizing safety and confidentiality. In order to honor these values, enforcement actions are carried out in private with the involved parties, but communicating to the whole community may be part of a mutually agreed upon resolution. 52 | 53 | 54 | ## Addressing and Repairing Harm 55 | 56 | **** 57 | 58 | If an investigation by the Community Moderators finds that this Code of Conduct has been violated, the following enforcement ladder may be used to determine how best to repair harm, based on the incident's impact on the individuals involved and the community as a whole. Depending on the severity of a violation, lower rungs on the ladder may be skipped. 59 | 60 | 1) Warning 61 | 1) Event: A violation involving a single incident or series of incidents. 62 | 2) Consequence: A private, written warning from the Community Moderators. 63 | 3) Repair: Examples of repair include a private written apology, acknowledgement of responsibility, and seeking clarification on expectations. 64 | 2) Temporarily Limited Activities 65 | 1) Event: A repeated incidence of a violation that previously resulted in a warning, or the first incidence of a more serious violation. 66 | 2) Consequence: A private, written warning with a time-limited cooldown period designed to underscore the seriousness of the situation and give the community members involved time to process the incident. The cooldown period may be limited to particular communication channels or interactions with particular community members. 67 | 3) Repair: Examples of repair may include making an apology, using the cooldown period to reflect on actions and impact, and being thoughtful about re-entering community spaces after the period is over. 68 | 3) Temporary Suspension 69 | 1) Event: A pattern of repeated violation which the Community Moderators have tried to address with warnings, or a single serious violation. 70 | 2) Consequence: A private written warning with conditions for return from suspension. In general, temporary suspensions give the person being suspended time to reflect upon their behavior and possible corrective actions. 71 | 3) Repair: Examples of repair include respecting the spirit of the suspension, meeting the specified conditions for return, and being thoughtful about how to reintegrate with the community when the suspension is lifted. 72 | 4) Permanent Ban 73 | 1) Event: A pattern of repeated code of conduct violations that other steps on the ladder have failed to resolve, or a violation so serious that the Community Moderators determine there is no way to keep the community safe with this person as a member. 74 | 2) Consequence: Access to all community spaces, tools, and communication channels is removed. In general, permanent bans should be rarely used, should have strong reasoning behind them, and should only be resorted to if working through other remedies has failed to change the behavior. 75 | 3) Repair: There is no possible repair in cases of this severity. 76 | 77 | This enforcement ladder is intended as a guideline. It does not limit the ability of Community Managers to use their discretion and judgment, in keeping with the best interests of our community. 78 | 79 | 80 | ## Scope 81 | 82 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public or other spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 83 | 84 | 85 | ## Attribution 86 | 87 | This Code of Conduct is adapted from the Contributor Covenant, version 3.0, permanently available at [https://www.contributor-covenant.org/version/3/0/](https://www.contributor-covenant.org/version/3/0/). 88 | 89 | Contributor Covenant is stewarded by the Organization for Ethical Source and licensed under CC BY-SA 4.0. To view a copy of this license, visit [https://creativecommons.org/licenses/by-sa/4.0/](https://creativecommons.org/licenses/by-sa/4.0/) 90 | 91 | For answers to common questions about Contributor Covenant, see the FAQ at [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are provided at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations). Additional enforcement and community guideline resources can be found at [https://www.contributor-covenant.org/resources](https://www.contributor-covenant.org/resources). The enforcement ladder was inspired by the work of [Mozilla’s code of conduct team](https://github.com/mozilla/inclusion). 92 | 93 | ``` -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- ```yaml 1 | github: [better-auth] ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/vite/src/server.ts: -------------------------------------------------------------------------------- ```typescript 1 | import "better-auth"; 2 | ``` -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "./types"; 2 | ``` -------------------------------------------------------------------------------- /demo/expo-example/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | import "expo-router/entry"; 2 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/admin/access/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "./statement"; 2 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/organization/access/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "./statement"; 2 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/api/middlewares/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "./origin-check"; 2 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/memory-adapter/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "./memory-adapter"; 2 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/prisma-adapter/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "./prisma-adapter"; 2 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/drizzle-adapter/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "./drizzle-adapter"; 2 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/mongodb-adapter/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "./mongodb-adapter"; 2 | ``` -------------------------------------------------------------------------------- /e2e/integration/vanilla-node/src/vite-env.d.ts: -------------------------------------------------------------------------------- ```typescript 1 | /// <reference types="vite/client" /> 2 | ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/src/global.d.ts: -------------------------------------------------------------------------------- ```typescript 1 | /// <reference types="@solidjs/start/env" /> 2 | ``` -------------------------------------------------------------------------------- /packages/core/src/utils/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export { defineErrorCodes } from "./error-codes"; 2 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/access/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "./access"; 2 | export * from "./types"; 3 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/social-providers/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "@better-auth/core/social-providers"; 2 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/admin/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "./admin"; 2 | export type * from "./types"; 3 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/hide-metadata.ts: -------------------------------------------------------------------------------- ```typescript 1 | export const HIDE_METADATA = { 2 | isAction: false as const, 3 | }; 4 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/constants.ts: -------------------------------------------------------------------------------- ```typescript 1 | export const DEFAULT_SECRET = "better-auth-secret-123456789"; 2 | ``` -------------------------------------------------------------------------------- /demo/nextjs/postcss.config.mjs: -------------------------------------------------------------------------------- ``` 1 | export default { 2 | plugins: { 3 | "@tailwindcss/postcss": {}, 4 | }, 5 | }; 6 | ``` -------------------------------------------------------------------------------- /docs/postcss.config.js: -------------------------------------------------------------------------------- ```javascript 1 | export default { 2 | plugins: { 3 | "@tailwindcss/postcss": {}, 4 | }, 5 | }; 6 | ``` -------------------------------------------------------------------------------- /demo/expo-example/components.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "aliases": { 3 | "components": "@/components", 4 | "lib": "@/lib" 5 | } 6 | } 7 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/kysely-adapter/types.ts: -------------------------------------------------------------------------------- ```typescript 1 | export type KyselyDatabaseType = "postgres" | "mysql" | "sqlite" | "mssql"; 2 | ``` -------------------------------------------------------------------------------- /packages/core/src/context/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export { 2 | getCurrentAdapter, 3 | runWithAdapter, 4 | runWithTransaction, 5 | } from "./transaction"; 6 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/boolean.ts: -------------------------------------------------------------------------------- ```typescript 1 | export function toBoolean(value: any): boolean { 2 | return value === "true" || value === true; 3 | } 4 | ``` -------------------------------------------------------------------------------- /docs/app/layout.config.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { source } from "@/lib/source"; 2 | 3 | export const docsOptions = { 4 | tree: source.pageTree, 5 | }; 6 | ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/app.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { defineConfig } from "@solidjs/start/config"; 2 | 3 | export default defineConfig({ 4 | ssr: false, 5 | }); 6 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/misc.ts: -------------------------------------------------------------------------------- ```typescript 1 | export function capitalizeFirstLetter(str: string) { 2 | return str.charAt(0).toUpperCase() + str.slice(1); 3 | } 4 | ``` -------------------------------------------------------------------------------- /demo/nextjs/lib/email/resend.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { Resend } from "resend"; 2 | 3 | export const resend = new Resend(process.env.RESEND_API_KEY || "re_123"); 4 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/two-factor/constant.ts: -------------------------------------------------------------------------------- ```typescript 1 | export const TWO_FACTOR_COOKIE_NAME = "two_factor"; 2 | export const TRUST_DEVICE_COOKIE_NAME = "trust_device"; 3 | ``` -------------------------------------------------------------------------------- /demo/expo-example/expo-env.d.ts: -------------------------------------------------------------------------------- ```typescript 1 | /// <reference types="expo/types" /> 2 | 3 | // NOTE: This file should not be edited and should be in your git ignore 4 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/test/apply-migrations.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { applyD1Migrations, env } from "cloudflare:test"; 2 | 3 | await applyD1Migrations(env.DB, env.TEST_MIGRATIONS); 4 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/import-util.ts: -------------------------------------------------------------------------------- ```typescript 1 | export const importRuntime = <T>(m: string): Promise<T> => { 2 | return (Function("mm", "return import(mm)") as any)(m); 3 | }; 4 | ``` -------------------------------------------------------------------------------- /packages/telemetry/src/utils/import-util.ts: -------------------------------------------------------------------------------- ```typescript 1 | export const importRuntime = <T>(m: string): Promise<T> => { 2 | return (Function("mm", "return import(mm)") as any)(m); 3 | }; 4 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/oauth2/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "@better-auth/core/oauth2"; 2 | export * from "./utils"; 3 | export * from "./state"; 4 | export * from "./link-account"; 5 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/organization/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "./organization"; 2 | export type * from "./schema"; 3 | export type * from "./access"; 4 | export type * from "./types"; 5 | ``` -------------------------------------------------------------------------------- /e2e/integration/vanilla-node/vite.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { defineConfig } from "vite"; 2 | 3 | export default defineConfig({ 4 | server: { 5 | allowedHosts: ["test.com", "localhost"], 6 | }, 7 | }); 8 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/drizzle.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { defineConfig } from "drizzle-kit"; 2 | 3 | export default defineConfig({ 4 | dialect: "sqlite", 5 | schema: "./src/auth-schema.ts", 6 | }); 7 | ``` -------------------------------------------------------------------------------- /demo/expo-example/src/lib/icons/X.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { X } from "lucide-react-native"; 2 | import { iconWithClassName } from "./iconWithClassName"; 3 | iconWithClassName(X); 4 | export { X }; 5 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "./misc"; 2 | export * from "./hide-metadata"; 3 | export * from "./id"; 4 | export * from "../oauth2/state"; 5 | export * from "./id"; 6 | ``` -------------------------------------------------------------------------------- /docs/app/reference/route.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { ApiReference } from "@scalar/nextjs-api-reference"; 2 | 3 | export const GET = ApiReference({ 4 | spec: { 5 | url: "/openapi.yml", 6 | }, 7 | }); 8 | ``` -------------------------------------------------------------------------------- /packages/telemetry/tsdown.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { defineConfig } from "tsdown"; 2 | 3 | export default defineConfig({ 4 | dts: true, 5 | format: ["esm", "cjs"], 6 | entry: ["./src/index.ts"], 7 | }); 8 | ``` -------------------------------------------------------------------------------- /demo/nextjs/app/api/auth/[...all]/route.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { auth } from "@/lib/auth"; 2 | import { toNextJsHandler } from "better-auth/next-js"; 3 | 4 | export const { GET, POST } = toNextJsHandler(auth); 5 | ``` -------------------------------------------------------------------------------- /demo/nextjs/app/dashboard/client.tsx: -------------------------------------------------------------------------------- ```typescript 1 | "use client"; 2 | 3 | export function ManageAccount() { 4 | return ( 5 | <div className="flex items-center gap-2"> 6 | <p>Manage Account</p> 7 | </div> 8 | ); 9 | } 10 | ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/src/entry-client.tsx: -------------------------------------------------------------------------------- ```typescript 1 | // @refresh reload 2 | import { mount, StartClient } from "@solidjs/start/client"; 3 | 4 | mount(() => <StartClient />, document.getElementById("app")!); 5 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/date.ts: -------------------------------------------------------------------------------- ```typescript 1 | export const getDate = (span: number, unit: "sec" | "ms" = "ms") => { 2 | return new Date(Date.now() + (unit === "sec" ? span * 1000 : span)); 3 | }; 4 | ``` -------------------------------------------------------------------------------- /packages/stripe/vitest.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | root: ".", 5 | test: { 6 | clearMocks: true, 7 | globals: true, 8 | }, 9 | }); 10 | ``` -------------------------------------------------------------------------------- /docs/app/changelogs/layout.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import type { ReactNode } from "react"; 2 | export default function Layout({ children }: { children: ReactNode }) { 3 | return <div>{children}</div>; 4 | } 5 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/tests/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "./normal"; 2 | export * from "./performance"; 3 | export * from "./transactions"; 4 | export * from "./auth-flow"; 5 | export * from "./number-id"; 6 | ``` -------------------------------------------------------------------------------- /docs/app/api/search/route.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { source } from "@/lib/source"; 2 | import { createFromSource } from "fumadocs-core/search/server"; 3 | 4 | export const { GET } = createFromSource(source); 5 | ``` -------------------------------------------------------------------------------- /demo/nextjs/components/ui/aspect-ratio.tsx: -------------------------------------------------------------------------------- ```typescript 1 | "use client"; 2 | 3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"; 4 | 5 | const AspectRatio = AspectRatioPrimitive.Root; 6 | 7 | export { AspectRatio }; 8 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/src/db.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { drizzle } from "drizzle-orm/d1"; 2 | import * as schema from "./auth-schema"; 3 | 4 | export const createDrizzle = (db: D1Database) => drizzle(db, { schema }); 5 | ``` -------------------------------------------------------------------------------- /demo/expo-example/nativewind-env.d.ts: -------------------------------------------------------------------------------- ```typescript 1 | /// <reference types="nativewind/types" /> 2 | 3 | // NOTE: This file should not be edited and should be committed with your source code. It is generated by NativeWind. 4 | ``` -------------------------------------------------------------------------------- /packages/better-auth/vitest.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | poolOptions: { 6 | forks: { 7 | execArgv: ["--expose-gc"], 8 | }, 9 | }, 10 | }, 11 | }); 12 | ``` -------------------------------------------------------------------------------- /demo/nextjs/lib/utils.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { clsx, type ClassValue } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | ``` -------------------------------------------------------------------------------- /e2e/smoke/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "smoke", 3 | "type": "module", 4 | "dependencies": { 5 | "better-auth": "workspace:*" 6 | }, 7 | "scripts": { 8 | "e2e:smoke": "node --test ./test/*.spec.ts" 9 | } 10 | } 11 | ``` -------------------------------------------------------------------------------- /demo/expo-example/babel.config.js: -------------------------------------------------------------------------------- ```javascript 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: [ 5 | ["babel-preset-expo", { jsxImportSource: "nativewind" }], 6 | "nativewind/babel", 7 | ], 8 | }; 9 | }; 10 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/crypto/random.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { createRandomStringGenerator } from "@better-auth/utils/random"; 2 | export const generateRandomString = createRandomStringGenerator( 3 | "a-z", 4 | "0-9", 5 | "A-Z", 6 | "-_", 7 | ); 8 | ``` -------------------------------------------------------------------------------- /packages/cli/tsdown.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { defineConfig } from "tsdown"; 2 | 3 | export default defineConfig({ 4 | dts: false, 5 | format: ["esm"], 6 | entry: ["./src/index.ts"], 7 | external: ["better-auth", "better-call"], 8 | }); 9 | ``` -------------------------------------------------------------------------------- /packages/telemetry/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "rootDir": "src" 6 | }, 7 | "include": ["src/**/*"], 8 | "exclude": ["node_modules", "dist"] 9 | } 10 | ``` -------------------------------------------------------------------------------- /packages/core/src/db/schema/shared.ts: -------------------------------------------------------------------------------- ```typescript 1 | import * as z from "zod"; 2 | 3 | export const coreSchema = z.object({ 4 | id: z.string(), 5 | createdAt: z.date().default(() => new Date()), 6 | updatedAt: z.date().default(() => new Date()), 7 | }); 8 | ``` -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src", 5 | "outDir": "./dist", 6 | "lib": ["esnext", "dom", "dom.iterable"] 7 | }, 8 | "include": ["src"] 9 | } 10 | ``` -------------------------------------------------------------------------------- /packages/expo/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src", 5 | "outDir": "./dist", 6 | "lib": ["esnext", "dom", "dom.iterable"] 7 | }, 8 | "include": ["src"] 9 | } 10 | ``` -------------------------------------------------------------------------------- /packages/sso/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src", 5 | "outDir": "./dist", 6 | "lib": ["esnext", "dom", "dom.iterable"] 7 | }, 8 | "include": ["src"] 9 | } 10 | ``` -------------------------------------------------------------------------------- /packages/stripe/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src", 5 | "outDir": "./dist", 6 | "lib": ["esnext", "dom", "dom.iterable"] 7 | }, 8 | "include": ["src"] 9 | } 10 | ``` -------------------------------------------------------------------------------- /docs/app/static.json/route.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { exportSearchIndexes } from "@/lib/export-search-indexes"; 2 | 3 | export const revalidate = false; 4 | 5 | export async function GET() { 6 | return Response.json(await exportSearchIndexes()); 7 | } 8 | ``` -------------------------------------------------------------------------------- /bump.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { defineConfig } from "bumpp"; 2 | import { globSync } from "tinyglobby"; 3 | 4 | export default defineConfig({ 5 | files: globSync(["./packages/*/package.json"], { expandDirectories: false }), 6 | }); 7 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/kysely-adapter/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "./dialect"; 2 | export * from "./types"; 3 | export * from "./kysely-adapter"; 4 | // Don't export node:sqlite by default, as it is not production ready. 5 | // export * from "./node-sqlite"; 6 | ``` -------------------------------------------------------------------------------- /demo/expo-example/src/app/api/auth/[...route]+api.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { auth } from "@/lib/auth"; 2 | 3 | export const GET = (request: Request) => { 4 | return auth.handler(request); 5 | }; 6 | 7 | export const POST = (request: Request) => { 8 | return auth.handler(request); 9 | }; 10 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/vite/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "vite", 3 | "private": true, 4 | "scripts": { 5 | "build": "vite build" 6 | }, 7 | "dependencies": { 8 | "better-auth": "workspace:*" 9 | }, 10 | "devDependencies": { 11 | "vite": "^7.1.5" 12 | } 13 | } 14 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/id.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { createRandomStringGenerator } from "@better-auth/utils/random"; 2 | 3 | export const generateId = (size?: number) => { 4 | return createRandomStringGenerator("a-z", "A-Z", "0-9")(size || 32); 5 | }; 6 | ``` -------------------------------------------------------------------------------- /packages/telemetry/src/utils/id.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { createRandomStringGenerator } from "@better-auth/utils/random"; 2 | 3 | export const generateId = (size?: number) => { 4 | return createRandomStringGenerator("a-z", "A-Z", "0-9")(size || 32); 5 | }; 6 | ``` -------------------------------------------------------------------------------- /e2e/integration/test-utils/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "@better-auth/test-utils", 3 | "type": "module", 4 | "exports": { 5 | "./playwright": "./src/playwright.ts" 6 | }, 7 | "private": true, 8 | "devDependencies": { 9 | "terminate": "^2.8.0" 10 | } 11 | } 12 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/captcha/verify-handlers/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export { cloudflareTurnstile } from "./cloudflare-turnstile"; 2 | export { googleRecaptcha } from "./google-recaptcha"; 3 | export { hCaptcha } from "./h-captcha"; 4 | export { captchaFox } from "./captchafox"; 5 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/tsconfig-verbatim-module-syntax-node10/src/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { betterAuth } from "better-auth"; 2 | import Database from "better-sqlite3"; 3 | import { expo } from "@better-auth/expo"; 4 | 5 | betterAuth({ 6 | database: new Database("./sqlite.db"), 7 | plugins: [expo()], 8 | }); 9 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/drizzle/meta/_journal.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "version": "7", 3 | "dialect": "sqlite", 4 | "entries": [ 5 | { 6 | "idx": 0, 7 | "version": "6", 8 | "when": 1740122602683, 9 | "tag": "0000_clean_vector", 10 | "breakpoints": true 11 | } 12 | ] 13 | } 14 | ``` -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src", 5 | "outDir": "./dist", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "types": ["node"] 8 | }, 9 | "include": ["src"] 10 | } 11 | ``` -------------------------------------------------------------------------------- /docs/lib/constants.ts: -------------------------------------------------------------------------------- ```typescript 1 | export const ENV = { 2 | NEXT_PUBLIC_WEBSITE_URL: 3 | process.env.NEXT_PUBLIC_WEBSITE_URL || "http://localhost:3000", 4 | NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION: "", 5 | NODE_ENV: process.env.NODE_ENV || "development", 6 | }; 7 | ``` -------------------------------------------------------------------------------- /e2e/integration/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "integration", 3 | "scripts": { 4 | "e2e:integration": "playwright test" 5 | }, 6 | "dependencies": { 7 | "better-auth": "workspace:*" 8 | }, 9 | "devDependencies": { 10 | "@playwright/test": "^1.55.0" 11 | } 12 | } 13 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/is-promise.ts: -------------------------------------------------------------------------------- ```typescript 1 | export function isPromise(obj?: unknown): obj is Promise<unknown> { 2 | return ( 3 | !!obj && 4 | (typeof obj === "object" || typeof obj === "function") && 5 | typeof (obj as Promise<unknown>).then === "function" 6 | ); 7 | } 8 | ``` -------------------------------------------------------------------------------- /packages/better-auth/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src", 5 | "outDir": "./dist", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "types": ["node", "bun"] 8 | }, 9 | "include": ["src"] 10 | } 11 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/middleware-response.ts: -------------------------------------------------------------------------------- ```typescript 1 | type Params = { 2 | message: string; 3 | status: number; 4 | }; 5 | 6 | export const middlewareResponse = ({ message, status }: Params) => ({ 7 | response: new Response( 8 | JSON.stringify({ 9 | message, 10 | }), 11 | { 12 | status, 13 | }, 14 | ), 15 | }); 16 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/test/env.d.ts: -------------------------------------------------------------------------------- ```typescript 1 | declare module "cloudflare:test" { 2 | // Controls the type of `import("cloudflare:test").env` 3 | interface ProvidedEnv extends Env { 4 | TEST_MIGRATIONS: D1Migration[]; // Defined in `vitest.config.mts` 5 | DB: D1Database; 6 | } 7 | } 8 | ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/src/routes/index.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { createEffect } from "solid-js"; 2 | import { client } from "../lib/auth-client"; 3 | 4 | export default function Home() { 5 | createEffect(() => { 6 | window.client = client; 7 | }); 8 | return ( 9 | <main> 10 | <div>Ready</div> 11 | </main> 12 | ); 13 | } 14 | ``` -------------------------------------------------------------------------------- /packages/core/src/db/plugin.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { DBFieldAttribute } from "./type"; 2 | 3 | export type BetterAuthPluginDBSchema = { 4 | [table in string]: { 5 | fields: { 6 | [field in string]: DBFieldAttribute; 7 | }; 8 | disableMigration?: boolean; 9 | modelName?: string; 10 | }; 11 | }; 12 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/vite/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "bundler", 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "skipLibCheck": true 10 | } 11 | } 12 | ``` -------------------------------------------------------------------------------- /packages/sso/tsdown.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { defineConfig } from "tsdown"; 2 | 3 | export default defineConfig({ 4 | dts: true, 5 | format: ["esm", "cjs"], 6 | entry: ["./src/index.ts", "./src/client.ts"], 7 | external: ["better-auth", "better-call", "@better-fetch/fetch", "stripe"], 8 | }); 9 | ``` -------------------------------------------------------------------------------- /packages/stripe/tsdown.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { defineConfig } from "tsdown"; 2 | 3 | export default defineConfig({ 4 | dts: true, 5 | format: ["esm", "cjs"], 6 | entry: ["./src/index.ts", "./src/client.ts"], 7 | external: ["better-auth", "better-call", "@better-fetch/fetch", "stripe"], 8 | }); 9 | ``` -------------------------------------------------------------------------------- /docs/components/builder/store.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { atom } from "jotai"; 2 | 3 | export const optionsAtom = atom({ 4 | email: true, 5 | passkey: false, 6 | socialProviders: ["google", "github"], 7 | magicLink: false, 8 | signUp: true, 9 | label: true, 10 | rememberMe: true, 11 | requestPasswordReset: true, 12 | }); 13 | ``` -------------------------------------------------------------------------------- /demo/nextjs/next.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | typescript: { 5 | ignoreBuildErrors: true, 6 | }, 7 | webpack: (config) => { 8 | config.externals.push("@libsql/client"); 9 | return config; 10 | }, 11 | }; 12 | 13 | export default nextConfig; 14 | ``` -------------------------------------------------------------------------------- /docs/ignore-build.sh: -------------------------------------------------------------------------------- ```bash 1 | #!/bin/bash 2 | 3 | # Check if commit message starts with release or docs prefix 4 | if [[ ! "$message" == "chore(release): version packages"* ]] && [[ ! "$message" == "docs"* ]]; then 5 | echo "Skipping build: Not a release or docs commit." 6 | exit 1 7 | fi ``` -------------------------------------------------------------------------------- /packages/sso/src/client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { BetterAuthClientPlugin } from "better-auth"; 2 | import { sso } from "./index"; 3 | export const ssoClient = () => { 4 | return { 5 | id: "sso-client", 6 | $InferServerPlugin: {} as ReturnType<typeof sso>, 7 | } satisfies BetterAuthClientPlugin; 8 | }; 9 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/siwe/client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { siwe } from "."; 2 | import type { BetterAuthClientPlugin } from "@better-auth/core"; 3 | 4 | export const siweClient = () => { 5 | return { 6 | id: "siwe", 7 | $InferServerPlugin: {} as ReturnType<typeof siwe>, 8 | } satisfies BetterAuthClientPlugin; 9 | }; 10 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/sso/client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { sso } from "."; 2 | import type { BetterAuthClientPlugin } from "@better-auth/core"; 3 | 4 | export const ssoClient = () => { 5 | return { 6 | id: "sso-client", 7 | $InferServerPlugin: {} as ReturnType<typeof sso>, 8 | } satisfies BetterAuthClientPlugin; 9 | }; 10 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/tsconfig-exact-optional-property-types/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "tsconfig-exact-optional-property-types", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "typecheck": "tsc --project tsconfig.json" 7 | }, 8 | "dependencies": { 9 | "better-auth": "workspace:*", 10 | "better-sqlite3": "^12.4.1" 11 | } 12 | } 13 | ``` -------------------------------------------------------------------------------- /packages/cli/src/utils/get-package-info.ts: -------------------------------------------------------------------------------- ```typescript 1 | import path from "path"; 2 | import fs from "fs"; 3 | 4 | export function getPackageInfo(cwd?: string) { 5 | const packageJsonPath = cwd 6 | ? path.join(cwd, "package.json") 7 | : path.join("package.json"); 8 | return JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")); 9 | } 10 | ``` -------------------------------------------------------------------------------- /demo/nextjs/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { cn } from "@/lib/utils"; 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes<HTMLDivElement>) { 7 | return ( 8 | <div 9 | className={cn("animate-pulse rounded-md bg-primary/10", className)} 10 | {...props} 11 | /> 12 | ); 13 | } 14 | 15 | export { Skeleton }; 16 | ``` -------------------------------------------------------------------------------- /docs/components/ui/fade-in.tsx: -------------------------------------------------------------------------------- ```typescript 1 | "use client"; 2 | 3 | import { AnimatePresence as PrimitiveAnimatePresence } from "framer-motion"; 4 | 5 | export const AnimatePresence = ( 6 | props: React.ComponentPropsWithoutRef<typeof PrimitiveAnimatePresence>, 7 | ) => { 8 | return <PrimitiveAnimatePresence {...props} />; 9 | }; 10 | ``` -------------------------------------------------------------------------------- /docs/content/docs/meta.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "title": "guide", 3 | "root": true, 4 | "pages": [ 5 | "introduction", 6 | "installation", 7 | "basic-usage", 8 | "email-password/sign-in-and-sign-up", 9 | "email-password/password-reset", 10 | "email-password/configuration", 11 | "social-sign-on/apple" 12 | ] 13 | } 14 | ``` -------------------------------------------------------------------------------- /demo/nextjs/lib/auth-types.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { auth } from "./auth"; 2 | import { client } from "./auth-client"; 3 | 4 | export type Session = typeof auth.$Infer.Session; 5 | export type ActiveOrganization = typeof client.$Infer.ActiveOrganization; 6 | export type Invitation = typeof client.$Infer.Invitation; 7 | ``` -------------------------------------------------------------------------------- /packages/core/src/error/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export class BetterAuthError extends Error { 2 | constructor(message: string, cause?: string) { 3 | super(message); 4 | this.name = "BetterAuthError"; 5 | this.message = message; 6 | this.cause = cause; 7 | this.stack = ""; 8 | } 9 | } 10 | 11 | export { BASE_ERROR_CODES } from "./codes"; 12 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/ensure-utc.ts: -------------------------------------------------------------------------------- ```typescript 1 | export function ensureUTC(date: Date): Date { 2 | const utcTimestamp = Date.UTC( 3 | date.getFullYear(), 4 | date.getMonth(), 5 | date.getDate(), 6 | date.getHours(), 7 | date.getMinutes(), 8 | date.getSeconds(), 9 | date.getMilliseconds(), 10 | ); 11 | 12 | return new Date(utcTimestamp); 13 | } 14 | ``` -------------------------------------------------------------------------------- /packages/core/src/types/cookie.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { CookieOptions } from "better-call"; 2 | 3 | export type BetterAuthCookies = { 4 | sessionToken: { name: string; options: CookieOptions }; 5 | sessionData: { name: string; options: CookieOptions }; 6 | dontRememberToken: { name: string; options: CookieOptions }; 7 | }; 8 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/username/client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { username } from "."; 2 | import type { BetterAuthClientPlugin } from "@better-auth/core"; 3 | 4 | export const usernameClient = () => { 5 | return { 6 | id: "username", 7 | $InferServerPlugin: {} as ReturnType<typeof username>, 8 | } satisfies BetterAuthClientPlugin; 9 | }; 10 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/jwt/client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { jwt } from "./index"; 2 | import type { BetterAuthClientPlugin } from "@better-auth/core"; 3 | 4 | export const jwtClient = () => { 5 | return { 6 | id: "better-auth-client", 7 | $InferServerPlugin: {} as ReturnType<typeof jwt>, 8 | } satisfies BetterAuthClientPlugin; 9 | }; 10 | ``` -------------------------------------------------------------------------------- /demo/nextjs/components/theme-provider.tsx: -------------------------------------------------------------------------------- ```typescript 1 | "use client"; 2 | import { ThemeProvider as NextThemesProvider } from "next-themes"; 3 | 4 | export function ThemeProvider({ 5 | children, 6 | ...props 7 | }: { 8 | children: React.ReactNode; 9 | [key: string]: any; 10 | }) { 11 | return <NextThemesProvider {...props}>{children}</NextThemesProvider>; 12 | } 13 | ``` -------------------------------------------------------------------------------- /docs/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { cn } from "@/lib/utils"; 2 | 3 | function Skeleton({ className, ...props }: React.ComponentProps<"div">) { 4 | return ( 5 | <div 6 | data-slot="skeleton" 7 | className={cn("bg-primary/10 animate-pulse rounded-md", className)} 8 | {...props} 9 | /> 10 | ); 11 | } 12 | 13 | export { Skeleton }; 14 | ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/src/routes/api/auth/[...all].ts: -------------------------------------------------------------------------------- ```typescript 1 | import { auth } from "../../../lib/auth"; 2 | import type { APIEvent } from "@solidjs/start/server"; 3 | 4 | export async function GET(event: APIEvent) { 5 | return auth.handler(event.request); 6 | } 7 | 8 | export async function POST(event: APIEvent) { 9 | return auth.handler(event.request); 10 | } 11 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/vite/src/client.ts: -------------------------------------------------------------------------------- ```typescript 1 | /// <reference types="vite/client" /> 2 | export * from "better-auth/client/plugins"; 3 | import { createAuthClient } from "better-auth/client"; 4 | 5 | export const authClient = createAuthClient({ 6 | baseURL: import.meta.env.VITE_BASE_URL || "http://localhost:3000", 7 | plugins: [], 8 | }); 9 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/magic-link/client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { magicLink } from "."; 2 | import type { BetterAuthClientPlugin } from "@better-auth/core"; 3 | 4 | export const magicLinkClient = () => { 5 | return { 6 | id: "magic-link", 7 | $InferServerPlugin: {} as ReturnType<typeof magicLink>, 8 | } satisfies BetterAuthClientPlugin; 9 | }; 10 | ``` -------------------------------------------------------------------------------- /packages/core/src/types/helper.ts: -------------------------------------------------------------------------------- ```typescript 1 | type Primitive = string | number | symbol | bigint | boolean | null | undefined; 2 | 3 | export type LiteralString = "" | (string & Record<never, never>); 4 | export type LiteralUnion<LiteralType, BaseType extends Primitive> = 5 | | LiteralType 6 | | (BaseType & Record<never, never>); 7 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/oidc-provider/client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { oidcProvider } from "."; 2 | import type { BetterAuthClientPlugin } from "@better-auth/core"; 3 | 4 | export const oidcClient = () => { 5 | return { 6 | id: "oidc-client", 7 | $InferServerPlugin: {} as ReturnType<typeof oidcProvider>, 8 | } satisfies BetterAuthClientPlugin; 9 | }; 10 | ``` -------------------------------------------------------------------------------- /docs/lib/is-active.ts: -------------------------------------------------------------------------------- ```typescript 1 | export function isActive( 2 | url: string, 3 | pathname: string, 4 | nested = true, 5 | ): boolean { 6 | if (url.endsWith("/")) url = url.slice(0, -1); 7 | if (pathname.endsWith("/")) pathname = pathname.slice(0, -1); 8 | 9 | return url === pathname || (nested && pathname.startsWith(`${url}/`)); 10 | } 11 | ``` -------------------------------------------------------------------------------- /packages/cli/src/generators/types.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { Adapter, BetterAuthOptions } from "better-auth"; 2 | 3 | export interface SchemaGenerator { 4 | (opts: { 5 | file?: string; 6 | adapter: Adapter; 7 | options: BetterAuthOptions; 8 | }): Promise<{ 9 | code?: string; 10 | fileName: string; 11 | overwrite?: boolean; 12 | append?: boolean; 13 | }>; 14 | } 15 | ``` -------------------------------------------------------------------------------- /docs/components/ui/aspect-ratio.tsx: -------------------------------------------------------------------------------- ```typescript 1 | "use client"; 2 | 3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"; 4 | 5 | function AspectRatio({ 6 | ...props 7 | }: React.ComponentProps<typeof AspectRatioPrimitive.Root>) { 8 | return <AspectRatioPrimitive.Root data-slot="aspect-ratio" {...props} />; 9 | } 10 | 11 | export { AspectRatio }; 12 | ``` -------------------------------------------------------------------------------- /demo/expo-example/src/lib/icons/iconWithClassName.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { LucideIcon } from "lucide-react-native"; 2 | import { cssInterop } from "nativewind"; 3 | 4 | export function iconWithClassName(icon: LucideIcon) { 5 | cssInterop(icon, { 6 | className: { 7 | target: "style", 8 | nativeStyleToProp: { 9 | color: true, 10 | opacity: true, 11 | }, 12 | }, 13 | }); 14 | } 15 | ``` -------------------------------------------------------------------------------- /packages/telemetry/src/utils/hash.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { createHash } from "@better-auth/utils/hash"; 2 | import { base64 } from "@better-auth/utils/base64"; 3 | 4 | export async function hashToBase64( 5 | data: string | ArrayBuffer, 6 | ): Promise<string> { 7 | const buffer = await createHash("SHA-256").digest(data); 8 | return base64.encode(buffer); 9 | } 10 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/generic-oauth/client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { genericOAuth } from "."; 2 | import type { BetterAuthClientPlugin } from "@better-auth/core"; 3 | 4 | export const genericOAuthClient = () => { 5 | return { 6 | id: "generic-oauth-client", 7 | $InferServerPlugin: {} as ReturnType<typeof genericOAuth>, 8 | } satisfies BetterAuthClientPlugin; 9 | }; 10 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/one-time-token/client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { oneTimeToken } from "./index"; 2 | import type { BetterAuthClientPlugin } from "@better-auth/core"; 3 | 4 | export const oneTimeTokenClient = () => { 5 | return { 6 | id: "one-time-token", 7 | $InferServerPlugin: {} as ReturnType<typeof oneTimeToken>, 8 | } satisfies BetterAuthClientPlugin; 9 | }; 10 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/tsconfig-verbatim-module-syntax-node10/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "tsconfig-verbatim-module-syntax-node10", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "typecheck": "tsc --project tsconfig.json" 7 | }, 8 | "dependencies": { 9 | "@better-auth/expo": "workspace:^", 10 | "better-auth": "workspace:^", 11 | "better-sqlite3": "^12.4.1" 12 | } 13 | } 14 | ``` -------------------------------------------------------------------------------- /e2e/integration/vanilla-node/index.html: -------------------------------------------------------------------------------- ```html 1 | <!doctype html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="UTF-8" /> 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 | <title>Better Auth - Vanilla Node E2E Test</title> 7 | </head> 8 | <body> 9 | <script type="module" src="./src/main.ts"></script> 10 | </body> 11 | </html> ``` -------------------------------------------------------------------------------- /docs/components/theme-provider.tsx: -------------------------------------------------------------------------------- ```typescript 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import { ThemeProvider as NextThemesProvider } from "next-themes"; 5 | 6 | export function ThemeProvider({ 7 | children, 8 | ...props 9 | }: React.ComponentProps<typeof NextThemesProvider>) { 10 | return <NextThemesProvider {...props}>{children}</NextThemesProvider>; 11 | } 12 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/tsconfig-verbatim-module-syntax-node10/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "NodeNext", 5 | "outDir": "dist", 6 | "strict": true, 7 | "verbatimModuleSyntax": true, 8 | "skipLibCheck": true, 9 | "types": ["node"], 10 | "jsx": "react-jsx", 11 | "jsxImportSource": "hono/jsx", 12 | "moduleResolution": "node16" 13 | } 14 | } 15 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/is-atom.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { Atom } from "nanostores"; 2 | 3 | export function isAtom(value: unknown): value is Atom<unknown> { 4 | return ( 5 | typeof value === "object" && 6 | value !== null && 7 | "get" in value && 8 | typeof (value as any).get === "function" && 9 | "lc" in value && 10 | typeof (value as any).lc === "number" 11 | ); 12 | } 13 | ``` -------------------------------------------------------------------------------- /e2e/smoke/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "target": "esnext", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "moduleResolution": "node16", 10 | "module": "Node16" 11 | }, 12 | "include": ["./test/**/*"] 13 | } 14 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/db/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "@better-auth/core/db"; 2 | export * from "./internal-adapter"; 3 | export * from "./field"; 4 | export * from "./get-tables"; 5 | export * from "./with-hooks"; 6 | export * from "./to-zod"; 7 | export * from "./utils"; 8 | export * from "./get-migration"; 9 | export * from "./get-schema"; 10 | export * from "./schema"; 11 | ``` -------------------------------------------------------------------------------- /docs/turbo.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "$schema": "https://turborepo.org/schema.json", 3 | "extends": ["//"], 4 | "tasks": { 5 | "build": { 6 | "env": [ 7 | "GITHUB_TOKEN", 8 | "ORAMA_PRIVATE_API_KEY", 9 | "ORAMA_INDEX_ID", 10 | "NEXT_PUBLIC_ORAMA_PUBLIC_API_KEY", 11 | "NEXT_PUBLIC_ORAMA_ENDPOINT" 12 | ] 13 | } 14 | } 15 | } 16 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/tsconfig-exact-optional-property-types/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "NodeNext", 5 | "outDir": "dist", 6 | "strict": true, 7 | "exactOptionalPropertyTypes": true, 8 | "skipLibCheck": true, 9 | "types": ["node"], 10 | "jsx": "react-jsx", 11 | "jsxImportSource": "hono/jsx", 12 | "moduleResolution": "node16" 13 | } 14 | } 15 | ``` -------------------------------------------------------------------------------- /e2e/integration/test-utils/src/playwright.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { createRequire } from "node:module"; 2 | 3 | export const terminate = createRequire(import.meta.url)( 4 | // use terminate instead of cp.kill, 5 | // because cp.kill will not kill the child process of the child process 6 | // to avoid the zombie process 7 | "terminate/promise", 8 | ) as (pid: number) => Promise<void>; 9 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/api/routes/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export * from "./sign-in"; 2 | export * from "./callback"; 3 | export * from "./session"; 4 | export * from "./sign-out"; 5 | export * from "./reset-password"; 6 | export * from "./email-verification"; 7 | export * from "./update-user"; 8 | export * from "./error"; 9 | export * from "./ok"; 10 | export * from "./sign-up"; 11 | export * from "./account"; 12 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/custom-session/client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { InferServerPlugin } from "../../client/plugins"; 2 | import type { BetterAuthOptions } from "../../types"; 3 | import type { Auth } from "better-auth"; 4 | 5 | export const customSessionClient = < 6 | A extends 7 | | Auth 8 | | { 9 | options: BetterAuthOptions; 10 | }, 11 | >() => { 12 | return InferServerPlugin<A, "custom-session">(); 13 | }; 14 | ``` -------------------------------------------------------------------------------- /packages/cli/src/utils/format-ms.ts: -------------------------------------------------------------------------------- ```typescript 1 | /** 2 | * Only supports up to seconds. 3 | */ 4 | export function formatMilliseconds(ms: number) { 5 | if (ms < 0) { 6 | throw new Error("Milliseconds cannot be negative"); 7 | } 8 | if (ms < 1000) { 9 | return `${ms}ms`; 10 | } 11 | 12 | const seconds = Math.floor(ms / 1000); 13 | const milliseconds = ms % 1000; 14 | 15 | return `${seconds}s ${milliseconds}ms`; 16 | } 17 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/anonymous/client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { anonymous } from "."; 2 | import type { BetterAuthClientPlugin } from "@better-auth/core"; 3 | 4 | export const anonymousClient = () => { 5 | return { 6 | id: "anonymous", 7 | $InferServerPlugin: {} as ReturnType<typeof anonymous>, 8 | pathMethods: { 9 | "/sign-in/anonymous": "POST", 10 | }, 11 | } satisfies BetterAuthClientPlugin; 12 | }; 13 | ``` -------------------------------------------------------------------------------- /packages/expo/tsdown.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { defineConfig } from "tsdown"; 2 | 3 | export default defineConfig({ 4 | dts: true, 5 | format: ["esm", "cjs"], 6 | entry: ["./src/index.ts", "./src/client.ts"], 7 | external: [ 8 | "better-auth", 9 | "better-call", 10 | "@better-fetch/fetch", 11 | "react-native", 12 | "expo-web-browser", 13 | "expo-linking", 14 | "expo-constants", 15 | ], 16 | treeshake: true, 17 | }); 18 | ``` -------------------------------------------------------------------------------- /demo/nextjs/components/ui/collapsible.tsx: -------------------------------------------------------------------------------- ```typescript 1 | "use client"; 2 | 3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"; 4 | 5 | const Collapsible = CollapsiblePrimitive.Root; 6 | 7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger; 8 | 9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent; 10 | 11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }; 12 | ``` -------------------------------------------------------------------------------- /packages/cli/tsconfig.test.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "./node_modules/.cache/test", 6 | "tsBuildInfoFile": "tsconfig.test.tsbuildinfo", 7 | "lib": ["esnext", "dom", "dom.iterable"] 8 | }, 9 | "references": [ 10 | { 11 | "path": "../better-auth/tsconfig.json" 12 | } 13 | ], 14 | "include": ["test", "src"] 15 | } 16 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/types/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export type { 2 | BetterAuthOptions, 3 | BetterAuthPlugin, 4 | BetterAuthRateLimitOptions, 5 | BetterAuthAdvancedOptions, 6 | BetterAuthCookies, 7 | } from "@better-auth/core"; 8 | export type * from "./models"; 9 | export type * from "../init"; 10 | export type * from "./plugins"; 11 | export type * from "./adapter"; 12 | export * from "../client/types"; 13 | export * from "./api"; 14 | ``` -------------------------------------------------------------------------------- /demo/nextjs/app/(auth)/sign-in/loading.tsx: -------------------------------------------------------------------------------- ```typescript 1 | export default function SignInLoading() { 2 | return ( 3 | <div className="flex min-h-screen items-center justify-center"> 4 | <div className="text-center"> 5 | <div className="h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-gray-900 mx-auto"></div> 6 | <p className="mt-4 text-gray-600">Loading...</p> 7 | </div> 8 | </div> 9 | ); 10 | } 11 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/jwt/schema.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { BetterAuthPluginDBSchema } from "@better-auth/core/db"; 2 | 3 | export const schema = { 4 | jwks: { 5 | fields: { 6 | publicKey: { 7 | type: "string", 8 | required: true, 9 | }, 10 | privateKey: { 11 | type: "string", 12 | required: true, 13 | }, 14 | createdAt: { 15 | type: "date", 16 | required: true, 17 | }, 18 | }, 19 | }, 20 | } satisfies BetterAuthPluginDBSchema; 21 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/magic-link/utils.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { createHash } from "@better-auth/utils/hash"; 2 | import { base64Url } from "@better-auth/utils/base64"; 3 | 4 | export const defaultKeyHasher = async (otp: string) => { 5 | const hash = await createHash("SHA-256").digest( 6 | new TextEncoder().encode(otp), 7 | ); 8 | const hashed = base64Url.encode(new Uint8Array(hash), { 9 | padding: false, 10 | }); 11 | return hashed; 12 | }; 13 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/one-time-token/utils.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { createHash } from "@better-auth/utils/hash"; 2 | import { base64Url } from "@better-auth/utils/base64"; 3 | export const defaultKeyHasher = async (token: string) => { 4 | const hash = await createHash("SHA-256").digest( 5 | new TextEncoder().encode(token), 6 | ); 7 | const hashed = base64Url.encode(new Uint8Array(hash), { 8 | padding: false, 9 | }); 10 | return hashed; 11 | }; 12 | ``` -------------------------------------------------------------------------------- /packages/core/src/env/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export { 2 | ENV, 3 | getEnvVar, 4 | env, 5 | nodeENV, 6 | isTest, 7 | getBooleanEnvVar, 8 | isDevelopment, 9 | type EnvObject, 10 | isProduction, 11 | } from "./env-impl"; 12 | export { getColorDepth } from "./color-depth"; 13 | export { 14 | logger, 15 | createLogger, 16 | levels, 17 | type Logger, 18 | type LogLevel, 19 | type LogHandlerParams, 20 | type InternalLogger, 21 | shouldPublishLog, 22 | TTY_COLORS, 23 | } from "./logger"; 24 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/two-factor/utils.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { createHash } from "@better-auth/utils/hash"; 2 | import { base64Url } from "@better-auth/utils/base64"; 3 | 4 | export const defaultKeyHasher = async (token: string) => { 5 | const hash = await createHash("SHA-256").digest( 6 | new TextEncoder().encode(token), 7 | ); 8 | const hashed = base64Url.encode(new Uint8Array(hash), { 9 | padding: false, 10 | }); 11 | return hashed; 12 | }; 13 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/integrations/solid-start.ts: -------------------------------------------------------------------------------- ```typescript 1 | export function toSolidStartHandler( 2 | auth: 3 | | { 4 | handler: (request: Request) => Promise<Response>; 5 | } 6 | | ((request: Request) => Promise<Response>), 7 | ) { 8 | const handler = async (event: { request: Request }) => { 9 | return "handler" in auth 10 | ? auth.handler(event.request) 11 | : auth(event.request); 12 | }; 13 | return { 14 | GET: handler, 15 | POST: handler, 16 | }; 17 | } 18 | ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/src/app.tsx: -------------------------------------------------------------------------------- ```typescript 1 | // @refresh reload 2 | import { type RouteDefinition, Router } from "@solidjs/router"; 3 | import { FileRoutes } from "@solidjs/start/router"; 4 | import { Suspense } from "solid-js"; 5 | 6 | export const route: RouteDefinition = {}; 7 | 8 | export default function App() { 9 | return ( 10 | <Router root={(props) => <Suspense>{props.children}</Suspense>}> 11 | <FileRoutes /> 12 | </Router> 13 | ); 14 | } 15 | ``` -------------------------------------------------------------------------------- /e2e/integration/vanilla-node/src/main.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { createAuthClient } from "better-auth/client"; 2 | 3 | const search = new URLSearchParams(window.location.search); 4 | const port = search.get("port"); 5 | 6 | const client = createAuthClient({ 7 | baseURL: `http://localhost:${port ?? 3000}`, 8 | }); 9 | 10 | declare global { 11 | interface Window { 12 | client: typeof client; 13 | } 14 | } 15 | 16 | window.client = client; 17 | 18 | document.body.innerHTML = "Ready"; 19 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/crypto/jwt.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { SignJWT } from "jose"; 2 | 3 | export async function signJWT( 4 | payload: any, 5 | secret: string, 6 | expiresIn: number = 3600, 7 | ): Promise<string> { 8 | const jwt = await new SignJWT(payload) 9 | .setProtectedHeader({ alg: "HS256" }) 10 | .setIssuedAt() 11 | .setExpirationTime(Math.floor(Date.now() / 1000) + expiresIn) 12 | .sign(new TextEncoder().encode(secret)); 13 | 14 | return jwt; 15 | } 16 | ``` -------------------------------------------------------------------------------- /demo/expo-example/src/lib/auth-client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { createAuthClient } from "better-auth/client"; 2 | import { expoClient } from "@better-auth/expo/client"; 3 | import * as SecureStore from "expo-secure-store"; 4 | 5 | export const authClient = createAuthClient({ 6 | baseURL: "http://localhost:8081", 7 | disableDefaultFetchPlugins: true, 8 | plugins: [ 9 | expoClient({ 10 | scheme: "better-auth", 11 | storage: SecureStore, 12 | }), 13 | ], 14 | }); 15 | ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "allowSyntheticDefaultImports": true, 7 | "esModuleInterop": true, 8 | "jsx": "preserve", 9 | "jsxImportSource": "solid-js", 10 | "allowJs": true, 11 | "strict": true, 12 | "noEmit": true, 13 | "types": ["vinxi/types/client"], 14 | "isolatedModules": true 15 | } 16 | } 17 | ``` -------------------------------------------------------------------------------- /demo/nextjs/middleware.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { NextRequest, NextResponse } from "next/server"; 2 | import { getSessionCookie } from "better-auth/cookies"; 3 | 4 | export async function middleware(request: NextRequest) { 5 | const cookies = getSessionCookie(request); 6 | if (!cookies) { 7 | return NextResponse.redirect(new URL("/", request.url)); 8 | } 9 | return NextResponse.next(); 10 | } 11 | 12 | export const config = { 13 | matcher: ["/dashboard"], 14 | }; 15 | ``` -------------------------------------------------------------------------------- /docs/components/banner.tsx: -------------------------------------------------------------------------------- ```typescript 1 | "use client"; 2 | 3 | import { Banner } from "fumadocs-ui/components/banner"; 4 | 5 | export function BetaNotice() { 6 | return ( 7 | <Banner 8 | id="beta-notice-1" 9 | className="lg:text-sm tracking-tight text-xs hidden md:flex bg-gradient-to-tr from-white to-stone-100 border dark:from-zinc-900 dark:to-zinc-950" 10 | > 11 | 🚧 Heads up! We're still in beta. V1 will be out by nov. 22! 12 | </Banner> 13 | ); 14 | } 15 | ``` -------------------------------------------------------------------------------- /docs/app/docs/layout.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { DocsLayout } from "@/components/docs/docs"; 2 | import type { ReactNode } from "react"; 3 | import { docsOptions } from "../layout.config"; 4 | import { AISearchTrigger } from "@/components/floating-ai-search"; 5 | export default function Layout({ children }: { children: ReactNode }) { 6 | return ( 7 | <DocsLayout {...docsOptions}> 8 | {children} 9 | <AISearchTrigger /> 10 | </DocsLayout> 11 | ); 12 | } 13 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/wrangler.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "$schema": "node_modules/wrangler/config-schema.json", 3 | "compatibility_date": "2025-04-01", 4 | "main": "src/index.ts", 5 | "name": "cloudflare", 6 | "upload_source_maps": true, 7 | "d1_databases": [ 8 | { 9 | "binding": "DB", 10 | "database_id": "d1-db-id", 11 | "database_name": "db", 12 | "migrations_dir": "./drizzle" 13 | } 14 | ], 15 | "observability": { 16 | "enabled": true 17 | } 18 | } 19 | ``` -------------------------------------------------------------------------------- /demo/nextjs/app/device/layout.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { auth } from "@/lib/auth"; 2 | import { headers } from "next/headers"; 3 | import { redirect } from "next/navigation"; 4 | 5 | export default async function DevicePage({ 6 | children, 7 | }: { 8 | children: React.ReactNode; 9 | }) { 10 | const session = await auth.api.getSession({ 11 | headers: await headers(), 12 | }); 13 | if (session === null) { 14 | throw redirect("/sign-in?callbackUrl=/device"); 15 | } 16 | return children; 17 | } 18 | ``` -------------------------------------------------------------------------------- /docs/lib/export-search-indexes.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { source } from "@/lib/source"; 2 | import type { OramaDocument } from "fumadocs-core/search/orama-cloud"; 3 | 4 | export async function exportSearchIndexes() { 5 | return source.getPages().map((page) => { 6 | return { 7 | id: page.url, 8 | structured: page.data.structuredData, 9 | url: page.url, 10 | title: page.data.title, 11 | description: page.data.description, 12 | } satisfies OramaDocument; 13 | }); 14 | } 15 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/email-otp/client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { emailOTP } from "."; 2 | import type { BetterAuthClientPlugin } from "@better-auth/core"; 3 | 4 | export const emailOTPClient = () => { 5 | return { 6 | id: "email-otp", 7 | $InferServerPlugin: {} as ReturnType<typeof emailOTP>, 8 | atomListeners: [ 9 | { 10 | matcher: (path) => path === "/email-otp/verify-email", 11 | signal: "$sessionSignal", 12 | }, 13 | ], 14 | } satisfies BetterAuthClientPlugin; 15 | }; 16 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/api-key/client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { apiKey } from "."; 2 | import type { BetterAuthClientPlugin } from "@better-auth/core"; 3 | 4 | export const apiKeyClient = () => { 5 | return { 6 | id: "api-key", 7 | $InferServerPlugin: {} as ReturnType<typeof apiKey>, 8 | pathMethods: { 9 | "/api-key/create": "POST", 10 | "/api-key/delete": "POST", 11 | "/api-key/delete-all-expired-api-keys": "POST", 12 | }, 13 | } satisfies BetterAuthClientPlugin; 14 | }; 15 | ``` -------------------------------------------------------------------------------- /e2e/integration/vanilla-node/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "vanilla-node-e2e", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "start:client": "vite" 7 | }, 8 | "devDependencies": { 9 | "@better-auth/test-utils": "workspace:*", 10 | "vite": "^7.1.5" 11 | }, 12 | "dependencies": { 13 | "better-auth": "workspace:*", 14 | "better-sqlite3": "^12.2.0", 15 | "kysely": "^0.28.5", 16 | "kysely-postgres-js": "^3.0.0", 17 | "postgres": "^3.4.7" 18 | } 19 | } 20 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/vite/vite.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { defineConfig } from "vite"; 2 | import { resolve } from "node:path"; 3 | 4 | export default defineConfig({ 5 | build: { 6 | rollupOptions: { 7 | input: { 8 | server: resolve(__dirname, "src", "server.ts"), 9 | client: resolve(__dirname, "src", "client.ts"), 10 | }, 11 | output: { 12 | entryFileNames: "[name].js", 13 | format: "es", 14 | }, 15 | treeshake: false, 16 | }, 17 | minify: false, 18 | sourcemap: "inline", 19 | }, 20 | }); 21 | ``` -------------------------------------------------------------------------------- /e2e/integration/playwright.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { defineConfig, devices } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | testMatch: "**/e2e/**/*.spec.ts", 5 | fullyParallel: true, 6 | forbidOnly: !!process.env.CI, 7 | retries: process.env.CI ? 2 : 0, 8 | workers: process.env.CI ? 1 : undefined, 9 | reporter: "html", 10 | use: { 11 | trace: "on-first-retry", 12 | }, 13 | 14 | projects: [ 15 | { 16 | name: "chromium", 17 | use: { ...devices["Desktop Chrome"] }, 18 | }, 19 | ], 20 | }); 21 | ``` -------------------------------------------------------------------------------- /e2e/smoke/test/typecheck.spec.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { test } from "node:test"; 2 | import { spawnSync } from "node:child_process"; 3 | import { fileURLToPath } from "node:url"; 4 | import { resolve } from "node:path"; 5 | 6 | const fixturesDir = fileURLToPath(new URL("./fixtures", import.meta.url)); 7 | 8 | test("typecheck", () => { 9 | spawnSync("pnpm", ["run", "typecheck"], { 10 | stdio: "inherit", 11 | cwd: resolve(fixturesDir, "tsconfig-verbatim-module-syntax-node10"), 12 | }); 13 | }); 14 | ``` -------------------------------------------------------------------------------- /packages/core/src/db/schema/rate-limit.ts: -------------------------------------------------------------------------------- ```typescript 1 | import * as z from "zod"; 2 | 3 | export const rateLimitSchema = z.object({ 4 | /** 5 | * The key to use for rate limiting 6 | */ 7 | key: z.string(), 8 | /** 9 | * The number of requests made 10 | */ 11 | count: z.number(), 12 | /** 13 | * The last request time in milliseconds 14 | */ 15 | lastRequest: z.number(), 16 | }); 17 | 18 | /** 19 | * Rate limit schema type used by better-auth for rate limiting 20 | */ 21 | export type RateLimit = z.infer<typeof rateLimitSchema>; 22 | ``` -------------------------------------------------------------------------------- /docs/app/llms.txt/route.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { source } from "@/lib/source"; 2 | import { getLLMText } from "../docs/lib/get-llm-text"; 3 | 4 | export const revalidate = false; 5 | 6 | export async function GET() { 7 | const scan = source 8 | .getPages() 9 | .filter((file) => file.slugs[0] !== "openapi") 10 | .map(getLLMText); 11 | const scanned = await Promise.all(scan); 12 | 13 | return new Response(scanned.join("\n\n"), { 14 | headers: { 15 | "Content-Type": "text/markdown", 16 | }, 17 | }); 18 | } 19 | ``` -------------------------------------------------------------------------------- /docs/components/divider-text.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import type { ReactNode } from "react"; 2 | 3 | export function DividerText({ children }: { children: ReactNode }) { 4 | return ( 5 | <div className="flex items-center justify-center w-full"> 6 | <div className="w-full border-b border-muted"></div> 7 | <div className="flex items-center justify-center w-full text-muted-foreground"> 8 | {children} 9 | </div> 10 | <div className="w-full border-b border-muted"></div> 11 | </div> 12 | ); 13 | } 14 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/captcha/utils.ts: -------------------------------------------------------------------------------- ```typescript 1 | export const encodeToURLParams = (obj: Record<string, any>): string => { 2 | if (typeof obj !== "object" || obj === null || Array.isArray(obj)) { 3 | throw new Error("Input must be a non-null object."); 4 | } 5 | 6 | const params = new URLSearchParams(); 7 | 8 | for (const [key, value] of Object.entries(obj)) { 9 | if (value !== undefined && value !== null) { 10 | params.append(key, String(value)); 11 | } 12 | } 13 | 14 | return params.toString(); 15 | }; 16 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/multi-session/client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { multiSession } from "."; 2 | import type { BetterAuthClientPlugin } from "@better-auth/core"; 3 | 4 | export const multiSessionClient = () => { 5 | return { 6 | id: "multi-session", 7 | $InferServerPlugin: {} as ReturnType<typeof multiSession>, 8 | atomListeners: [ 9 | { 10 | matcher(path) { 11 | return path === "/multi-session/set-active"; 12 | }, 13 | signal: "$sessionSignal", 14 | }, 15 | ], 16 | } satisfies BetterAuthClientPlugin; 17 | }; 18 | ``` -------------------------------------------------------------------------------- /docs/components.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "", 8 | "css": "app/global.css", 9 | "baseColor": "stone", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } 22 | ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/src/lib/auth-client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { createAuthClient } from "better-auth/solid"; 2 | 3 | const search = 4 | typeof window !== "undefined" 5 | ? new URLSearchParams(window.location.search) 6 | : new URLSearchParams(""); 7 | const port = search.get("port"); 8 | 9 | export const client = createAuthClient({ 10 | baseURL: `http://localhost:${port || 3000}`, 11 | }); 12 | 13 | export type Session = typeof client.$Infer.Session; 14 | 15 | declare global { 16 | interface Window { 17 | client: typeof client; 18 | } 19 | } 20 | ``` -------------------------------------------------------------------------------- /demo/nextjs/lib/shared.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { ReadonlyURLSearchParams } from "next/navigation"; 2 | 3 | const allowedCallbackSet: ReadonlySet<string> = new Set([ 4 | "/dashboard", 5 | "/device", 6 | ]); 7 | 8 | export const getCallbackURL = ( 9 | queryParams: ReadonlyURLSearchParams, 10 | ): string => { 11 | const callbackUrl = queryParams.get("callbackUrl"); 12 | if (callbackUrl) { 13 | if (allowedCallbackSet.has(callbackUrl)) { 14 | return callbackUrl; 15 | } 16 | return "/dashboard"; 17 | } 18 | return "/dashboard"; 19 | }; 20 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/oidc-provider/utils.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { base64Url } from "@better-auth/utils/base64"; 2 | import { createHash } from "@better-auth/utils/hash"; 3 | 4 | /** 5 | * Default client secret hasher using SHA-256 6 | */ 7 | export const defaultClientSecretHasher = async (clientSecret: string) => { 8 | const hash = await createHash("SHA-256").digest( 9 | new TextEncoder().encode(clientSecret), 10 | ); 11 | const hashed = base64Url.encode(new Uint8Array(hash), { 12 | padding: false, 13 | }); 14 | return hashed; 15 | }; 16 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/clone.ts: -------------------------------------------------------------------------------- ```typescript 1 | const cloneBase = (object: any, base: any): any => { 2 | for (const key in object) { 3 | if (!object.hasOwnProperty(key)) continue; 4 | 5 | const value = object[key]; 6 | 7 | if (typeof value === "object" && value !== null) { 8 | base[key] = cloneBase(value, value.constructor()); 9 | } else { 10 | base[key] = value; 11 | } 12 | } 13 | 14 | return base; 15 | }; 16 | 17 | export const clone = <T extends object>(object: T): T => { 18 | return cloneBase(object, object.constructor()); 19 | }; 20 | ``` -------------------------------------------------------------------------------- /demo/nextjs/components/logo.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import { SVGProps } from "react"; 2 | 3 | export const Logo = (props: SVGProps<any>) => { 4 | return ( 5 | <svg 6 | width="60" 7 | height="45" 8 | viewBox="0 0 60 45" 9 | fill="none" 10 | className="w-5 h-5" 11 | xmlns="http://www.w3.org/2000/svg" 12 | > 13 | <path 14 | fillRule="evenodd" 15 | clipRule="evenodd" 16 | d="M0 0H15V15H30V30H15V45H0V30V15V0ZM45 30V15H30V0H45H60V15V30V45H45H30V30H45Z" 17 | className="fill-black dark:fill-white" 18 | /> 19 | </svg> 20 | ); 21 | }; 22 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/plugin-helper.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { APIError } from "better-call"; 2 | 3 | export const getEndpointResponse = async <T>(ctx: { 4 | context: { 5 | returned?: unknown; 6 | }; 7 | }) => { 8 | const returned = ctx.context.returned; 9 | if (!returned) { 10 | return null; 11 | } 12 | if (returned instanceof Response) { 13 | if (returned.status !== 200) { 14 | return null; 15 | } 16 | return (await returned.clone().json()) as T; 17 | } 18 | if (returned instanceof APIError) { 19 | return null; 20 | } 21 | return returned as T; 22 | }; 23 | ``` -------------------------------------------------------------------------------- /packages/better-auth/src/client/fetch-plugins.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { type BetterFetchPlugin } from "@better-fetch/fetch"; 2 | 3 | export const redirectPlugin = { 4 | id: "redirect", 5 | name: "Redirect", 6 | hooks: { 7 | onSuccess(context) { 8 | if (context.data?.url && context.data?.redirect) { 9 | if (typeof window !== "undefined" && window.location) { 10 | if (window.location) { 11 | try { 12 | window.location.href = context.data.url; 13 | } catch {} 14 | } 15 | } 16 | } 17 | }, 18 | }, 19 | } satisfies BetterFetchPlugin; 20 | ``` -------------------------------------------------------------------------------- /packages/core/tsdown.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { defineConfig } from "tsdown"; 2 | 3 | export default defineConfig({ 4 | dts: true, 5 | format: ["esm", "cjs"], 6 | entry: [ 7 | "./src/index.ts", 8 | "./src/db/index.ts", 9 | "./src/db/adapter/index.ts", 10 | "./src/async_hooks/index.ts", 11 | "./src/context/index.ts", 12 | "./src/env/index.ts", 13 | "./src/oauth2/index.ts", 14 | "./src/middleware/index.ts", 15 | "./src/social-providers/index.ts", 16 | "./src/utils/index.ts", 17 | "./src/error/index.ts", 18 | ], 19 | clean: true, 20 | }); 21 | ``` -------------------------------------------------------------------------------- /demo/nextjs/components.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } 22 | ```