This is page 1 of 48. Use http://codebase.md/better-auth/better-auth?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 │ │ │ │ └── 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 │ │ │ ├── context │ │ │ │ └── transaction.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-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-passkey.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 │ │ │ ├── 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: -------------------------------------------------------------------------------- ``` 22.18.0 ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/drizzle-adapter/test/.gitignore: -------------------------------------------------------------------------------- ``` .tmp drizzle ``` -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- ``` * text=auto eol=lf ``` -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- ``` link-workspace-packages=true ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/prisma-adapter/test/.gitignore: -------------------------------------------------------------------------------- ``` auth.ts schema-mysql.prisma schema-sqlite.prisma schema-postgresql.prisma .tmp ``` -------------------------------------------------------------------------------- /demo/expo-example/.env.example: -------------------------------------------------------------------------------- ``` GITHUB_CLIENT_ID= GITHUB_CLIENT_SECRET= GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= BETTER_AUTH_SECRET= DATABASE_URL= BETTER_AUTH_URL=http://localohst:8081 ``` -------------------------------------------------------------------------------- /docs/.env.example: -------------------------------------------------------------------------------- ``` NEXT_PUBLIC_URL=http://localhost:3000 # Orama Search Configuration ORAMA_PRIVATE_API_KEY= NEXT_PUBLIC_ORAMA_PUBLIC_API_KEY= NEXT_PUBLIC_ORAMA_ENDPOINT= ORAMA_INDEX_ID= ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/.gitignore: -------------------------------------------------------------------------------- ``` dist .wrangler .output .vercel .netlify .vinxi app.config.timestamp_*.js # Environment .env .env*.local # dependencies /node_modules # IDEs and editors /.idea .project .classpath *.launch .settings/ # Temp gitignore # System Files .DS_Store Thumbs.db ``` -------------------------------------------------------------------------------- /demo/expo-example/.gitignore: -------------------------------------------------------------------------------- ``` node_modules/ .expo/ dist/ npm-debug.* *.jks *.p8 *.p12 *.key *.mobileprovision *.orig.* web-build/ # macOS .DS_Store # @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb # The following patterns were generated by expo-cli expo-env.d.ts # @end expo-cli ``` -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- ``` # deps /node_modules # generated content .contentlayer .content-collections .source # test & build /coverage /.next/ /out/ /build *.tsbuildinfo # misc .DS_Store *.pem /.pnp .pnp.js npm-debug.log* yarn-debug.log* yarn-error.log* # others .env*.local .vercel next-env.d.ts ``` -------------------------------------------------------------------------------- /demo/nextjs/.gitignore: -------------------------------------------------------------------------------- ``` # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.* .yarn/* !.yarn/patches !.yarn/plugins !.yarn/releases !.yarn/versions # testing /coverage # next.js /.next/ /out/ # production /build # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* # env files (can opt-in for commiting if needed) .env # vercel .vercel # typescript *.tsbuildinfo next-env.d.ts certificates ``` -------------------------------------------------------------------------------- /demo/nextjs/.env.example: -------------------------------------------------------------------------------- ``` GOOGLE_CLIENT_SECRET= NEXT_PUBLIC_GOOGLE_CLIENT_ID= BETTER_AUTH_URL= BETTER_AUTH_SECRET= TURSO_DATABASE_URL= TURSO_AUTH_TOKEN= GITHUB_CLIENT_ID= GITHUB_CLIENT_SECRET= RESEND_API_KEY= TEST_EMAIL= DISCORD_CLIENT_ID= DISCORD_CLIENT_SECRET= TWITTER_CLIENT_ID= TWITTER_CLIENT_SECRET= MICROSOFT_CLIENT_ID= MICROSOFT_CLIENT_SECRET= TWITCH_CLIENT_ID= TWITCH_CLIENT_SECRET= FACEBOOK_CLIENT_ID= FACEBOOK_CLIENT_SECRET= NODE_ENV= STRIPE_KEY= STRIPE_WEBHOOK_SECRET= PAYPAL_CLIENT_ID= PAYPAL_CLIENT_SECRET= ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore # Logs logs _.log npm-debug.log_ yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* # Caches .cache # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json # Runtime data pids _.pid _.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # Snowpack dependency directory (https://snowpack.dev/) web_modules/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional stylelint cache .stylelintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variable files .env .env.development.local .env.test.local .env.production.local .env.local # parcel-bundler cache (https://parceljs.org/) .parcel-cache # Next.js build output .next out # Nuxt.js build / generate output .nuxt dist # Gatsby files # Comment in the public line in if your project uses Gatsby and not Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # vuepress v2.x temp and cache directory .temp # Docusaurus cache and generated files .docusaurus # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port # Stores VSCode versions used for testing VSCode extensions .vscode-test # yarn v2 .yarn/cache .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz .pnp.* # IntelliJ based IDEs .idea # Finder (MacOS) folder config .DS_Store .notes/ .db/ *.sqlite *.db .turbo/ .tsup .todo android/ .expo/ .vercel .vinxi # Turborepo .turbo playwright-report/ test-results/ state.txt ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/.gitignore: -------------------------------------------------------------------------------- ``` # Created by https://www.toptal.com/developers/gitignore/api/node,macos # Edit at https://www.toptal.com/developers/gitignore?templates=node,macos ### macOS ### # General .DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ### Node ### # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # Moved from ./templates for ignoring all locks in templates templates/**/*-lock.* templates/**/*.lock # Snowpack dependency directory (https://snowpack.dev/) web_modules/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional stylelint cache .stylelintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variable files .env .env.development.local .env.test.local .env.production.local .env.local # parcel-bundler cache (https://parceljs.org/) .cache .parcel-cache # Next.js build output .next out # Nuxt.js build / generate output .nuxt dist # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and not Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # vuepress v2.x temp and cache directory .temp # Docusaurus cache and generated files .docusaurus # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port # Stores VSCode versions used for testing VSCode extensions .vscode-test # yarn v2 .yarn/cache .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz .pnp.* ### Node Patch ### # Serverless Webpack directories .webpack/ # Optional stylelint cache # SvelteKit build / generate output .svelte-kit # End of https://www.toptal.com/developers/gitignore/api/node,macos # Wrangler output .wrangler/ build/ # Turbo output .turbo/ .dev.vars* !.dev.vars.example .env* !.env.example ``` -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- ```markdown # Better Auth Docs This is the documentation site for Better Auth. ## Contributing To contribute to the docs, you can clone the repo and run the docs site locally. ```bash pnpm install pnpm run dev ``` This will start the docs site on [http://localhost:3000](http://localhost:3000). ``` -------------------------------------------------------------------------------- /demo/expo-example/README.md: -------------------------------------------------------------------------------- ```markdown # Better Auth Expo Example This is an example of how to use Better Auth with Expo. It uses Expo's new API Router to host the auth server. ## How to run 1. Clone the code sandbox (or the repo) and open it in your code editor 2. Move and Provide environment variable 3. Run the following commands ```bash pnpm install pnpm start ```s Checkout the [expo guide](https://www.better-auth.com/docs/integrations/expo) to learn more. ``` -------------------------------------------------------------------------------- /packages/better-auth/README.md: -------------------------------------------------------------------------------- ```markdown <p align="center"> <picture> <source srcset="https://github.com/better-auth/better-auth/blob/main/banner-dark.png?raw=true" media="(prefers-color-scheme: dark)"> <source srcset="https://github.com/better-auth/better-auth/blob/main/banner.png?raw=true" media="(prefers-color-scheme: light)"> <img src="https://github.com/better-auth/better-auth/blob/main/banner.png?raw=true" alt="Better Auth Logo"> </picture> <h2 align="center"> Better Auth </h2> <p align="center"> The most comprehensive authentication library for TypeScript <br /> <a href="https://better-auth.com"><strong>Learn more »</strong></a> <br /> <br /> <a href="https://discord.gg/better-auth">Discord</a> · <a href="https://better-auth.com">Website</a> · <a href="https://github.com/better-auth/better-auth/issues">Issues</a> </p> </p> ## Getting Started ```bash pnpm install better-auth ``` Read the [Installation Guide](https://better-auth.com/docs/installation) to learn more. ``` -------------------------------------------------------------------------------- /packages/cli/README.md: -------------------------------------------------------------------------------- ```markdown # Better Auth CLI Better Auth comes with a built-in CLI to help you manage the database schema needed for both core functionality and plugins. ### **Init** The CLI includes an `init` command to add Better Auth to your project. ```bash title="terminal" npx @better-auth/cli@latest init ``` ### **Generate** 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. ```bash title="terminal" npx @better-auth/cli@latest generate ``` ### **Migrate** 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. ```bash title="terminal" npx @better-auth/cli@latest migrate ``` ### **Secret** The CLI also provides a way to generate a secret key for your Better Auth instance. ```bash title="terminal" npx @better-auth/cli@latest secret ``` ## License MIT ``` -------------------------------------------------------------------------------- /docs/scripts/endpoint-to-doc/readme.md: -------------------------------------------------------------------------------- ```markdown # Endpoint To Documentation 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 to easily document the details of a given endpoint. This script will also generate JSDoc which you can then place above each endpoint code. ## Requirements This does however require Bun since we're running typescript code without transpiling to JS before executing. ## How to run Head into the `docs/scripts/endpoint-to-doc/input.ts` file, and copy over the desired `createAuthEndpoint` properties. 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. Since we don't run the handler, we can safely ignore those types. 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. Then, make sure you're in the `docs` directory within your terminal. and run: ```bash bun scripts:endpoint-to-doc ``` 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 <p align="center"> <picture> <source srcset="./banner-dark.png" media="(prefers-color-scheme: dark)"> <source srcset="./banner.png" media="(prefers-color-scheme: light)"> <img src="./banner.png" alt="Better Auth Logo"> </picture> <h2 align="center"> Better Auth </h2> <p align="center"> The most comprehensive authentication library for TypeScript <br /> <a href="https://better-auth.com"><strong>Learn more »</strong></a> <br /> <br /> <a href="https://discord.gg/better-auth">Discord</a> · <a href="https://better-auth.com">Website</a> · <a href="https://github.com/better-auth/better-auth/issues">Issues</a> </p> [](https://npm.chart.dev/better-auth?primary=neutral&gray=neutral&theme=dark) [](https://www.npmjs.com/package/better-auth) [](https://github.com/better-auth/better-auth/stargazers) </p> ## About the Project 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. ### Why Better Auth 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. ## Contribution 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. You could help continuing its development by: - [Contribute to the source code](./CONTRIBUTING.md) - [Suggest new features and report issues](https://github.com/better-auth/better-auth/issues) ## Security If you discover a security vulnerability within Better Auth, please send an e-mail to [email protected]. All reports will be promptly addressed, and you'll be credited accordingly. ``` -------------------------------------------------------------------------------- /packages/expo/README.md: -------------------------------------------------------------------------------- ```markdown # Better Auth Expo Plugin 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. ## Installation To get started, install the necessary packages: ```bash # Using npm npm install better-auth @better-auth/expo # Using yarn yarn add better-auth @better-auth/expo # Using pnpm pnpm add better-auth @better-auth/expo # Using bun bun add better-auth @better-auth/expo ``` You will also need to install `expo-secure-store` for secure session and cookie storage in your Expo app: ```bash npm install expo-secure-store # or yarn add expo-secure-store # or pnpm add expo-secure-store # or bun add expo-secure-store ``` ## Basic Usage ### Configure the Better Auth Backend Ensure you have a Better Auth backend set up. You can follow the main [Installation Guide](https://www.better-auth.com/docs/installation). Then, add the Expo plugin to your Better Auth server configuration (e.g., in your `auth.ts` or `lib/auth.ts` file): ```typescript // lib/auth.ts import { betterAuth } from "better-auth"; import { expo } from "@better-auth/expo"; // Import the server plugin export const auth = betterAuth({ // ...your other Better Auth options baseURL: "http://localhost:8081", // The base URL of your application server where the routes are mounted. plugins: [expo()], // Add the Expo server plugin emailAndPassword: { enabled: true, }, // Add other configurations like trustedOrigins trustedOrigins: ["myapp://"] // Replace "myapp" with your app's scheme }); ``` ### Initialize the Better Auth Client in Expo In your Expo app, initialize the client (e.g., in `lib/auth-client.ts`): ```typescript // lib/auth-client.ts import { createAuthClient } from "better-auth/react"; import { expoClient } from "@better-auth/expo/client"; // Import the client plugin import * as SecureStore from "expo-secure-store"; export const authClient = createAuthClient({ baseURL: "http://localhost:8081", // Your Better Auth backend URL plugins: [ expoClient({ scheme: "myapp", // Your app's scheme (defined in app.json) storagePrefix: "myapp", // A prefix for storage keys storage: SecureStore, // Pass SecureStore for token storage }) ] }); // You can also export specific methods if you prefer: // export const { signIn, signUp, useSession } = authClient; ``` Make sure your app's scheme (e.g., "myapp") is defined in your `app.json`. ## Documentation For more detailed information and advanced configurations, please refer to the documentation: - **Main Better Auth Installation:** [https://www.better-auth.com/docs/installation](https://www.better-auth.com/docs/installation) - **Expo Integration Guide:** [https://www.better-auth.com/docs/integrations/expo](https://www.better-auth.com/docs/integrations/expo) ## License MIT ``` -------------------------------------------------------------------------------- /demo/nextjs/README.md: -------------------------------------------------------------------------------- ```markdown # Better Auth Demo App 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). ## Getting Started Here’s how you can get the app running locally: ### Prerequisites 1. **Clone the repo**: ```bash git clone https://github.com/better-auth/better-auth cd better-auth/demo/nextjs ``` 2. **Install the dependencies**: ```bash npm install # or yarn install # or pnpm install ``` 3. **Set up your environment variables**: - Rename the `.env.example` file to `.env`: ```bash mv .env.example .env ``` - 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. 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. ### Start the Development Server Once everything is set up, start the development server with: ```bash npm run dev # or yarn dev # or pnpm dev # or bun dev ``` The app will be live at [http://localhost:3000](http://localhost:3000). Open it in your browser, and you’re good to go! Feel free to jump in and edit the app by modifying `app/page.tsx`. Any changes you make will update automatically in the browser. ## Features Here’s what this app supports out of the box: - **[Email & Password](https://www.better-auth.com/docs/basic-usage#email-password)**: Simple and secure authentication. - **[Organization / Teams](https://www.better-auth.com/docs/plugins/organization)**: Manage users within organizations or teams. - **[Passkeys](https://www.better-auth.com/docs/plugins/passkey)**: Passwordless login using modern authentication standards. - **[Multi-Factor Authentication (MFA)](https://www.better-auth.com/docs/plugins/2fa)**: Add an extra layer of security. - **[Password Reset](https://www.better-auth.com/docs/concepts/email#password-reset-email)**: Let users reset their passwords if they forget them. - **[Email Verification](https://www.better-auth.com/docs/concepts/email#email-verification)**: Ensure users verify their email addresses. - **[Roles & Permissions](https://www.better-auth.com/docs/plugins/admin#role)**: Define and manage who can do what. - **[Rate Limiting](https://www.better-auth.com/docs/concepts/rate-limit)**: Protect your app from abuse with smart limits. - **[Session Management](https://www.better-auth.com/docs/concepts/session-management)**: Handle user sessions seamlessly. - **[Stripe Plugin](https://www.better-auth.com/docs/plugins/stripe)**: Integrate Stripe for customer management, subscriptions, and webhooks. ## Learn More Here are some helpful links if you want to dive deeper: - [Better Auth Documentation](https://better-auth.com/docs) - Everything you need to know to integrate Better Auth. - [Next.js Documentation](https://nextjs.org/docs) - Learn about the framework we used to build this app. - [Learn Next.js](https://nextjs.org/learn) - A hands-on tutorial for Next.js. --- 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). Happy coding! ``` -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- ```markdown ## Security Policy ### Reporting a Vulnerability If you believe you've found a security vulnerability, please follow these steps: 1. Do not disclose the vulnerability publicly until it has been addressed by our team. 2. Email your findings to `[email protected]` Include: - A description of the vulnerability - Steps to reproduce the vulnerability - Potential impact of the vulnerability - Any suggestions for mitigation - Any other relevant information 3. We will respond to your report within 72 hours. 4. If the issue is confirmed, we will release a patch as soon as possible. ### Disclosure Policy 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. ## Supported Versions We only support the latest version of Better Auth. Older versions are not supported. ``` -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- ```markdown The MIT License (MIT) Copyright (c) 2024 - present, Bereket Engida Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- ```markdown # Contributing to Better Auth Thank you for your interest in contributing to Better Auth. This guide will help you get started with the contribution process. ## Code of Conduct 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. ## Project Structure The Better Auth monorepo is organized as follows: - `/packages/better-auth` - Core authentication library - `/packages/cli` - Command-line interface tools - `/packages/expo` - Expo integration - `/packages/stripe` - Stripe payment integration - `/packages/sso` - SSO plugin with SAML and OIDC support - `/docs` - Documentation website - `/examples` - Example applications - `/demo` - Demo applications ## Development Guidelines When contributing to Better Auth: - 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. - Ensure all code is type-safe and takes full advantage of TypeScript features. - Write clear, self-explanatory code. Use comments only when truly necessary. - Maintain a consistent and predictable API across all supported frameworks. - Follow the existing code style and conventions. - We aim for stability, so avoid changes that would require users to run a migration or update their config... ## Getting Started 1. Fork the repository to your GitHub account 2. Clone your fork locally: ```bash git clone https://github.com/your-username/better-auth.git cd better-auth ``` 3. Install Node.js (LTS version recommended) 4. Install pnpm if you haven't already: ```bash npm install -g pnpm ``` 5. Install project dependencies: ```bash pnpm install ``` 6. Create a `.env` file from the example: - On Unix-based systems: ```bash cp -n ./docs/.env.example ./docs/.env ``` - On Windows: ```cmd copy /Y .\docs\.env.example .\docs\.env ``` 7. Build the project: ```bash pnpm build ``` 8. Run the documentation locally: ```bash pnpm -F docs dev ``` ## Code Formatting with BiomeJS We use [BiomeJS](https://biomejs.dev/) for code formatting and linting. Before committing, please ensure your code is properly formatted: ```bash # Format all code pnpm format # Check for linting issues pnpm lint # Fix auto-fixable issues pnpm lint:fix ``` ## Development Workflow 1. Create a new branch for your changes: ```bash git checkout -b type/description # Example: git checkout -b feat/oauth-provider ``` Branch type prefixes: - `feat/` - New features - `fix/` - Bug fixes - `docs/` - Documentation changes - `refactor/` - Code refactoring - `test/` - Test-related changes - `chore/` - Build process or tooling changes 2. Make your changes following the code style guidelines 3. Add tests for your changes 4. Run the test suite: ```bash # Run all tests pnpm test # Run tests for a specific package pnpm -F "{packagename}" test ``` 5. Ensure all tests pass and the code is properly formatted 6. Commit your changes with a descriptive message following this format: 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: ``` fix(organization): fix incorrect member role assignment feat(two-factor): add support for TOTP authentication ``` For core library changes that don't have a specific plugin or scope, you can use `fix` and `feat` without a scope: ``` fix: resolve memory leak in session handling feat: add support for custom error messages ``` For documentation changes, use `docs`: ```bash docs: improve authentication flow explanation docs: fix typos in API reference ``` For changes that refactor or don't change the functionality of the library or docs, use `chore`: ```bash chore(refactor): reorganize authentication middleware chore: update dependencies to latest versions ``` 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. 7. Push your branch to your fork 8. Open a pull request against the **canary** branch. In your PR description: - Clearly describe what changes you made and why - Include any relevant context or background - List any breaking changes or deprecations - Add screenshots for UI changes - Reference related issues or discussions ## Testing All contributions must include appropriate tests. Follow these guidelines: - Write unit tests for new features - Ensure all tests pass before submitting a pull request - Update existing tests if your changes affect their behavior - Follow the existing test patterns and structure - Test across different environments when applicable ## Pull Request Process 1. Create a draft pull request early to facilitate discussion 2. Reference any related issues in your PR description (e.g., 'Closes #123') 3. Ensure all tests pass and the build is successful 4. Update documentation as needed 5. Keep your PR focused on a single feature or bug fix 6. Be responsive to code review feedback 7. Update the CHANGELOG.md if your changes are user-facing ## Code Style - Follow the existing code style - Use TypeScript types and interfaces effectively - Keep functions small and focused - Use meaningful variable and function names - Add comments for complex logic - Update relevant documentation when making API changes - Follow the BiomeJS formatting rules - Avoid using Classes ## Component-Specific Guidelines ### Core Library (`/packages/better-auth`) - Keep the core library focused on essential authentication functionality - Plugins in the core generally are made by core members. If you have a plugin idea consider open sourcing it yourself instead. - Ensure all public APIs are well-documented with JSDoc comments - Maintain backward compatibility. If it's super necessary, provide a clear migration path - Follow the existing patterns for error handling and logging ### Documentation (`/docs`) - Keep documentation up-to-date with code changes - Use clear, concise language - Include code examples for common use cases - Document any breaking changes in the migration guide - Follow the existing documentation style and structure ## Security Issues 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 # Contributor Covenant 3.0 ## Our Pledge We pledge to make our community welcoming, safe, and equitable for all. 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. ## Encouraged Behaviors 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. With these considerations in mind, we agree to behave mindfully toward each other and act in ways that center our shared values, including: 1. Respecting the **purpose of our community**, our activities, and our ways of gathering. 2. Engaging **kindly and honestly** with others. 3. Respecting **different viewpoints** and experiences. 4. **Taking responsibility** for our actions and contributions. 5. Gracefully giving and accepting **constructive feedback**. 6. Committing to **repairing harm** when it occurs. 7. Behaving in other ways that promote and sustain the **well-being of our community**. ## Restricted Behaviors We agree to restrict the following behaviors in our community. Instances, threats, and promotion of these behaviors are violations of this Code of Conduct. 1. **Harassment.** Violating explicitly expressed boundaries or engaging in unnecessary personal attention after any clear request to stop. 2. **Character attacks.** Making insulting, demeaning, or pejorative comments directed at a community member or group of people. 3. **Stereotyping or discrimination.** Characterizing anyone’s personality or behavior on the basis of immutable identities or traits. 4. **Sexualization.** Behaving in a way that would generally be considered inappropriately intimate in the context or purpose of the community. 5. **Violating confidentiality**. Sharing or acting on someone's personal or private information without their permission. 6. **Endangerment.** Causing, encouraging, or threatening violence or other harm toward any person or group. 7. Behaving in other ways that **threaten the well-being** of our community. ### Other Restrictions 1. **Misleading identity.** Impersonating someone else for any reason, or pretending to be someone else to evade enforcement actions. 2. **Failing to credit sources.** Not properly crediting the sources of content you contribute. 3. **Promotional materials**. Sharing marketing or other commercial content in a way that is outside the norms of the community. 4. **Irresponsible communication.** Failing to responsibly present content which includes, links or describes any other restricted behaviors. ## Reporting an Issue 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. When an incident does occur, it is important to report it promptly. To report a possible violation, **email [email protected]** 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. ## Addressing and Repairing Harm **** 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. 1) Warning 1) Event: A violation involving a single incident or series of incidents. 2) Consequence: A private, written warning from the Community Moderators. 3) Repair: Examples of repair include a private written apology, acknowledgement of responsibility, and seeking clarification on expectations. 2) Temporarily Limited Activities 1) Event: A repeated incidence of a violation that previously resulted in a warning, or the first incidence of a more serious violation. 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. 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. 3) Temporary Suspension 1) Event: A pattern of repeated violation which the Community Moderators have tried to address with warnings, or a single serious violation. 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. 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. 4) Permanent Ban 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. 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. 3) Repair: There is no possible repair in cases of this severity. 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. ## Scope 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. ## Attribution 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/). 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/) 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). ``` -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- ```yaml github: [better-auth] ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/vite/src/server.ts: -------------------------------------------------------------------------------- ```typescript import "better-auth"; ``` -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "./types"; ``` -------------------------------------------------------------------------------- /demo/expo-example/index.ts: -------------------------------------------------------------------------------- ```typescript import "expo-router/entry"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/admin/access/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "./statement"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/organization/access/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "./statement"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/api/middlewares/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "./origin-check"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/memory-adapter/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "./memory-adapter"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/prisma-adapter/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "./prisma-adapter"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/drizzle-adapter/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "./drizzle-adapter"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/mongodb-adapter/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "./mongodb-adapter"; ``` -------------------------------------------------------------------------------- /e2e/integration/vanilla-node/src/vite-env.d.ts: -------------------------------------------------------------------------------- ```typescript /// <reference types="vite/client" /> ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/src/global.d.ts: -------------------------------------------------------------------------------- ```typescript /// <reference types="@solidjs/start/env" /> ``` -------------------------------------------------------------------------------- /packages/core/src/utils/index.ts: -------------------------------------------------------------------------------- ```typescript export { defineErrorCodes } from "./error-codes"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/access/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "./access"; export * from "./types"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/social-providers/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "@better-auth/core/social-providers"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/admin/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "./admin"; export type * from "./types"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/hide-metadata.ts: -------------------------------------------------------------------------------- ```typescript export const HIDE_METADATA = { isAction: false as const, }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/constants.ts: -------------------------------------------------------------------------------- ```typescript export const DEFAULT_SECRET = "better-auth-secret-123456789"; ``` -------------------------------------------------------------------------------- /demo/nextjs/postcss.config.mjs: -------------------------------------------------------------------------------- ``` export default { plugins: { "@tailwindcss/postcss": {}, }, }; ``` -------------------------------------------------------------------------------- /docs/postcss.config.js: -------------------------------------------------------------------------------- ```javascript export default { plugins: { "@tailwindcss/postcss": {}, }, }; ``` -------------------------------------------------------------------------------- /demo/expo-example/components.json: -------------------------------------------------------------------------------- ```json { "aliases": { "components": "@/components", "lib": "@/lib" } } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/kysely-adapter/types.ts: -------------------------------------------------------------------------------- ```typescript export type KyselyDatabaseType = "postgres" | "mysql" | "sqlite" | "mssql"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/boolean.ts: -------------------------------------------------------------------------------- ```typescript export function toBoolean(value: any): boolean { return value === "true" || value === true; } ``` -------------------------------------------------------------------------------- /docs/app/layout.config.tsx: -------------------------------------------------------------------------------- ```typescript import { source } from "@/lib/source"; export const docsOptions = { tree: source.pageTree, }; ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/app.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineConfig } from "@solidjs/start/config"; export default defineConfig({ ssr: false, }); ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/misc.ts: -------------------------------------------------------------------------------- ```typescript export function capitalizeFirstLetter(str: string) { return str.charAt(0).toUpperCase() + str.slice(1); } ``` -------------------------------------------------------------------------------- /demo/nextjs/lib/email/resend.ts: -------------------------------------------------------------------------------- ```typescript import { Resend } from "resend"; export const resend = new Resend(process.env.RESEND_API_KEY || "re_123"); ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/two-factor/constant.ts: -------------------------------------------------------------------------------- ```typescript export const TWO_FACTOR_COOKIE_NAME = "two_factor"; export const TRUST_DEVICE_COOKIE_NAME = "trust_device"; ``` -------------------------------------------------------------------------------- /demo/expo-example/expo-env.d.ts: -------------------------------------------------------------------------------- ```typescript /// <reference types="expo/types" /> // NOTE: This file should not be edited and should be in your git ignore ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/test/apply-migrations.ts: -------------------------------------------------------------------------------- ```typescript import { applyD1Migrations, env } from "cloudflare:test"; await applyD1Migrations(env.DB, env.TEST_MIGRATIONS); ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/import-util.ts: -------------------------------------------------------------------------------- ```typescript export const importRuntime = <T>(m: string): Promise<T> => { return (Function("mm", "return import(mm)") as any)(m); }; ``` -------------------------------------------------------------------------------- /packages/telemetry/src/utils/import-util.ts: -------------------------------------------------------------------------------- ```typescript export const importRuntime = <T>(m: string): Promise<T> => { return (Function("mm", "return import(mm)") as any)(m); }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/oauth2/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "@better-auth/core/oauth2"; export * from "./utils"; export * from "./state"; export * from "./link-account"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/organization/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "./organization"; export type * from "./schema"; export type * from "./access"; export type * from "./types"; ``` -------------------------------------------------------------------------------- /e2e/integration/vanilla-node/vite.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineConfig } from "vite"; export default defineConfig({ server: { allowedHosts: ["test.com", "localhost"], }, }); ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/drizzle.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineConfig } from "drizzle-kit"; export default defineConfig({ dialect: "sqlite", schema: "./src/auth-schema.ts", }); ``` -------------------------------------------------------------------------------- /demo/expo-example/src/lib/icons/X.tsx: -------------------------------------------------------------------------------- ```typescript import { X } from "lucide-react-native"; import { iconWithClassName } from "./iconWithClassName"; iconWithClassName(X); export { X }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "./misc"; export * from "./hide-metadata"; export * from "./id"; export * from "../oauth2/state"; export * from "./id"; ``` -------------------------------------------------------------------------------- /docs/app/reference/route.ts: -------------------------------------------------------------------------------- ```typescript import { ApiReference } from "@scalar/nextjs-api-reference"; export const GET = ApiReference({ spec: { url: "/openapi.yml", }, }); ``` -------------------------------------------------------------------------------- /packages/telemetry/tsdown.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineConfig } from "tsdown"; export default defineConfig({ dts: true, format: ["esm", "cjs"], entry: ["./src/index.ts"], }); ``` -------------------------------------------------------------------------------- /demo/nextjs/app/api/auth/[...all]/route.ts: -------------------------------------------------------------------------------- ```typescript import { auth } from "@/lib/auth"; import { toNextJsHandler } from "better-auth/next-js"; export const { GET, POST } = toNextJsHandler(auth); ``` -------------------------------------------------------------------------------- /demo/nextjs/app/dashboard/client.tsx: -------------------------------------------------------------------------------- ```typescript "use client"; export function ManageAccount() { return ( <div className="flex items-center gap-2"> <p>Manage Account</p> </div> ); } ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/src/entry-client.tsx: -------------------------------------------------------------------------------- ```typescript // @refresh reload import { mount, StartClient } from "@solidjs/start/client"; mount(() => <StartClient />, document.getElementById("app")!); ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/date.ts: -------------------------------------------------------------------------------- ```typescript export const getDate = (span: number, unit: "sec" | "ms" = "ms") => { return new Date(Date.now() + (unit === "sec" ? span * 1000 : span)); }; ``` -------------------------------------------------------------------------------- /packages/stripe/vitest.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineConfig } from "vitest/config"; export default defineConfig({ root: ".", test: { clearMocks: true, globals: true, }, }); ``` -------------------------------------------------------------------------------- /docs/app/changelogs/layout.tsx: -------------------------------------------------------------------------------- ```typescript import type { ReactNode } from "react"; export default function Layout({ children }: { children: ReactNode }) { return <div>{children}</div>; } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/tests/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "./normal"; export * from "./performance"; export * from "./transactions"; export * from "./auth-flow"; export * from "./number-id"; ``` -------------------------------------------------------------------------------- /docs/app/api/search/route.ts: -------------------------------------------------------------------------------- ```typescript import { source } from "@/lib/source"; import { createFromSource } from "fumadocs-core/search/server"; export const { GET } = createFromSource(source); ``` -------------------------------------------------------------------------------- /demo/nextjs/components/ui/aspect-ratio.tsx: -------------------------------------------------------------------------------- ```typescript "use client"; import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"; const AspectRatio = AspectRatioPrimitive.Root; export { AspectRatio }; ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/src/db.ts: -------------------------------------------------------------------------------- ```typescript import { drizzle } from "drizzle-orm/d1"; import * as schema from "./auth-schema"; export const createDrizzle = (db: D1Database) => drizzle(db, { schema }); ``` -------------------------------------------------------------------------------- /demo/expo-example/nativewind-env.d.ts: -------------------------------------------------------------------------------- ```typescript /// <reference types="nativewind/types" /> // NOTE: This file should not be edited and should be committed with your source code. It is generated by NativeWind. ``` -------------------------------------------------------------------------------- /packages/better-auth/vitest.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineConfig } from "vitest/config"; export default defineConfig({ test: { poolOptions: { forks: { execArgv: ["--expose-gc"], }, }, }, }); ``` -------------------------------------------------------------------------------- /demo/nextjs/lib/utils.ts: -------------------------------------------------------------------------------- ```typescript import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } ``` -------------------------------------------------------------------------------- /e2e/smoke/package.json: -------------------------------------------------------------------------------- ```json { "name": "smoke", "type": "module", "dependencies": { "better-auth": "workspace:*" }, "scripts": { "e2e:smoke": "node --test ./test/*.spec.ts" } } ``` -------------------------------------------------------------------------------- /demo/expo-example/babel.config.js: -------------------------------------------------------------------------------- ```javascript module.exports = function (api) { api.cache(true); return { presets: [ ["babel-preset-expo", { jsxImportSource: "nativewind" }], "nativewind/babel", ], }; }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/crypto/random.ts: -------------------------------------------------------------------------------- ```typescript import { createRandomStringGenerator } from "@better-auth/utils/random"; export const generateRandomString = createRandomStringGenerator( "a-z", "0-9", "A-Z", "-_", ); ``` -------------------------------------------------------------------------------- /packages/cli/tsdown.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineConfig } from "tsdown"; export default defineConfig({ dts: false, format: ["esm"], entry: ["./src/index.ts"], external: ["better-auth", "better-call"], }); ``` -------------------------------------------------------------------------------- /packages/telemetry/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "rootDir": "src" }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] } ``` -------------------------------------------------------------------------------- /packages/core/src/db/schema/shared.ts: -------------------------------------------------------------------------------- ```typescript import * as z from "zod"; export const coreSchema = z.object({ id: z.string(), createdAt: z.date().default(() => new Date()), updatedAt: z.date().default(() => new Date()), }); ``` -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.json", "compilerOptions": { "rootDir": "./src", "outDir": "./dist", "lib": ["esnext", "dom", "dom.iterable"] }, "include": ["src"] } ``` -------------------------------------------------------------------------------- /packages/expo/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.json", "compilerOptions": { "rootDir": "./src", "outDir": "./dist", "lib": ["esnext", "dom", "dom.iterable"] }, "include": ["src"] } ``` -------------------------------------------------------------------------------- /packages/sso/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.json", "compilerOptions": { "rootDir": "./src", "outDir": "./dist", "lib": ["esnext", "dom", "dom.iterable"] }, "include": ["src"] } ``` -------------------------------------------------------------------------------- /packages/stripe/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.json", "compilerOptions": { "rootDir": "./src", "outDir": "./dist", "lib": ["esnext", "dom", "dom.iterable"] }, "include": ["src"] } ``` -------------------------------------------------------------------------------- /docs/app/static.json/route.ts: -------------------------------------------------------------------------------- ```typescript import { exportSearchIndexes } from "@/lib/export-search-indexes"; export const revalidate = false; export async function GET() { return Response.json(await exportSearchIndexes()); } ``` -------------------------------------------------------------------------------- /bump.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineConfig } from "bumpp"; import { globSync } from "tinyglobby"; export default defineConfig({ files: globSync(["./packages/*/package.json"], { expandDirectories: false }), }); ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/kysely-adapter/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "./dialect"; export * from "./types"; export * from "./kysely-adapter"; // Don't export node:sqlite by default, as it is not production ready. // export * from "./node-sqlite"; ``` -------------------------------------------------------------------------------- /demo/expo-example/src/app/api/auth/[...route]+api.ts: -------------------------------------------------------------------------------- ```typescript import { auth } from "@/lib/auth"; export const GET = (request: Request) => { return auth.handler(request); }; export const POST = (request: Request) => { return auth.handler(request); }; ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/vite/package.json: -------------------------------------------------------------------------------- ```json { "name": "vite", "private": true, "scripts": { "build": "vite build" }, "dependencies": { "better-auth": "workspace:*" }, "devDependencies": { "vite": "^7.1.5" } } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/id.ts: -------------------------------------------------------------------------------- ```typescript import { createRandomStringGenerator } from "@better-auth/utils/random"; export const generateId = (size?: number) => { return createRandomStringGenerator("a-z", "A-Z", "0-9")(size || 32); }; ``` -------------------------------------------------------------------------------- /packages/telemetry/src/utils/id.ts: -------------------------------------------------------------------------------- ```typescript import { createRandomStringGenerator } from "@better-auth/utils/random"; export const generateId = (size?: number) => { return createRandomStringGenerator("a-z", "A-Z", "0-9")(size || 32); }; ``` -------------------------------------------------------------------------------- /e2e/integration/test-utils/package.json: -------------------------------------------------------------------------------- ```json { "name": "@better-auth/test-utils", "type": "module", "exports": { "./playwright": "./src/playwright.ts" }, "private": true, "devDependencies": { "terminate": "^2.8.0" } } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/captcha/verify-handlers/index.ts: -------------------------------------------------------------------------------- ```typescript export { cloudflareTurnstile } from "./cloudflare-turnstile"; export { googleRecaptcha } from "./google-recaptcha"; export { hCaptcha } from "./h-captcha"; export { captchaFox } from "./captchafox"; ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/tsconfig-verbatim-module-syntax-node10/src/index.ts: -------------------------------------------------------------------------------- ```typescript import { betterAuth } from "better-auth"; import Database from "better-sqlite3"; import { expo } from "@better-auth/expo"; betterAuth({ database: new Database("./sqlite.db"), plugins: [expo()], }); ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/drizzle/meta/_journal.json: -------------------------------------------------------------------------------- ```json { "version": "7", "dialect": "sqlite", "entries": [ { "idx": 0, "version": "6", "when": 1740122602683, "tag": "0000_clean_vector", "breakpoints": true } ] } ``` -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.json", "compilerOptions": { "rootDir": "./src", "outDir": "./dist", "lib": ["esnext", "dom", "dom.iterable"], "types": ["node"] }, "include": ["src"] } ``` -------------------------------------------------------------------------------- /docs/lib/constants.ts: -------------------------------------------------------------------------------- ```typescript export const ENV = { NEXT_PUBLIC_WEBSITE_URL: process.env.NEXT_PUBLIC_WEBSITE_URL || "http://localhost:3000", NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION: "", NODE_ENV: process.env.NODE_ENV || "development", }; ``` -------------------------------------------------------------------------------- /e2e/integration/package.json: -------------------------------------------------------------------------------- ```json { "name": "integration", "scripts": { "e2e:integration": "playwright test" }, "dependencies": { "better-auth": "workspace:*" }, "devDependencies": { "@playwright/test": "^1.55.0" } } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/is-promise.ts: -------------------------------------------------------------------------------- ```typescript export function isPromise(obj?: unknown): obj is Promise<unknown> { return ( !!obj && (typeof obj === "object" || typeof obj === "function") && typeof (obj as Promise<unknown>).then === "function" ); } ``` -------------------------------------------------------------------------------- /packages/better-auth/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.json", "compilerOptions": { "rootDir": "./src", "outDir": "./dist", "lib": ["esnext", "dom", "dom.iterable"], "types": ["node", "bun"] }, "include": ["src"] } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/middleware-response.ts: -------------------------------------------------------------------------------- ```typescript type Params = { message: string; status: number; }; export const middlewareResponse = ({ message, status }: Params) => ({ response: new Response( JSON.stringify({ message, }), { status, }, ), }); ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/test/env.d.ts: -------------------------------------------------------------------------------- ```typescript declare module "cloudflare:test" { // Controls the type of `import("cloudflare:test").env` interface ProvidedEnv extends Env { TEST_MIGRATIONS: D1Migration[]; // Defined in `vitest.config.mts` DB: D1Database; } } ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/src/routes/index.tsx: -------------------------------------------------------------------------------- ```typescript import { createEffect } from "solid-js"; import { client } from "../lib/auth-client"; export default function Home() { createEffect(() => { window.client = client; }); return ( <main> <div>Ready</div> </main> ); } ``` -------------------------------------------------------------------------------- /packages/core/src/db/plugin.ts: -------------------------------------------------------------------------------- ```typescript import type { DBFieldAttribute } from "./type"; export type BetterAuthPluginDBSchema = { [table in string]: { fields: { [field in string]: DBFieldAttribute; }; disableMigration?: boolean; modelName?: string; }; }; ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/vite/tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "target": "esnext", "module": "esnext", "moduleResolution": "bundler", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true } } ``` -------------------------------------------------------------------------------- /packages/sso/tsdown.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineConfig } from "tsdown"; export default defineConfig({ dts: true, format: ["esm", "cjs"], entry: ["./src/index.ts", "./src/client.ts"], external: ["better-auth", "better-call", "@better-fetch/fetch", "stripe"], }); ``` -------------------------------------------------------------------------------- /packages/stripe/tsdown.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineConfig } from "tsdown"; export default defineConfig({ dts: true, format: ["esm", "cjs"], entry: ["./src/index.ts", "./src/client.ts"], external: ["better-auth", "better-call", "@better-fetch/fetch", "stripe"], }); ``` -------------------------------------------------------------------------------- /docs/components/builder/store.ts: -------------------------------------------------------------------------------- ```typescript import { atom } from "jotai"; export const optionsAtom = atom({ email: true, passkey: false, socialProviders: ["google", "github"], magicLink: false, signUp: true, label: true, rememberMe: true, requestPasswordReset: true, }); ``` -------------------------------------------------------------------------------- /demo/nextjs/next.config.ts: -------------------------------------------------------------------------------- ```typescript import type { NextConfig } from "next"; const nextConfig: NextConfig = { typescript: { ignoreBuildErrors: true, }, webpack: (config) => { config.externals.push("@libsql/client"); return config; }, }; export default nextConfig; ``` -------------------------------------------------------------------------------- /docs/ignore-build.sh: -------------------------------------------------------------------------------- ```bash #!/bin/bash # Check if commit message starts with release or docs prefix if [[ ! "$message" == "chore(release): version packages"* ]] && [[ ! "$message" == "docs"* ]]; then echo "Skipping build: Not a release or docs commit." exit 1 fi ``` -------------------------------------------------------------------------------- /packages/sso/src/client.ts: -------------------------------------------------------------------------------- ```typescript import type { BetterAuthClientPlugin } from "better-auth"; import { sso } from "./index"; export const ssoClient = () => { return { id: "sso-client", $InferServerPlugin: {} as ReturnType<typeof sso>, } satisfies BetterAuthClientPlugin; }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/siwe/client.ts: -------------------------------------------------------------------------------- ```typescript import type { siwe } from "."; import type { BetterAuthClientPlugin } from "@better-auth/core"; export const siweClient = () => { return { id: "siwe", $InferServerPlugin: {} as ReturnType<typeof siwe>, } satisfies BetterAuthClientPlugin; }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/sso/client.ts: -------------------------------------------------------------------------------- ```typescript import type { sso } from "."; import type { BetterAuthClientPlugin } from "@better-auth/core"; export const ssoClient = () => { return { id: "sso-client", $InferServerPlugin: {} as ReturnType<typeof sso>, } satisfies BetterAuthClientPlugin; }; ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/tsconfig-exact-optional-property-types/package.json: -------------------------------------------------------------------------------- ```json { "name": "tsconfig-exact-optional-property-types", "private": true, "type": "module", "scripts": { "typecheck": "tsc --project tsconfig.json" }, "dependencies": { "better-auth": "workspace:*", "better-sqlite3": "^12.4.1" } } ``` -------------------------------------------------------------------------------- /packages/cli/src/utils/get-package-info.ts: -------------------------------------------------------------------------------- ```typescript import path from "path"; import fs from "fs"; export function getPackageInfo(cwd?: string) { const packageJsonPath = cwd ? path.join(cwd, "package.json") : path.join("package.json"); return JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")); } ``` -------------------------------------------------------------------------------- /demo/nextjs/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- ```typescript import { cn } from "@/lib/utils"; function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) { return ( <div className={cn("animate-pulse rounded-md bg-primary/10", className)} {...props} /> ); } export { Skeleton }; ``` -------------------------------------------------------------------------------- /docs/components/ui/fade-in.tsx: -------------------------------------------------------------------------------- ```typescript "use client"; import { AnimatePresence as PrimitiveAnimatePresence } from "framer-motion"; export const AnimatePresence = ( props: React.ComponentPropsWithoutRef<typeof PrimitiveAnimatePresence>, ) => { return <PrimitiveAnimatePresence {...props} />; }; ``` -------------------------------------------------------------------------------- /docs/content/docs/meta.json: -------------------------------------------------------------------------------- ```json { "title": "guide", "root": true, "pages": [ "introduction", "installation", "basic-usage", "email-password/sign-in-and-sign-up", "email-password/password-reset", "email-password/configuration", "social-sign-on/apple" ] } ``` -------------------------------------------------------------------------------- /demo/nextjs/lib/auth-types.ts: -------------------------------------------------------------------------------- ```typescript import type { auth } from "./auth"; import { client } from "./auth-client"; export type Session = typeof auth.$Infer.Session; export type ActiveOrganization = typeof client.$Infer.ActiveOrganization; export type Invitation = typeof client.$Infer.Invitation; ``` -------------------------------------------------------------------------------- /packages/core/src/error/index.ts: -------------------------------------------------------------------------------- ```typescript export class BetterAuthError extends Error { constructor(message: string, cause?: string) { super(message); this.name = "BetterAuthError"; this.message = message; this.cause = cause; this.stack = ""; } } export { BASE_ERROR_CODES } from "./codes"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/ensure-utc.ts: -------------------------------------------------------------------------------- ```typescript export function ensureUTC(date: Date): Date { const utcTimestamp = Date.UTC( date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds(), ); return new Date(utcTimestamp); } ``` -------------------------------------------------------------------------------- /packages/core/src/types/cookie.ts: -------------------------------------------------------------------------------- ```typescript import type { CookieOptions } from "better-call"; export type BetterAuthCookies = { sessionToken: { name: string; options: CookieOptions }; sessionData: { name: string; options: CookieOptions }; dontRememberToken: { name: string; options: CookieOptions }; }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/username/client.ts: -------------------------------------------------------------------------------- ```typescript import type { username } from "."; import type { BetterAuthClientPlugin } from "@better-auth/core"; export const usernameClient = () => { return { id: "username", $InferServerPlugin: {} as ReturnType<typeof username>, } satisfies BetterAuthClientPlugin; }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/jwt/client.ts: -------------------------------------------------------------------------------- ```typescript import type { jwt } from "./index"; import type { BetterAuthClientPlugin } from "@better-auth/core"; export const jwtClient = () => { return { id: "better-auth-client", $InferServerPlugin: {} as ReturnType<typeof jwt>, } satisfies BetterAuthClientPlugin; }; ``` -------------------------------------------------------------------------------- /demo/nextjs/components/theme-provider.tsx: -------------------------------------------------------------------------------- ```typescript "use client"; import { ThemeProvider as NextThemesProvider } from "next-themes"; export function ThemeProvider({ children, ...props }: { children: React.ReactNode; [key: string]: any; }) { return <NextThemesProvider {...props}>{children}</NextThemesProvider>; } ``` -------------------------------------------------------------------------------- /docs/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- ```typescript import { cn } from "@/lib/utils"; function Skeleton({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="skeleton" className={cn("bg-primary/10 animate-pulse rounded-md", className)} {...props} /> ); } export { Skeleton }; ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/src/routes/api/auth/[...all].ts: -------------------------------------------------------------------------------- ```typescript import { auth } from "../../../lib/auth"; import type { APIEvent } from "@solidjs/start/server"; export async function GET(event: APIEvent) { return auth.handler(event.request); } export async function POST(event: APIEvent) { return auth.handler(event.request); } ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/vite/src/client.ts: -------------------------------------------------------------------------------- ```typescript /// <reference types="vite/client" /> export * from "better-auth/client/plugins"; import { createAuthClient } from "better-auth/client"; export const authClient = createAuthClient({ baseURL: import.meta.env.VITE_BASE_URL || "http://localhost:3000", plugins: [], }); ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/magic-link/client.ts: -------------------------------------------------------------------------------- ```typescript import type { magicLink } from "."; import type { BetterAuthClientPlugin } from "@better-auth/core"; export const magicLinkClient = () => { return { id: "magic-link", $InferServerPlugin: {} as ReturnType<typeof magicLink>, } satisfies BetterAuthClientPlugin; }; ``` -------------------------------------------------------------------------------- /packages/core/src/types/helper.ts: -------------------------------------------------------------------------------- ```typescript type Primitive = string | number | symbol | bigint | boolean | null | undefined; export type LiteralString = "" | (string & Record<never, never>); export type LiteralUnion<LiteralType, BaseType extends Primitive> = | LiteralType | (BaseType & Record<never, never>); ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/oidc-provider/client.ts: -------------------------------------------------------------------------------- ```typescript import type { oidcProvider } from "."; import type { BetterAuthClientPlugin } from "@better-auth/core"; export const oidcClient = () => { return { id: "oidc-client", $InferServerPlugin: {} as ReturnType<typeof oidcProvider>, } satisfies BetterAuthClientPlugin; }; ``` -------------------------------------------------------------------------------- /docs/lib/is-active.ts: -------------------------------------------------------------------------------- ```typescript export function isActive( url: string, pathname: string, nested = true, ): boolean { if (url.endsWith("/")) url = url.slice(0, -1); if (pathname.endsWith("/")) pathname = pathname.slice(0, -1); return url === pathname || (nested && pathname.startsWith(`${url}/`)); } ``` -------------------------------------------------------------------------------- /packages/cli/src/generators/types.ts: -------------------------------------------------------------------------------- ```typescript import type { Adapter, BetterAuthOptions } from "better-auth"; export interface SchemaGenerator { (opts: { file?: string; adapter: Adapter; options: BetterAuthOptions; }): Promise<{ code?: string; fileName: string; overwrite?: boolean; append?: boolean; }>; } ``` -------------------------------------------------------------------------------- /docs/components/ui/aspect-ratio.tsx: -------------------------------------------------------------------------------- ```typescript "use client"; import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"; function AspectRatio({ ...props }: React.ComponentProps<typeof AspectRatioPrimitive.Root>) { return <AspectRatioPrimitive.Root data-slot="aspect-ratio" {...props} />; } export { AspectRatio }; ``` -------------------------------------------------------------------------------- /demo/expo-example/src/lib/icons/iconWithClassName.ts: -------------------------------------------------------------------------------- ```typescript import type { LucideIcon } from "lucide-react-native"; import { cssInterop } from "nativewind"; export function iconWithClassName(icon: LucideIcon) { cssInterop(icon, { className: { target: "style", nativeStyleToProp: { color: true, opacity: true, }, }, }); } ``` -------------------------------------------------------------------------------- /packages/telemetry/src/utils/hash.ts: -------------------------------------------------------------------------------- ```typescript import { createHash } from "@better-auth/utils/hash"; import { base64 } from "@better-auth/utils/base64"; export async function hashToBase64( data: string | ArrayBuffer, ): Promise<string> { const buffer = await createHash("SHA-256").digest(data); return base64.encode(buffer); } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/generic-oauth/client.ts: -------------------------------------------------------------------------------- ```typescript import type { genericOAuth } from "."; import type { BetterAuthClientPlugin } from "@better-auth/core"; export const genericOAuthClient = () => { return { id: "generic-oauth-client", $InferServerPlugin: {} as ReturnType<typeof genericOAuth>, } satisfies BetterAuthClientPlugin; }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/one-time-token/client.ts: -------------------------------------------------------------------------------- ```typescript import type { oneTimeToken } from "./index"; import type { BetterAuthClientPlugin } from "@better-auth/core"; export const oneTimeTokenClient = () => { return { id: "one-time-token", $InferServerPlugin: {} as ReturnType<typeof oneTimeToken>, } satisfies BetterAuthClientPlugin; }; ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/tsconfig-verbatim-module-syntax-node10/package.json: -------------------------------------------------------------------------------- ```json { "name": "tsconfig-verbatim-module-syntax-node10", "private": true, "type": "module", "scripts": { "typecheck": "tsc --project tsconfig.json" }, "dependencies": { "@better-auth/expo": "workspace:^", "better-auth": "workspace:^", "better-sqlite3": "^12.4.1" } } ``` -------------------------------------------------------------------------------- /e2e/integration/vanilla-node/index.html: -------------------------------------------------------------------------------- ```html <!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Better Auth - Vanilla Node E2E Test</title> </head> <body> <script type="module" src="./src/main.ts"></script> </body> </html> ``` -------------------------------------------------------------------------------- /docs/components/theme-provider.tsx: -------------------------------------------------------------------------------- ```typescript "use client"; import * as React from "react"; import { ThemeProvider as NextThemesProvider } from "next-themes"; export function ThemeProvider({ children, ...props }: React.ComponentProps<typeof NextThemesProvider>) { return <NextThemesProvider {...props}>{children}</NextThemesProvider>; } ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/tsconfig-verbatim-module-syntax-node10/tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "target": "ESNext", "module": "NodeNext", "outDir": "dist", "strict": true, "verbatimModuleSyntax": true, "skipLibCheck": true, "types": ["node"], "jsx": "react-jsx", "jsxImportSource": "hono/jsx", "moduleResolution": "node16" } } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/is-atom.ts: -------------------------------------------------------------------------------- ```typescript import type { Atom } from "nanostores"; export function isAtom(value: unknown): value is Atom<unknown> { return ( typeof value === "object" && value !== null && "get" in value && typeof (value as any).get === "function" && "lc" in value && typeof (value as any).lc === "number" ); } ``` -------------------------------------------------------------------------------- /e2e/smoke/tsconfig.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.json", "compilerOptions": { "target": "esnext", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true, "moduleResolution": "node16", "module": "Node16" }, "include": ["./test/**/*"] } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/db/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "@better-auth/core/db"; export * from "./internal-adapter"; export * from "./field"; export * from "./get-tables"; export * from "./with-hooks"; export * from "./to-zod"; export * from "./utils"; export * from "./get-migration"; export * from "./get-schema"; export * from "./schema"; ``` -------------------------------------------------------------------------------- /docs/turbo.json: -------------------------------------------------------------------------------- ```json { "$schema": "https://turborepo.org/schema.json", "extends": ["//"], "tasks": { "build": { "env": [ "GITHUB_TOKEN", "ORAMA_PRIVATE_API_KEY", "ORAMA_INDEX_ID", "NEXT_PUBLIC_ORAMA_PUBLIC_API_KEY", "NEXT_PUBLIC_ORAMA_ENDPOINT" ] } } } ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/tsconfig-exact-optional-property-types/tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "target": "ESNext", "module": "NodeNext", "outDir": "dist", "strict": true, "exactOptionalPropertyTypes": true, "skipLibCheck": true, "types": ["node"], "jsx": "react-jsx", "jsxImportSource": "hono/jsx", "moduleResolution": "node16" } } ``` -------------------------------------------------------------------------------- /e2e/integration/test-utils/src/playwright.ts: -------------------------------------------------------------------------------- ```typescript import { createRequire } from "node:module"; export const terminate = createRequire(import.meta.url)( // use terminate instead of cp.kill, // because cp.kill will not kill the child process of the child process // to avoid the zombie process "terminate/promise", ) as (pid: number) => Promise<void>; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/api/routes/index.ts: -------------------------------------------------------------------------------- ```typescript export * from "./sign-in"; export * from "./callback"; export * from "./session"; export * from "./sign-out"; export * from "./reset-password"; export * from "./email-verification"; export * from "./update-user"; export * from "./error"; export * from "./ok"; export * from "./sign-up"; export * from "./account"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/custom-session/client.ts: -------------------------------------------------------------------------------- ```typescript import { InferServerPlugin } from "../../client/plugins"; import type { BetterAuthOptions } from "../../types"; import type { Auth } from "better-auth"; export const customSessionClient = < A extends | Auth | { options: BetterAuthOptions; }, >() => { return InferServerPlugin<A, "custom-session">(); }; ``` -------------------------------------------------------------------------------- /packages/cli/src/utils/format-ms.ts: -------------------------------------------------------------------------------- ```typescript /** * Only supports up to seconds. */ export function formatMilliseconds(ms: number) { if (ms < 0) { throw new Error("Milliseconds cannot be negative"); } if (ms < 1000) { return `${ms}ms`; } const seconds = Math.floor(ms / 1000); const milliseconds = ms % 1000; return `${seconds}s ${milliseconds}ms`; } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/anonymous/client.ts: -------------------------------------------------------------------------------- ```typescript import type { anonymous } from "."; import type { BetterAuthClientPlugin } from "@better-auth/core"; export const anonymousClient = () => { return { id: "anonymous", $InferServerPlugin: {} as ReturnType<typeof anonymous>, pathMethods: { "/sign-in/anonymous": "POST", }, } satisfies BetterAuthClientPlugin; }; ``` -------------------------------------------------------------------------------- /packages/expo/tsdown.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineConfig } from "tsdown"; export default defineConfig({ dts: true, format: ["esm", "cjs"], entry: ["./src/index.ts", "./src/client.ts"], external: [ "better-auth", "better-call", "@better-fetch/fetch", "react-native", "expo-web-browser", "expo-linking", "expo-constants", ], treeshake: true, }); ``` -------------------------------------------------------------------------------- /demo/nextjs/components/ui/collapsible.tsx: -------------------------------------------------------------------------------- ```typescript "use client"; import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"; const Collapsible = CollapsiblePrimitive.Root; const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger; const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent; export { Collapsible, CollapsibleTrigger, CollapsibleContent }; ``` -------------------------------------------------------------------------------- /packages/cli/tsconfig.test.json: -------------------------------------------------------------------------------- ```json { "extends": "../../tsconfig.json", "compilerOptions": { "rootDir": ".", "outDir": "./node_modules/.cache/test", "tsBuildInfoFile": "tsconfig.test.tsbuildinfo", "lib": ["esnext", "dom", "dom.iterable"] }, "references": [ { "path": "../better-auth/tsconfig.json" } ], "include": ["test", "src"] } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/types/index.ts: -------------------------------------------------------------------------------- ```typescript export type { BetterAuthOptions, BetterAuthPlugin, BetterAuthRateLimitOptions, BetterAuthAdvancedOptions, BetterAuthCookies, } from "@better-auth/core"; export type * from "./models"; export type * from "../init"; export type * from "./plugins"; export type * from "./adapter"; export * from "../client/types"; export * from "./api"; ``` -------------------------------------------------------------------------------- /demo/nextjs/app/(auth)/sign-in/loading.tsx: -------------------------------------------------------------------------------- ```typescript export default function SignInLoading() { return ( <div className="flex min-h-screen items-center justify-center"> <div className="text-center"> <div className="h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-gray-900 mx-auto"></div> <p className="mt-4 text-gray-600">Loading...</p> </div> </div> ); } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/jwt/schema.ts: -------------------------------------------------------------------------------- ```typescript import type { BetterAuthPluginDBSchema } from "@better-auth/core/db"; export const schema = { jwks: { fields: { publicKey: { type: "string", required: true, }, privateKey: { type: "string", required: true, }, createdAt: { type: "date", required: true, }, }, }, } satisfies BetterAuthPluginDBSchema; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/magic-link/utils.ts: -------------------------------------------------------------------------------- ```typescript import { createHash } from "@better-auth/utils/hash"; import { base64Url } from "@better-auth/utils/base64"; export const defaultKeyHasher = async (otp: string) => { const hash = await createHash("SHA-256").digest( new TextEncoder().encode(otp), ); const hashed = base64Url.encode(new Uint8Array(hash), { padding: false, }); return hashed; }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/one-time-token/utils.ts: -------------------------------------------------------------------------------- ```typescript import { createHash } from "@better-auth/utils/hash"; import { base64Url } from "@better-auth/utils/base64"; export const defaultKeyHasher = async (token: string) => { const hash = await createHash("SHA-256").digest( new TextEncoder().encode(token), ); const hashed = base64Url.encode(new Uint8Array(hash), { padding: false, }); return hashed; }; ``` -------------------------------------------------------------------------------- /packages/core/src/env/index.ts: -------------------------------------------------------------------------------- ```typescript export { ENV, getEnvVar, env, nodeENV, isTest, getBooleanEnvVar, isDevelopment, type EnvObject, isProduction, } from "./env-impl"; export { getColorDepth } from "./color-depth"; export { logger, createLogger, levels, type Logger, type LogLevel, type LogHandlerParams, type InternalLogger, shouldPublishLog, TTY_COLORS, } from "./logger"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/two-factor/utils.ts: -------------------------------------------------------------------------------- ```typescript import { createHash } from "@better-auth/utils/hash"; import { base64Url } from "@better-auth/utils/base64"; export const defaultKeyHasher = async (token: string) => { const hash = await createHash("SHA-256").digest( new TextEncoder().encode(token), ); const hashed = base64Url.encode(new Uint8Array(hash), { padding: false, }); return hashed; }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/integrations/solid-start.ts: -------------------------------------------------------------------------------- ```typescript export function toSolidStartHandler( auth: | { handler: (request: Request) => Promise<Response>; } | ((request: Request) => Promise<Response>), ) { const handler = async (event: { request: Request }) => { return "handler" in auth ? auth.handler(event.request) : auth(event.request); }; return { GET: handler, POST: handler, }; } ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/src/app.tsx: -------------------------------------------------------------------------------- ```typescript // @refresh reload import { type RouteDefinition, Router } from "@solidjs/router"; import { FileRoutes } from "@solidjs/start/router"; import { Suspense } from "solid-js"; export const route: RouteDefinition = {}; export default function App() { return ( <Router root={(props) => <Suspense>{props.children}</Suspense>}> <FileRoutes /> </Router> ); } ``` -------------------------------------------------------------------------------- /e2e/integration/vanilla-node/src/main.ts: -------------------------------------------------------------------------------- ```typescript import { createAuthClient } from "better-auth/client"; const search = new URLSearchParams(window.location.search); const port = search.get("port"); const client = createAuthClient({ baseURL: `http://localhost:${port ?? 3000}`, }); declare global { interface Window { client: typeof client; } } window.client = client; document.body.innerHTML = "Ready"; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/crypto/jwt.ts: -------------------------------------------------------------------------------- ```typescript import { SignJWT } from "jose"; export async function signJWT( payload: any, secret: string, expiresIn: number = 3600, ): Promise<string> { const jwt = await new SignJWT(payload) .setProtectedHeader({ alg: "HS256" }) .setIssuedAt() .setExpirationTime(Math.floor(Date.now() / 1000) + expiresIn) .sign(new TextEncoder().encode(secret)); return jwt; } ``` -------------------------------------------------------------------------------- /demo/expo-example/src/lib/auth-client.ts: -------------------------------------------------------------------------------- ```typescript import { createAuthClient } from "better-auth/client"; import { expoClient } from "@better-auth/expo/client"; import * as SecureStore from "expo-secure-store"; export const authClient = createAuthClient({ baseURL: "http://localhost:8081", disableDefaultFetchPlugins: true, plugins: [ expoClient({ scheme: "better-auth", storage: SecureStore, }), ], }); ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "target": "ESNext", "module": "ESNext", "moduleResolution": "bundler", "allowSyntheticDefaultImports": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "solid-js", "allowJs": true, "strict": true, "noEmit": true, "types": ["vinxi/types/client"], "isolatedModules": true } } ``` -------------------------------------------------------------------------------- /demo/nextjs/middleware.ts: -------------------------------------------------------------------------------- ```typescript import { NextRequest, NextResponse } from "next/server"; import { getSessionCookie } from "better-auth/cookies"; export async function middleware(request: NextRequest) { const cookies = getSessionCookie(request); if (!cookies) { return NextResponse.redirect(new URL("/", request.url)); } return NextResponse.next(); } export const config = { matcher: ["/dashboard"], }; ``` -------------------------------------------------------------------------------- /docs/components/banner.tsx: -------------------------------------------------------------------------------- ```typescript "use client"; import { Banner } from "fumadocs-ui/components/banner"; export function BetaNotice() { return ( <Banner id="beta-notice-1" 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" > 🚧 Heads up! We're still in beta. V1 will be out by nov. 22! </Banner> ); } ``` -------------------------------------------------------------------------------- /docs/app/docs/layout.tsx: -------------------------------------------------------------------------------- ```typescript import { DocsLayout } from "@/components/docs/docs"; import type { ReactNode } from "react"; import { docsOptions } from "../layout.config"; import { AISearchTrigger } from "@/components/floating-ai-search"; export default function Layout({ children }: { children: ReactNode }) { return ( <DocsLayout {...docsOptions}> {children} <AISearchTrigger /> </DocsLayout> ); } ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/wrangler.json: -------------------------------------------------------------------------------- ```json { "$schema": "node_modules/wrangler/config-schema.json", "compatibility_date": "2025-04-01", "main": "src/index.ts", "name": "cloudflare", "upload_source_maps": true, "d1_databases": [ { "binding": "DB", "database_id": "d1-db-id", "database_name": "db", "migrations_dir": "./drizzle" } ], "observability": { "enabled": true } } ``` -------------------------------------------------------------------------------- /demo/nextjs/app/device/layout.tsx: -------------------------------------------------------------------------------- ```typescript import { auth } from "@/lib/auth"; import { headers } from "next/headers"; import { redirect } from "next/navigation"; export default async function DevicePage({ children, }: { children: React.ReactNode; }) { const session = await auth.api.getSession({ headers: await headers(), }); if (session === null) { throw redirect("/sign-in?callbackUrl=/device"); } return children; } ``` -------------------------------------------------------------------------------- /docs/lib/export-search-indexes.ts: -------------------------------------------------------------------------------- ```typescript import { source } from "@/lib/source"; import type { OramaDocument } from "fumadocs-core/search/orama-cloud"; export async function exportSearchIndexes() { return source.getPages().map((page) => { return { id: page.url, structured: page.data.structuredData, url: page.url, title: page.data.title, description: page.data.description, } satisfies OramaDocument; }); } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/email-otp/client.ts: -------------------------------------------------------------------------------- ```typescript import type { emailOTP } from "."; import type { BetterAuthClientPlugin } from "@better-auth/core"; export const emailOTPClient = () => { return { id: "email-otp", $InferServerPlugin: {} as ReturnType<typeof emailOTP>, atomListeners: [ { matcher: (path) => path === "/email-otp/verify-email", signal: "$sessionSignal", }, ], } satisfies BetterAuthClientPlugin; }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/api-key/client.ts: -------------------------------------------------------------------------------- ```typescript import type { apiKey } from "."; import type { BetterAuthClientPlugin } from "@better-auth/core"; export const apiKeyClient = () => { return { id: "api-key", $InferServerPlugin: {} as ReturnType<typeof apiKey>, pathMethods: { "/api-key/create": "POST", "/api-key/delete": "POST", "/api-key/delete-all-expired-api-keys": "POST", }, } satisfies BetterAuthClientPlugin; }; ``` -------------------------------------------------------------------------------- /e2e/integration/vanilla-node/package.json: -------------------------------------------------------------------------------- ```json { "name": "vanilla-node-e2e", "private": true, "type": "module", "scripts": { "start:client": "vite" }, "devDependencies": { "@better-auth/test-utils": "workspace:*", "vite": "^7.1.5" }, "dependencies": { "better-auth": "workspace:*", "better-sqlite3": "^12.2.0", "kysely": "^0.28.5", "kysely-postgres-js": "^3.0.0", "postgres": "^3.4.7" } } ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/vite/vite.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineConfig } from "vite"; import { resolve } from "node:path"; export default defineConfig({ build: { rollupOptions: { input: { server: resolve(__dirname, "src", "server.ts"), client: resolve(__dirname, "src", "client.ts"), }, output: { entryFileNames: "[name].js", format: "es", }, treeshake: false, }, minify: false, sourcemap: "inline", }, }); ``` -------------------------------------------------------------------------------- /e2e/integration/playwright.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineConfig, devices } from "@playwright/test"; export default defineConfig({ testMatch: "**/e2e/**/*.spec.ts", fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, reporter: "html", use: { trace: "on-first-retry", }, projects: [ { name: "chromium", use: { ...devices["Desktop Chrome"] }, }, ], }); ``` -------------------------------------------------------------------------------- /e2e/smoke/test/typecheck.spec.ts: -------------------------------------------------------------------------------- ```typescript import { test } from "node:test"; import { spawnSync } from "node:child_process"; import { fileURLToPath } from "node:url"; import { resolve } from "node:path"; const fixturesDir = fileURLToPath(new URL("./fixtures", import.meta.url)); test("typecheck", () => { spawnSync("pnpm", ["run", "typecheck"], { stdio: "inherit", cwd: resolve(fixturesDir, "tsconfig-verbatim-module-syntax-node10"), }); }); ``` -------------------------------------------------------------------------------- /packages/core/src/db/schema/rate-limit.ts: -------------------------------------------------------------------------------- ```typescript import * as z from "zod"; export const rateLimitSchema = z.object({ /** * The key to use for rate limiting */ key: z.string(), /** * The number of requests made */ count: z.number(), /** * The last request time in milliseconds */ lastRequest: z.number(), }); /** * Rate limit schema type used by better-auth for rate limiting */ export type RateLimit = z.infer<typeof rateLimitSchema>; ``` -------------------------------------------------------------------------------- /docs/app/llms.txt/route.ts: -------------------------------------------------------------------------------- ```typescript import { source } from "@/lib/source"; import { getLLMText } from "../docs/lib/get-llm-text"; export const revalidate = false; export async function GET() { const scan = source .getPages() .filter((file) => file.slugs[0] !== "openapi") .map(getLLMText); const scanned = await Promise.all(scan); return new Response(scanned.join("\n\n"), { headers: { "Content-Type": "text/markdown", }, }); } ``` -------------------------------------------------------------------------------- /docs/components/divider-text.tsx: -------------------------------------------------------------------------------- ```typescript import type { ReactNode } from "react"; export function DividerText({ children }: { children: ReactNode }) { return ( <div className="flex items-center justify-center w-full"> <div className="w-full border-b border-muted"></div> <div className="flex items-center justify-center w-full text-muted-foreground"> {children} </div> <div className="w-full border-b border-muted"></div> </div> ); } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/captcha/utils.ts: -------------------------------------------------------------------------------- ```typescript export const encodeToURLParams = (obj: Record<string, any>): string => { if (typeof obj !== "object" || obj === null || Array.isArray(obj)) { throw new Error("Input must be a non-null object."); } const params = new URLSearchParams(); for (const [key, value] of Object.entries(obj)) { if (value !== undefined && value !== null) { params.append(key, String(value)); } } return params.toString(); }; ``` -------------------------------------------------------------------------------- /packages/core/tsdown.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineConfig } from "tsdown"; export default defineConfig({ dts: true, format: ["esm", "cjs"], entry: [ "./src/index.ts", "./src/db/index.ts", "./src/db/adapter/index.ts", "./src/async_hooks/index.ts", "./src/env/index.ts", "./src/oauth2/index.ts", "./src/middleware/index.ts", "./src/social-providers/index.ts", "./src/utils/index.ts", "./src/error/index.ts", ], clean: true, }); ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/multi-session/client.ts: -------------------------------------------------------------------------------- ```typescript import type { multiSession } from "."; import type { BetterAuthClientPlugin } from "@better-auth/core"; export const multiSessionClient = () => { return { id: "multi-session", $InferServerPlugin: {} as ReturnType<typeof multiSession>, atomListeners: [ { matcher(path) { return path === "/multi-session/set-active"; }, signal: "$sessionSignal", }, ], } satisfies BetterAuthClientPlugin; }; ``` -------------------------------------------------------------------------------- /docs/components.json: -------------------------------------------------------------------------------- ```json { "$schema": "https://ui.shadcn.com/schema.json", "style": "new-york", "rsc": true, "tsx": true, "tailwind": { "config": "", "css": "app/global.css", "baseColor": "stone", "cssVariables": true, "prefix": "" }, "aliases": { "components": "@/components", "utils": "@/lib/utils", "ui": "@/components/ui", "lib": "@/lib", "hooks": "@/hooks" }, "iconLibrary": "lucide" } ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/src/lib/auth-client.ts: -------------------------------------------------------------------------------- ```typescript import { createAuthClient } from "better-auth/solid"; const search = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : new URLSearchParams(""); const port = search.get("port"); export const client = createAuthClient({ baseURL: `http://localhost:${port || 3000}`, }); export type Session = typeof client.$Infer.Session; declare global { interface Window { client: typeof client; } } ``` -------------------------------------------------------------------------------- /demo/nextjs/lib/shared.ts: -------------------------------------------------------------------------------- ```typescript import { ReadonlyURLSearchParams } from "next/navigation"; const allowedCallbackSet: ReadonlySet<string> = new Set([ "/dashboard", "/device", ]); export const getCallbackURL = ( queryParams: ReadonlyURLSearchParams, ): string => { const callbackUrl = queryParams.get("callbackUrl"); if (callbackUrl) { if (allowedCallbackSet.has(callbackUrl)) { return callbackUrl; } return "/dashboard"; } return "/dashboard"; }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/oidc-provider/utils.ts: -------------------------------------------------------------------------------- ```typescript import { base64Url } from "@better-auth/utils/base64"; import { createHash } from "@better-auth/utils/hash"; /** * Default client secret hasher using SHA-256 */ export const defaultClientSecretHasher = async (clientSecret: string) => { const hash = await createHash("SHA-256").digest( new TextEncoder().encode(clientSecret), ); const hashed = base64Url.encode(new Uint8Array(hash), { padding: false, }); return hashed; }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/clone.ts: -------------------------------------------------------------------------------- ```typescript const cloneBase = (object: any, base: any): any => { for (const key in object) { if (!object.hasOwnProperty(key)) continue; const value = object[key]; if (typeof value === "object" && value !== null) { base[key] = cloneBase(value, value.constructor()); } else { base[key] = value; } } return base; }; export const clone = <T extends object>(object: T): T => { return cloneBase(object, object.constructor()); }; ``` -------------------------------------------------------------------------------- /demo/nextjs/components/logo.tsx: -------------------------------------------------------------------------------- ```typescript import { SVGProps } from "react"; export const Logo = (props: SVGProps<any>) => { return ( <svg width="60" height="45" viewBox="0 0 60 45" fill="none" className="w-5 h-5" xmlns="http://www.w3.org/2000/svg" > <path fillRule="evenodd" clipRule="evenodd" d="M0 0H15V15H30V30H15V45H0V30V15V0ZM45 30V15H30V0H45H60V15V30V45H45H30V30H45Z" className="fill-black dark:fill-white" /> </svg> ); }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/plugin-helper.ts: -------------------------------------------------------------------------------- ```typescript import { APIError } from "better-call"; export const getEndpointResponse = async <T>(ctx: { context: { returned?: unknown; }; }) => { const returned = ctx.context.returned; if (!returned) { return null; } if (returned instanceof Response) { if (returned.status !== 200) { return null; } return (await returned.clone().json()) as T; } if (returned instanceof APIError) { return null; } return returned as T; }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/client/fetch-plugins.ts: -------------------------------------------------------------------------------- ```typescript import { type BetterFetchPlugin } from "@better-fetch/fetch"; export const redirectPlugin = { id: "redirect", name: "Redirect", hooks: { onSuccess(context) { if (context.data?.url && context.data?.redirect) { if (typeof window !== "undefined" && window.location) { if (window.location) { try { window.location.href = context.data.url; } catch {} } } } }, }, } satisfies BetterFetchPlugin; ``` -------------------------------------------------------------------------------- /demo/nextjs/components.json: -------------------------------------------------------------------------------- ```json { "$schema": "https://ui.shadcn.com/schema.json", "style": "new-york", "rsc": true, "tsx": true, "tailwind": { "config": "tailwind.config.ts", "css": "app/globals.css", "baseColor": "neutral", "cssVariables": true, "prefix": "" }, "aliases": { "components": "@/components", "utils": "@/lib/utils", "ui": "@/components/ui", "lib": "@/lib", "hooks": "@/hooks" }, "iconLibrary": "lucide" } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/client/session-atom.ts: -------------------------------------------------------------------------------- ```typescript import type { BetterFetch } from "@better-fetch/fetch"; import { atom } from "nanostores"; import { useAuthQuery } from "./query"; import type { Session, User } from "../types"; export function getSessionAtom($fetch: BetterFetch) { const $signal = atom<boolean>(false); const session = useAuthQuery<{ user: User; session: Session; }>($signal, "/get-session", $fetch, { method: "GET", }); return { session, $sessionSignal: $signal, }; } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/api/routes/sign-out.test.ts: -------------------------------------------------------------------------------- ```typescript import { describe, expect } from "vitest"; import { getTestInstance } from "../../test-utils/test-instance"; describe("sign-out", async (it) => { const { signInWithTestUser, client } = await getTestInstance(); it("should sign out", async () => { const { runWithUser } = await signInWithTestUser(); await runWithUser(async () => { const res = await client.signOut(); expect(res.data).toMatchObject({ success: true, }); }); }); }); ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "target": "esnext", "lib": ["esnext"], "module": "esnext", "outDir": "./dist", "moduleResolution": "bundler", "types": [ "@cloudflare/workers-types/2023-07-01", "@cloudflare/workers-types/experimental", "@cloudflare/vitest-pool-workers" ], "noEmit": true, "isolatedModules": true, "forceConsistentCasingInFileNames": true, "skipLibCheck": true, "strict": true } } ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/package.json: -------------------------------------------------------------------------------- ```json { "name": "better-auth-solid-vinxi-e2e", "private": true, "type": "module", "scripts": { "dev": "vinxi dev", "build": "vinxi build", "start": "vinxi start" }, "dependencies": { "@solidjs/router": "^0.15.3", "@solidjs/start": "^1.1.7", "better-auth": "workspace:*", "better-sqlite3": "^12.2.0", "solid-js": "^1.9.7", "vinxi": "^0.5.8" }, "devDependencies": { "@better-auth/test-utils": "workspace:*" } } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/phone-number/client.ts: -------------------------------------------------------------------------------- ```typescript import type { phoneNumber } from "."; import type { BetterAuthClientPlugin } from "@better-auth/core"; export const phoneNumberClient = () => { return { id: "phoneNumber", $InferServerPlugin: {} as ReturnType<typeof phoneNumber>, atomListeners: [ { matcher(path) { return ( path === "/phone-number/update" || path === "/phone-number/verify" ); }, signal: "$sessionSignal", }, ], } satisfies BetterAuthClientPlugin; }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/device-authorization/client.ts: -------------------------------------------------------------------------------- ```typescript import type { deviceAuthorization } from "."; import type { BetterAuthClientPlugin } from "@better-auth/core"; export const deviceAuthorizationClient = () => { return { id: "device-authorization", $InferServerPlugin: {} as ReturnType<typeof deviceAuthorization>, pathMethods: { "/device/code": "POST", "/device/token": "POST", "/device": "GET", "/device/approve": "POST", "/device/deny": "POST", }, } satisfies BetterAuthClientPlugin; }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/phone-number/phone-number-error.ts: -------------------------------------------------------------------------------- ```typescript import { defineErrorCodes } from "@better-auth/core/utils"; export const ERROR_CODES = defineErrorCodes({ INVALID_PHONE_NUMBER: "Invalid phone number", PHONE_NUMBER_EXIST: "Phone number already exists", INVALID_PHONE_NUMBER_OR_PASSWORD: "Invalid phone number or password", UNEXPECTED_ERROR: "Unexpected error", OTP_NOT_FOUND: "OTP not found", OTP_EXPIRED: "OTP expired", INVALID_OTP: "Invalid OTP", PHONE_NUMBER_NOT_VERIFIED: "Phone number not verified", }); ``` -------------------------------------------------------------------------------- /packages/cli/src/commands/secret.ts: -------------------------------------------------------------------------------- ```typescript import { logger } from "better-auth"; import chalk from "chalk"; import { Command } from "commander"; import Crypto from "crypto"; export const generateSecret = new Command("secret").action(() => { const secret = generateSecretHash(); logger.info(`\nAdd the following to your .env file: ${ chalk.gray("# Auth Secret") + chalk.green(`\nBETTER_AUTH_SECRET=${secret}`) }`); }); export const generateSecretHash = () => { return Crypto.randomBytes(32).toString("hex"); }; ``` -------------------------------------------------------------------------------- /packages/cli/src/generators/kysely.ts: -------------------------------------------------------------------------------- ```typescript import { getMigrations } from "better-auth/db"; import type { SchemaGenerator } from "./types"; export const generateMigrations: SchemaGenerator = async ({ options, file, }) => { const { compileMigrations } = await getMigrations(options); const migrations = await compileMigrations(); return { code: migrations.trim() === ";" ? "" : migrations, fileName: file || `./better-auth_migrations/${new Date() .toISOString() .replace(/:/g, "-")}.sql`, }; }; ``` -------------------------------------------------------------------------------- /demo/expo-example/src/lib/utils.ts: -------------------------------------------------------------------------------- ```typescript import { type ClassValue, clsx } from "clsx"; import { PressableStateCallbackType } from "react-native"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } export function isTextChildren( children: | React.ReactNode | ((state: PressableStateCallbackType) => React.ReactNode), ) { return Array.isArray(children) ? children.every((child) => typeof child === "string") : typeof children === "string"; } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/crypto/buffer.ts: -------------------------------------------------------------------------------- ```typescript /** * Compare two buffers in constant time. */ export function constantTimeEqual( a: ArrayBuffer | Uint8Array, b: ArrayBuffer | Uint8Array, ): boolean { const aBuffer = new Uint8Array(a); const bBuffer = new Uint8Array(b); let c = aBuffer.length ^ bBuffer.length; const length = Math.max(aBuffer.length, bBuffer.length); for (let i = 0; i < length; i++) { c |= (i < aBuffer.length ? aBuffer[i]! : 0) ^ (i < bBuffer.length ? bBuffer[i]! : 0); } return c === 0; } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/utils/await-object.ts: -------------------------------------------------------------------------------- ```typescript export async function awaitObject<T extends Record<string, Promise<any>>>( promises: T, ): Promise<{ [K in keyof T]: Awaited<T[K]> }> { const entries = Object.entries(promises) as [keyof T, T[keyof T]][]; const results = await Promise.all(entries.map(([, promise]) => promise)); const resolved: Partial<{ [K in keyof T]: Awaited<T[K]> }> = {}; entries.forEach(([key], index) => { resolved[key] = results[index]; }); return resolved as { [K in keyof T]: Awaited<T[K]> }; } ``` -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- ```yaml packages: - packages/** - docs - demo/* - e2e/** catalog: '@better-fetch/fetch': 1.1.18 better-call: 1.0.19 typescript: ^5.9.2 tsdown: ^0.15.6 vitest: ^3.2.4 catalogs: react18: '@types/react': ^19.1.12 '@types/react-dom': ^19.1.9 react: 19.1.1 react-dom: 19.1.1 neverBuiltDependencies: [] overrides: brace-expansion@>=1.0.0 <=1.1.11: '>=1.1.12' cookie@<0.7.0: '>=0.7.0' esbuild@<=0.24.2: '>=0.25.0' miniflare>zod: ^3.25.1 zod: ^4.1.5 ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "strict": true, "target": "esnext", "module": "esnext", "moduleResolution": "bundler", "downlevelIteration": true, "baseUrl": ".", "esModuleInterop": true, "skipLibCheck": true, "verbatimModuleSyntax": true, "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": false, "incremental": true, "noErrorTruncation": true, "types": ["node", "bun"] }, "exclude": ["**/dist/**", "**/node_modules/**"] } ``` -------------------------------------------------------------------------------- /docs/components/logo.tsx: -------------------------------------------------------------------------------- ```typescript import { SVGProps } from "react"; import { cn } from "@/lib/utils"; export const Logo = (props: SVGProps<any>) => { return ( <svg width="60" height="45" viewBox="0 0 60 45" fill="none" className={cn("w-5 h-5", props.className)} xmlns="http://www.w3.org/2000/svg" > <path fillRule="evenodd" clipRule="evenodd" d="M0 0H15V15H30V30H15V45H0V30V15V0ZM45 30V15H30V0H45H60V15V30V45H45H30V30H45Z" className="fill-black dark:fill-white" /> </svg> ); }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/adapters/prisma-adapter/test/push-prisma-schema.ts: -------------------------------------------------------------------------------- ```typescript import { execSync } from "node:child_process"; import { createRequire } from "node:module"; import { join } from "node:path"; export async function pushPrismaSchema( dialect: "sqlite" | "postgresql" | "mysql", ) { const node = process.execPath; const cli = createRequire(import.meta.url).resolve("prisma"); execSync(`${node} ${cli} db push --schema ./schema-${dialect}.prisma`, { stdio: "ignore", // use `inherit` if you want to see the output cwd: join(import.meta.dirname), }); } ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/src/entry-server.tsx: -------------------------------------------------------------------------------- ```typescript // @refresh reload import { createHandler, StartServer } from "@solidjs/start/server"; export default createHandler(() => ( <StartServer document={({ assets, children, scripts }) => ( <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="icon" href="/favicon.ico" /> {assets} </head> <body> <div id="app">{children}</div> {scripts} </body> </html> )} /> )); ``` -------------------------------------------------------------------------------- /packages/telemetry/src/detectors/detect-project-info.ts: -------------------------------------------------------------------------------- ```typescript // https://github.com/zkochan/packages/blob/main/which-pm-runs/index.js import { env } from "@better-auth/core/env"; export function detectPackageManager() { const userAgent = env.npm_config_user_agent; if (!userAgent) { return undefined; } const pmSpec = userAgent.split(" ")[0]!; const separatorPos = pmSpec.lastIndexOf("/"); const name = pmSpec.substring(0, separatorPos); return { name: name === "npminstall" ? "cnpm" : name, version: pmSpec.substring(separatorPos + 1), }; } ``` -------------------------------------------------------------------------------- /packages/core/src/db/schema/verification.ts: -------------------------------------------------------------------------------- ```typescript import * as z from "zod"; import { coreSchema } from "./shared"; export const verificationSchema = coreSchema.extend({ value: z.string(), expiresAt: z.date(), identifier: z.string(), }); /** * Verification schema type used by better-auth, note that it's possible that verification could have additional fields * * todo: we should use generics to extend this type with additional fields from plugins and options in the future */ export type Verification = z.infer<typeof verificationSchema>; ``` -------------------------------------------------------------------------------- /packages/core/src/types/index.ts: -------------------------------------------------------------------------------- ```typescript export type * from "./helper"; export type { BetterAuthAdvancedOptions, GenerateIdFn, BetterAuthRateLimitOptions, BetterAuthOptions, } from "./init-options"; export type { BetterAuthCookies } from "./cookie"; export type { AuthContext, GenericEndpointContext, InternalAdapter, } from "./context"; export type { BetterAuthPlugin, HookEndpointContext } from "./plugin"; export type { BetterAuthClientPlugin, BetterAuthClientOptions, ClientStore, ClientAtomListener, } from "./plugin-client"; ``` -------------------------------------------------------------------------------- /docs/app/blog/_components/fmt-dates.tsx: -------------------------------------------------------------------------------- ```typescript import { cn } from "@/lib/utils"; const dateFormatter = new Intl.DateTimeFormat("en-US", { year: "numeric", month: "short", day: "numeric", timeZone: "UTC", }); export function FormattedDate({ date, ...props }: React.ComponentPropsWithoutRef<"time"> & { date: string | Date }) { date = typeof date === "string" ? new Date(date) : date; return ( <time className={cn(props.className, "")} dateTime={date.toISOString()} {...props} > {dateFormatter.format(date)} </time> ); } ``` -------------------------------------------------------------------------------- /docs/app/changelogs/_components/fmt-dates.tsx: -------------------------------------------------------------------------------- ```typescript import { cn } from "@/lib/utils"; const dateFormatter = new Intl.DateTimeFormat("en-US", { year: "numeric", month: "short", day: "numeric", timeZone: "UTC", }); export function FormattedDate({ date, ...props }: React.ComponentPropsWithoutRef<"time"> & { date: string | Date }) { date = typeof date === "string" ? new Date(date) : date; return ( <time className={cn(props.className, "")} dateTime={date.toISOString()} {...props} > {dateFormatter.format(date)} </time> ); } ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/bun-simple.ts: -------------------------------------------------------------------------------- ```typescript import { betterAuth } from "better-auth"; import Database from "bun:sqlite"; import { getMigrations } from "better-auth/db"; const database = new Database(":memory:"); export const auth = betterAuth({ baseURL: "http://localhost:4000", database, emailAndPassword: { enabled: true, }, logger: { level: "debug", }, }); const { runMigrations } = await getMigrations(auth.options); await runMigrations(); const server = Bun.serve({ fetch: auth.handler, port: 0, }); console.log(server.port); ``` -------------------------------------------------------------------------------- /docs/components/resource-grid.tsx: -------------------------------------------------------------------------------- ```typescript import { cn } from "@/lib/utils"; import { ResourceCard } from "./resource-card"; interface ResourceGridProps { resources: { title: string; description: string; href: string; tags?: string[]; }[]; className?: string; } export function ResourceGrid({ resources, className }: ResourceGridProps) { return ( <div className={cn("grid gap-4 sm:grid-cols-2 lg:grid-cols-3", className)}> {resources.map((resource) => ( <ResourceCard key={resource.href} {...resource} /> ))} </div> ); } ``` -------------------------------------------------------------------------------- /docs/components/docs/shared.tsx: -------------------------------------------------------------------------------- ```typescript import type { ReactNode } from "react"; import { Slot } from "@radix-ui/react-slot"; export interface BaseLayoutProps { children?: ReactNode; } export function replaceOrDefault( obj: | { enabled?: boolean; component?: ReactNode; } | undefined, def: ReactNode, customComponentProps?: object, disabled?: ReactNode, ): ReactNode { if (obj?.enabled === false) return disabled; if (obj?.component !== undefined) return <Slot {...customComponentProps}>{obj.component}</Slot>; return def; } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/test-utils/state.ts: -------------------------------------------------------------------------------- ```typescript import * as fs from "fs"; import path from "path"; export type State = "IDLE" | "RUNNING"; export function makeTestState(dirname: string) { const stateFilePath = path.join(dirname, "./state.txt"); function getState(): State { try { return fs .readFileSync(stateFilePath, "utf-8") .split("\n")[0]! .trim() as State; } catch { return "IDLE"; } } function setState(state: State) { fs.writeFileSync(stateFilePath, state, "utf-8"); } return { stateFilePath, getState, setState }; } ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/captcha/error-codes.ts: -------------------------------------------------------------------------------- ```typescript // These error codes are returned by the API import { defineErrorCodes } from "@better-auth/core/utils"; export const EXTERNAL_ERROR_CODES = defineErrorCodes({ VERIFICATION_FAILED: "Captcha verification failed", MISSING_RESPONSE: "Missing CAPTCHA response", UNKNOWN_ERROR: "Something went wrong", }); // These error codes are only visible in the server logs export const INTERNAL_ERROR_CODES = defineErrorCodes({ MISSING_SECRET_KEY: "Missing secret key", SERVICE_UNAVAILABLE: "CAPTCHA service unavailable", }); ``` -------------------------------------------------------------------------------- /demo/nextjs/components/theme-toggle.tsx: -------------------------------------------------------------------------------- ```typescript "use client"; import { Moon, Sun } from "lucide-react"; import { useTheme } from "next-themes"; import { Button } from "@/components/ui/button"; export function ThemeToggle() { const { setTheme, theme } = useTheme(); return ( <Button variant="ghost" size="icon" onClick={() => setTheme(theme === "light" ? "dark" : "light")} > <Sun className="h-6 w-[1.3rem] dark:hidden" color="#000" /> <Moon className="hidden h-5 w-5 dark:block" /> <span className="sr-only">Toggle theme</span> </Button> ); } ``` -------------------------------------------------------------------------------- /docs/proxy.ts: -------------------------------------------------------------------------------- ```typescript import { NextRequest, NextResponse } from "next/server"; import { isMarkdownPreferred, rewritePath } from "fumadocs-core/negotiation"; const { rewrite: rewriteLLM } = rewritePath("/docs/*path", "/llms.txt/*path"); export function proxy(request: NextRequest) { if (isMarkdownPreferred(request)) { const result = rewriteLLM(request.nextUrl.pathname); if (result) { return NextResponse.rewrite(new URL(result, request.nextUrl)); } } return NextResponse.next(); } export const config = { matcher: "/docs/:path*", }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/username/error-codes.ts: -------------------------------------------------------------------------------- ```typescript import { defineErrorCodes } from "@better-auth/core/utils"; export const USERNAME_ERROR_CODES = defineErrorCodes({ INVALID_USERNAME_OR_PASSWORD: "Invalid username or password", EMAIL_NOT_VERIFIED: "Email not verified", UNEXPECTED_ERROR: "Unexpected error", USERNAME_IS_ALREADY_TAKEN: "Username is already taken. Please try another.", USERNAME_TOO_SHORT: "Username is too short", USERNAME_TOO_LONG: "Username is too long", INVALID_USERNAME: "Username is invalid", INVALID_DISPLAY_USERNAME: "Display username is invalid", }); ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/siwe/schema.ts: -------------------------------------------------------------------------------- ```typescript import type { BetterAuthPluginDBSchema } from "@better-auth/core/db"; export const schema = { walletAddress: { fields: { userId: { type: "string", references: { model: "user", field: "id", }, required: true, }, address: { type: "string", required: true, }, chainId: { type: "number", required: true, }, isPrimary: { type: "boolean", defaultValue: false, }, createdAt: { type: "date", required: true, }, }, }, } satisfies BetterAuthPluginDBSchema; ``` -------------------------------------------------------------------------------- /packages/cli/src/utils/check-package-managers.ts: -------------------------------------------------------------------------------- ```typescript import { exec } from "child_process"; function checkCommand(command: string): Promise<boolean> { return new Promise((resolve) => { exec(`${command} --version`, (error) => { if (error) { resolve(false); // Command not found or error occurred } else { resolve(true); // Command exists } }); }); } export async function checkPackageManagers(): Promise<{ hasPnpm: boolean; hasBun: boolean; }> { const hasPnpm = await checkCommand("pnpm"); const hasBun = await checkCommand("bun"); return { hasPnpm, hasBun, }; } ``` -------------------------------------------------------------------------------- /packages/core/src/db/schema/user.ts: -------------------------------------------------------------------------------- ```typescript import * as z from "zod"; import { coreSchema } from "./shared"; export const userSchema = coreSchema.extend({ email: z.string().transform((val) => val.toLowerCase()), emailVerified: z.boolean().default(false), name: z.string(), image: z.string().nullish(), }); /** * User schema type used by better-auth, note that it's possible that user could have additional fields * * todo: we should use generics to extend this type with additional fields from plugins and options in the future */ export type User = z.infer<typeof userSchema>; ``` -------------------------------------------------------------------------------- /packages/core/src/db/schema/session.ts: -------------------------------------------------------------------------------- ```typescript import * as z from "zod"; import { coreSchema } from "./shared"; export const sessionSchema = coreSchema.extend({ userId: z.coerce.string(), expiresAt: z.date(), token: z.string(), ipAddress: z.string().nullish(), userAgent: z.string().nullish(), }); /** * Session schema type used by better-auth, note that it's possible that session could have additional fields * * todo: we should use generics to extend this type with additional fields from plugins and options in the future */ export type Session = z.infer<typeof sessionSchema>; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/email-otp/utils.ts: -------------------------------------------------------------------------------- ```typescript import { createHash } from "@better-auth/utils/hash"; import { base64Url } from "@better-auth/utils/base64"; export const defaultKeyHasher = async (otp: string) => { const hash = await createHash("SHA-256").digest( new TextEncoder().encode(otp), ); const hashed = base64Url.encode(new Uint8Array(hash), { padding: false, }); return hashed; }; export function splitAtLastColon(input: string): [string, string] { const idx = input.lastIndexOf(":"); if (idx === -1) { return [input, ""]; } return [input.slice(0, idx), input.slice(idx + 1)]; } ``` -------------------------------------------------------------------------------- /demo/expo-example/src/lib/auth.ts: -------------------------------------------------------------------------------- ```typescript import { betterAuth } from "better-auth"; import { expo } from "@better-auth/expo"; import { Pool } from "pg"; export const auth = betterAuth({ database: new Pool({ connectionString: process.env.DATABASE_URL, }), emailAndPassword: { enabled: true, }, plugins: [expo()], socialProviders: { github: { clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET!, }, google: { clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, }, }, trustedOrigins: ["exp://"], }); ``` -------------------------------------------------------------------------------- /docs/hooks/use-mobile.ts: -------------------------------------------------------------------------------- ```typescript import * as React from "react"; const MOBILE_BREAKPOINT = 768; export function useIsMobile() { const [isMobile, setIsMobile] = React.useState<boolean | undefined>( undefined, ); React.useEffect(() => { const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); const onChange = () => { setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); }; mql.addEventListener("change", onChange); setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); return () => mql.removeEventListener("change", onChange); }, []); return !!isMobile; } ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/deno-simple.ts: -------------------------------------------------------------------------------- ```typescript import { betterAuth } from "better-auth"; import { DatabaseSync } from "node:sqlite"; import { getMigrations } from "better-auth/db"; const database = new DatabaseSync(":memory:"); export const auth = betterAuth({ baseURL: "http://localhost:4000", database, emailAndPassword: { enabled: true, }, logger: { level: "debug", }, }); const { runMigrations } = await getMigrations(auth.options); await runMigrations(); Deno.serve( { port: 0, onListen: ({ port }) => { console.log(`Listening on http://localhost:${port}`); }, }, auth.handler, ); ``` -------------------------------------------------------------------------------- /packages/better-auth/src/plugins/two-factor/error-code.ts: -------------------------------------------------------------------------------- ```typescript import { defineErrorCodes } from "@better-auth/core/utils"; export const TWO_FACTOR_ERROR_CODES = defineErrorCodes({ OTP_NOT_ENABLED: "OTP not enabled", OTP_HAS_EXPIRED: "OTP has expired", TOTP_NOT_ENABLED: "TOTP not enabled", TWO_FACTOR_NOT_ENABLED: "Two factor isn't enabled", BACKUP_CODES_NOT_ENABLED: "Backup codes aren't enabled", INVALID_BACKUP_CODE: "Invalid backup code", INVALID_CODE: "Invalid code", TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE: "Too many attempts. Please request a new code.", INVALID_TWO_FACTOR_COOKIE: "Invalid two factor cookie", }); ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/src/lib/auth.ts: -------------------------------------------------------------------------------- ```typescript import { betterAuth } from "better-auth"; import Database from "better-sqlite3"; import { getMigrations } from "better-auth/db"; const database = new Database(":memory:"); const baseURL = process.env.BASE_URL || "http://localhost:3000"; export const auth = betterAuth({ database, baseURL, emailAndPassword: { enabled: true, }, }); const { runMigrations } = await getMigrations(auth.options); await runMigrations(); // Create an example user await auth.api.signUpEmail({ body: { name: "Test User", email: "[email protected]", password: "password123", }, }); ``` -------------------------------------------------------------------------------- /docs/lib/source.ts: -------------------------------------------------------------------------------- ```typescript import { changelogCollection, docs, blogCollection } from "@/.source"; import { getPageTree } from "@/components/sidebar-content"; import { loader } from "fumadocs-core/source"; import { createMDXSource } from "fumadocs-mdx"; export let source = loader({ baseUrl: "/docs", source: docs.toFumadocsSource(), }); source = { ...source, pageTree: getPageTree() }; export const changelogs = loader({ baseUrl: "/changelogs", source: createMDXSource(changelogCollection), }); export const blogs = loader({ baseUrl: "/blogs", source: createMDXSource(blogCollection), }); ``` -------------------------------------------------------------------------------- /e2e/integration/solid-vinxi/src/routes/[...404].tsx: -------------------------------------------------------------------------------- ```typescript import { A } from "@solidjs/router"; export default function NotFound() { return ( <main class="text-center mx-auto text-gray-700 p-4"> <h1 class="max-6-xs text-6xl text-sky-700 font-thin uppercase my-16"> Not Found </h1> <p class="mt-8"> Visit{" "} <a href="https://solidjs.com" target="_blank" class="text-sky-600 hover:underline" > solidjs.com </a>{" "} to learn how to build Solid apps. </p> <p class="my-4"> <A href="/" class="text-sky-600 hover:underline"> Home </A> </p> </main> ); } ``` -------------------------------------------------------------------------------- /packages/core/src/oauth2/index.ts: -------------------------------------------------------------------------------- ```typescript export type { OAuth2Tokens, OAuthProvider, OAuth2UserInfo, ProviderOptions, } from "./oauth-provider"; export { generateCodeChallenge, getOAuth2Tokens } from "./utils"; export { createAuthorizationURL } from "./create-authorization-url"; export { createAuthorizationCodeRequest, validateAuthorizationCode, validateToken, } from "./validate-authorization-code"; export { createRefreshAccessTokenRequest, refreshAccessToken, } from "./refresh-access-token"; export { clientCredentialsToken, createClientCredentialsTokenRequest, } from "./client-credentials-token"; ``` -------------------------------------------------------------------------------- /packages/telemetry/src/detectors/detect-framework.ts: -------------------------------------------------------------------------------- ```typescript import { getPackageVersion } from "../utils/package-json"; const FRAMEWORKS: Record<string, string> = { next: "next", nuxt: "nuxt", "@remix-run/server-runtime": "remix", astro: "astro", "@sveltejs/kit": "sveltekit", "solid-start": "solid-start", "tanstack-start": "tanstack-start", hono: "hono", express: "express", elysia: "elysia", expo: "expo", }; export async function detectFramework() { for (const [pkg, name] of Object.entries(FRAMEWORKS)) { const version = await getPackageVersion(pkg); if (version) return { name, version }; } return undefined; } ``` -------------------------------------------------------------------------------- /docs/content/docs/integrations/solid-start.mdx: -------------------------------------------------------------------------------- ```markdown --- title: SolidStart Integration description: Integrate Better Auth with SolidStart. --- Before you start, make sure you have a Better Auth instance configured. If you haven't done that yet, check out the [installation](/docs/installation). ### Mount the handler We need to mount the handler to SolidStart server. Put the following code in your `*auth.ts` file inside `/routes/api/auth` folder. ```ts title="*auth.ts" import { auth } from "~/lib/auth"; import { toSolidStartHandler } from "better-auth/solid-start"; export const { GET, POST } = toSolidStartHandler(auth); ``` ``` -------------------------------------------------------------------------------- /packages/better-auth/src/client/plugins/infer-plugin.ts: -------------------------------------------------------------------------------- ```typescript import type { BetterAuthClientPlugin } from "@better-auth/core"; import type { BetterAuthOptions } from "@better-auth/core"; export const InferServerPlugin = < AuthOrOption extends | BetterAuthOptions | { options: BetterAuthOptions; }, ID extends string, >() => { type Option = AuthOrOption extends { options: infer O } ? O : AuthOrOption; type Plugin = Option["plugins"] extends Array<infer P> ? P extends { id: ID; } ? P : never : never; return { id: "infer-server-plugin", $InferServerPlugin: {} as Plugin, } satisfies BetterAuthClientPlugin; }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/oauth2/utils.ts: -------------------------------------------------------------------------------- ```typescript import { symmetricDecrypt, symmetricEncrypt } from "../crypto"; import type { AuthContext } from "@better-auth/core"; export function decryptOAuthToken(token: string, ctx: AuthContext) { if (!token) return token; if (ctx.options.account?.encryptOAuthTokens) { return symmetricDecrypt({ key: ctx.secret, data: token, }); } return token; } export function setTokenUtil( token: string | null | undefined, ctx: AuthContext, ) { if (ctx.options.account?.encryptOAuthTokens && token) { return symmetricEncrypt({ key: ctx.secret, data: token, }); } return token; } ``` -------------------------------------------------------------------------------- /demo/nextjs/components/ui/label.tsx: -------------------------------------------------------------------------------- ```typescript "use client"; import * as React from "react"; import * as LabelPrimitive from "@radix-ui/react-label"; import { cn } from "@/lib/utils"; function Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) { return ( <LabelPrimitive.Root data-slot="label" className={cn( "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", className, )} {...props} /> ); } export { Label }; ``` -------------------------------------------------------------------------------- /docs/components/ui/label.tsx: -------------------------------------------------------------------------------- ```typescript "use client"; import * as React from "react"; import * as LabelPrimitive from "@radix-ui/react-label"; import { cn } from "@/lib/utils"; function Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) { return ( <LabelPrimitive.Root data-slot="label" className={cn( "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", className, )} {...props} /> ); } export { Label }; ``` -------------------------------------------------------------------------------- /packages/better-auth/src/client/index.ts: -------------------------------------------------------------------------------- ```typescript import type { BetterAuthOptions, BetterAuthPlugin } from "@better-auth/core"; import type { BetterAuthClientPlugin } from "@better-auth/core"; export * from "./vanilla"; export * from "./query"; export * from "./types"; export const InferPlugin = <T extends BetterAuthPlugin>() => { return { id: "infer-server-plugin", $InferServerPlugin: {} as T, } satisfies BetterAuthClientPlugin; }; export function InferAuth<O extends { options: BetterAuthOptions }>() { return {} as O["options"]; } // @ts-expect-error export type * from "nanostores"; export type * from "@better-fetch/fetch"; ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/package.json: -------------------------------------------------------------------------------- ```json { "name": "cloudflare", "private": true, "dependencies": { "better-auth": "workspace:*", "drizzle-orm": "^0.44.5", "hono": "^4.9.7" }, "devDependencies": { "@cloudflare/vitest-pool-workers": "^0.8.69", "@cloudflare/workers-types": "^4.20250903.0", "drizzle-kit": "^0.31.4", "wrangler": "4.33.2" }, "scripts": { "check": "tsc && wrangler deploy --dry-run", "dev": "wrangler dev", "migrate:local": "wrangler d1 migrations apply db --local", "cf-typegen": "wrangler types --env-interface CloudflareBindings", "e2e:smoke": "vitest" } } ``` -------------------------------------------------------------------------------- /e2e/integration/vanilla-node/tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "target": "ES2022", "useDefineForClassFields": true, "module": "ESNext", "lib": ["ES2022", "DOM", "DOM.Iterable"], "skipLibCheck": true, "moduleResolution": "bundler", "allowImportingTsExtensions": true, "verbatimModuleSyntax": true, "esModuleInterop": true, "moduleDetection": "force", "noEmit": true, "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "erasableSyntaxOnly": true, "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true }, "include": ["src", "e2e"] } ``` -------------------------------------------------------------------------------- /e2e/smoke/test/fixtures/cloudflare/vitest.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineWorkersProject, readD1Migrations, } from "@cloudflare/vitest-pool-workers/config"; import path from "node:path"; export default defineWorkersProject(async () => { const migrationsPath = path.join(__dirname, "drizzle"); const migrations = await readD1Migrations(migrationsPath); return { test: { setupFiles: ["./test/apply-migrations.ts"], poolOptions: { workers: { singleWorker: true, wrangler: { configPath: "./wrangler.json" }, miniflare: { d1Databases: ["DB"], bindings: { TEST_MIGRATIONS: migrations }, }, }, }, }, }; }); ``` -------------------------------------------------------------------------------- /packages/telemetry/src/detectors/detect-database.ts: -------------------------------------------------------------------------------- ```typescript import { getPackageVersion } from "../utils/package-json"; import type { DetectionInfo } from "../types"; const DATABASES: Record<string, string> = { pg: "postgresql", mysql: "mysql", mariadb: "mariadb", sqlite3: "sqlite", "better-sqlite3": "sqlite", "@prisma/client": "prisma", mongoose: "mongodb", mongodb: "mongodb", "drizzle-orm": "drizzle", }; export async function detectDatabase(): Promise<DetectionInfo | undefined> { for (const [pkg, name] of Object.entries(DATABASES)) { const version = await getPackageVersion(pkg); if (version) return { name, version }; } return undefined; } ``` -------------------------------------------------------------------------------- /demo/nextjs/turbo.json: -------------------------------------------------------------------------------- ```json { "$schema": "https://turborepo.org/schema.json", "extends": ["//"], "tasks": { "build": { "env": [ "TURSO_DATABASE_URL", "TURSO_AUTH_TOKEN", "RESEND_API_KEY", "BETTER_AUTH_EMAIL", "BETTER_AUTH_SECRET", "BETTER_AUTH_URL", "GITHUB_CLIENT_SECRET", "GITHUB_CLIENT_ID", "GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET", "DISCORD_CLIENT_ID", "DISCORD_CLIENT_SECRET", "MICROSOFT_CLIENT_ID", "MICROSOFT_CLIENT_SECRET", "STRIPE_KEY", "STRIPE_WEBHOOK_SECRET" ] } } } ```