#
tokens: 49666/50000 50/1093 files (page 5/68)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 5 of 68. Use http://codebase.md/better-auth/better-auth?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .gitattributes
├── .github
│   ├── CODEOWNERS
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.yml
│   │   └── feature_request.yml
│   ├── renovate.json5
│   └── workflows
│       ├── ci.yml
│       ├── e2e.yml
│       ├── preview.yml
│       └── release.yml
├── .gitignore
├── .npmrc
├── .nvmrc
├── .vscode
│   └── settings.json
├── banner-dark.png
├── banner.png
├── biome.json
├── bump.config.ts
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── demo
│   ├── expo-example
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── app.config.ts
│   │   ├── assets
│   │   │   ├── bg-image.jpeg
│   │   │   ├── fonts
│   │   │   │   └── SpaceMono-Regular.ttf
│   │   │   ├── icon.png
│   │   │   └── images
│   │   │       ├── adaptive-icon.png
│   │   │       ├── favicon.png
│   │   │       ├── logo.png
│   │   │       ├── partial-react-logo.png
│   │   │       ├── react-logo.png
│   │   │       ├── [email protected]
│   │   │       ├── [email protected]
│   │   │       └── splash.png
│   │   ├── babel.config.js
│   │   ├── components.json
│   │   ├── expo-env.d.ts
│   │   ├── index.ts
│   │   ├── metro.config.js
│   │   ├── nativewind-env.d.ts
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── app
│   │   │   │   ├── _layout.tsx
│   │   │   │   ├── api
│   │   │   │   │   └── auth
│   │   │   │   │       └── [...route]+api.ts
│   │   │   │   ├── dashboard.tsx
│   │   │   │   ├── forget-password.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   └── sign-up.tsx
│   │   │   ├── components
│   │   │   │   ├── icons
│   │   │   │   │   └── google.tsx
│   │   │   │   └── ui
│   │   │   │       ├── avatar.tsx
│   │   │   │       ├── button.tsx
│   │   │   │       ├── card.tsx
│   │   │   │       ├── dialog.tsx
│   │   │   │       ├── input.tsx
│   │   │   │       ├── separator.tsx
│   │   │   │       └── text.tsx
│   │   │   ├── global.css
│   │   │   └── lib
│   │   │       ├── auth-client.ts
│   │   │       ├── auth.ts
│   │   │       ├── icons
│   │   │       │   ├── iconWithClassName.ts
│   │   │       │   └── X.tsx
│   │   │       └── utils.ts
│   │   ├── tailwind.config.js
│   │   └── tsconfig.json
│   └── nextjs
│       ├── .env.example
│       ├── .gitignore
│       ├── app
│       │   ├── (auth)
│       │   │   ├── forget-password
│       │   │   │   └── page.tsx
│       │   │   ├── reset-password
│       │   │   │   └── page.tsx
│       │   │   ├── sign-in
│       │   │   │   ├── loading.tsx
│       │   │   │   └── page.tsx
│       │   │   └── two-factor
│       │   │       ├── otp
│       │   │       │   └── page.tsx
│       │   │       └── page.tsx
│       │   ├── accept-invitation
│       │   │   └── [id]
│       │   │       ├── invitation-error.tsx
│       │   │       └── page.tsx
│       │   ├── admin
│       │   │   └── page.tsx
│       │   ├── api
│       │   │   └── auth
│       │   │       └── [...all]
│       │   │           └── route.ts
│       │   ├── apps
│       │   │   └── register
│       │   │       └── page.tsx
│       │   ├── client-test
│       │   │   └── page.tsx
│       │   ├── dashboard
│       │   │   ├── change-plan.tsx
│       │   │   ├── client.tsx
│       │   │   ├── organization-card.tsx
│       │   │   ├── page.tsx
│       │   │   ├── upgrade-button.tsx
│       │   │   └── user-card.tsx
│       │   ├── device
│       │   │   ├── approve
│       │   │   │   └── page.tsx
│       │   │   ├── denied
│       │   │   │   └── page.tsx
│       │   │   ├── layout.tsx
│       │   │   ├── page.tsx
│       │   │   └── success
│       │   │       └── page.tsx
│       │   ├── favicon.ico
│       │   ├── features.tsx
│       │   ├── fonts
│       │   │   ├── GeistMonoVF.woff
│       │   │   └── GeistVF.woff
│       │   ├── globals.css
│       │   ├── layout.tsx
│       │   ├── oauth
│       │   │   └── authorize
│       │   │       ├── concet-buttons.tsx
│       │   │       └── page.tsx
│       │   ├── page.tsx
│       │   └── pricing
│       │       └── page.tsx
│       ├── components
│       │   ├── account-switch.tsx
│       │   ├── blocks
│       │   │   └── pricing.tsx
│       │   ├── logo.tsx
│       │   ├── one-tap.tsx
│       │   ├── sign-in-btn.tsx
│       │   ├── sign-in.tsx
│       │   ├── sign-up.tsx
│       │   ├── theme-provider.tsx
│       │   ├── theme-toggle.tsx
│       │   ├── tier-labels.tsx
│       │   ├── ui
│       │   │   ├── accordion.tsx
│       │   │   ├── alert-dialog.tsx
│       │   │   ├── alert.tsx
│       │   │   ├── aspect-ratio.tsx
│       │   │   ├── avatar.tsx
│       │   │   ├── badge.tsx
│       │   │   ├── breadcrumb.tsx
│       │   │   ├── button.tsx
│       │   │   ├── calendar.tsx
│       │   │   ├── card.tsx
│       │   │   ├── carousel.tsx
│       │   │   ├── chart.tsx
│       │   │   ├── checkbox.tsx
│       │   │   ├── collapsible.tsx
│       │   │   ├── command.tsx
│       │   │   ├── context-menu.tsx
│       │   │   ├── copy-button.tsx
│       │   │   ├── dialog.tsx
│       │   │   ├── drawer.tsx
│       │   │   ├── dropdown-menu.tsx
│       │   │   ├── form.tsx
│       │   │   ├── hover-card.tsx
│       │   │   ├── input-otp.tsx
│       │   │   ├── input.tsx
│       │   │   ├── label.tsx
│       │   │   ├── menubar.tsx
│       │   │   ├── navigation-menu.tsx
│       │   │   ├── pagination.tsx
│       │   │   ├── password-input.tsx
│       │   │   ├── popover.tsx
│       │   │   ├── progress.tsx
│       │   │   ├── radio-group.tsx
│       │   │   ├── resizable.tsx
│       │   │   ├── scroll-area.tsx
│       │   │   ├── select.tsx
│       │   │   ├── separator.tsx
│       │   │   ├── sheet.tsx
│       │   │   ├── skeleton.tsx
│       │   │   ├── slider.tsx
│       │   │   ├── sonner.tsx
│       │   │   ├── switch.tsx
│       │   │   ├── table.tsx
│       │   │   ├── tabs.tsx
│       │   │   ├── tabs2.tsx
│       │   │   ├── textarea.tsx
│       │   │   ├── toast.tsx
│       │   │   ├── toaster.tsx
│       │   │   ├── toggle-group.tsx
│       │   │   ├── toggle.tsx
│       │   │   └── tooltip.tsx
│       │   └── wrapper.tsx
│       ├── components.json
│       ├── hooks
│       │   └── use-toast.ts
│       ├── lib
│       │   ├── auth-client.ts
│       │   ├── auth-types.ts
│       │   ├── auth.ts
│       │   ├── email
│       │   │   ├── invitation.tsx
│       │   │   ├── resend.ts
│       │   │   └── reset-password.tsx
│       │   ├── metadata.ts
│       │   ├── shared.ts
│       │   └── utils.ts
│       ├── next.config.ts
│       ├── package.json
│       ├── postcss.config.mjs
│       ├── proxy.ts
│       ├── public
│       │   ├── __og.png
│       │   ├── _og.png
│       │   ├── favicon
│       │   │   ├── android-chrome-192x192.png
│       │   │   ├── android-chrome-512x512.png
│       │   │   ├── apple-touch-icon.png
│       │   │   ├── favicon-16x16.png
│       │   │   ├── favicon-32x32.png
│       │   │   ├── favicon.ico
│       │   │   ├── light
│       │   │   │   ├── android-chrome-192x192.png
│       │   │   │   ├── android-chrome-512x512.png
│       │   │   │   ├── apple-touch-icon.png
│       │   │   │   ├── favicon-16x16.png
│       │   │   │   ├── favicon-32x32.png
│       │   │   │   ├── favicon.ico
│       │   │   │   └── site.webmanifest
│       │   │   └── site.webmanifest
│       │   ├── logo.svg
│       │   └── og.png
│       ├── README.md
│       ├── tailwind.config.ts
│       ├── tsconfig.json
│       └── turbo.json
├── docker-compose.yml
├── docs
│   ├── .env.example
│   ├── .gitignore
│   ├── app
│   │   ├── api
│   │   │   ├── ai-chat
│   │   │   │   └── route.ts
│   │   │   ├── analytics
│   │   │   │   ├── conversation
│   │   │   │   │   └── route.ts
│   │   │   │   ├── event
│   │   │   │   │   └── route.ts
│   │   │   │   └── feedback
│   │   │   │       └── route.ts
│   │   │   ├── chat
│   │   │   │   └── route.ts
│   │   │   ├── og
│   │   │   │   └── route.tsx
│   │   │   ├── og-release
│   │   │   │   └── route.tsx
│   │   │   ├── search
│   │   │   │   └── route.ts
│   │   │   └── support
│   │   │       └── route.ts
│   │   ├── blog
│   │   │   ├── _components
│   │   │   │   ├── _layout.tsx
│   │   │   │   ├── blog-list.tsx
│   │   │   │   ├── changelog-layout.tsx
│   │   │   │   ├── default-changelog.tsx
│   │   │   │   ├── fmt-dates.tsx
│   │   │   │   ├── icons.tsx
│   │   │   │   ├── stat-field.tsx
│   │   │   │   └── support.tsx
│   │   │   ├── [[...slug]]
│   │   │   │   └── page.tsx
│   │   │   └── layout.tsx
│   │   ├── changelogs
│   │   │   ├── _components
│   │   │   │   ├── _layout.tsx
│   │   │   │   ├── changelog-layout.tsx
│   │   │   │   ├── default-changelog.tsx
│   │   │   │   ├── fmt-dates.tsx
│   │   │   │   ├── grid-pattern.tsx
│   │   │   │   ├── icons.tsx
│   │   │   │   └── stat-field.tsx
│   │   │   ├── [[...slug]]
│   │   │   │   └── page.tsx
│   │   │   └── layout.tsx
│   │   ├── community
│   │   │   ├── _components
│   │   │   │   ├── header.tsx
│   │   │   │   └── stats.tsx
│   │   │   └── page.tsx
│   │   ├── docs
│   │   │   ├── [[...slug]]
│   │   │   │   ├── page.client.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── layout.tsx
│   │   │   └── lib
│   │   │       └── get-llm-text.ts
│   │   ├── global.css
│   │   ├── layout.config.tsx
│   │   ├── layout.tsx
│   │   ├── llms.txt
│   │   │   ├── [...slug]
│   │   │   │   └── route.ts
│   │   │   └── route.ts
│   │   ├── not-found.tsx
│   │   ├── page.tsx
│   │   ├── reference
│   │   │   └── route.ts
│   │   ├── sitemap.xml
│   │   ├── static.json
│   │   │   └── route.ts
│   │   └── v1
│   │       ├── _components
│   │       │   └── v1-text.tsx
│   │       ├── bg-line.tsx
│   │       └── page.tsx
│   ├── assets
│   │   ├── Geist.ttf
│   │   └── GeistMono.ttf
│   ├── components
│   │   ├── ai-chat-modal.tsx
│   │   ├── anchor-scroll-fix.tsx
│   │   ├── api-method-tabs.tsx
│   │   ├── api-method.tsx
│   │   ├── banner.tsx
│   │   ├── blocks
│   │   │   └── features.tsx
│   │   ├── builder
│   │   │   ├── beam.tsx
│   │   │   ├── code-tabs
│   │   │   │   ├── code-editor.tsx
│   │   │   │   ├── code-tabs.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   ├── tab-bar.tsx
│   │   │   │   └── theme.ts
│   │   │   ├── index.tsx
│   │   │   ├── sign-in.tsx
│   │   │   ├── sign-up.tsx
│   │   │   ├── social-provider.tsx
│   │   │   ├── store.ts
│   │   │   └── tabs.tsx
│   │   ├── display-techstack.tsx
│   │   ├── divider-text.tsx
│   │   ├── docs
│   │   │   ├── docs.client.tsx
│   │   │   ├── docs.tsx
│   │   │   ├── layout
│   │   │   │   ├── nav.tsx
│   │   │   │   ├── theme-toggle.tsx
│   │   │   │   ├── toc-thumb.tsx
│   │   │   │   └── toc.tsx
│   │   │   ├── page.client.tsx
│   │   │   ├── page.tsx
│   │   │   ├── shared.tsx
│   │   │   └── ui
│   │   │       ├── button.tsx
│   │   │       ├── collapsible.tsx
│   │   │       ├── popover.tsx
│   │   │       └── scroll-area.tsx
│   │   ├── endpoint.tsx
│   │   ├── features.tsx
│   │   ├── floating-ai-search.tsx
│   │   ├── fork-button.tsx
│   │   ├── generate-apple-jwt.tsx
│   │   ├── generate-secret.tsx
│   │   ├── github-stat.tsx
│   │   ├── icons.tsx
│   │   ├── landing
│   │   │   ├── gradient-bg.tsx
│   │   │   ├── grid-pattern.tsx
│   │   │   ├── hero.tsx
│   │   │   ├── section-svg.tsx
│   │   │   ├── section.tsx
│   │   │   ├── spotlight.tsx
│   │   │   └── testimonials.tsx
│   │   ├── logo-context-menu.tsx
│   │   ├── logo.tsx
│   │   ├── markdown-renderer.tsx
│   │   ├── markdown.tsx
│   │   ├── mdx
│   │   │   ├── add-to-cursor.tsx
│   │   │   └── database-tables.tsx
│   │   ├── message-feedback.tsx
│   │   ├── mobile-search-icon.tsx
│   │   ├── nav-bar.tsx
│   │   ├── nav-link.tsx
│   │   ├── nav-mobile.tsx
│   │   ├── promo-card.tsx
│   │   ├── resource-card.tsx
│   │   ├── resource-grid.tsx
│   │   ├── resource-section.tsx
│   │   ├── ripple.tsx
│   │   ├── search-dialog.tsx
│   │   ├── side-bar.tsx
│   │   ├── sidebar-content.tsx
│   │   ├── techstack-icons.tsx
│   │   ├── theme-provider.tsx
│   │   ├── theme-toggler.tsx
│   │   └── ui
│   │       ├── accordion.tsx
│   │       ├── alert-dialog.tsx
│   │       ├── alert.tsx
│   │       ├── aside-link.tsx
│   │       ├── aspect-ratio.tsx
│   │       ├── avatar.tsx
│   │       ├── background-beams.tsx
│   │       ├── background-boxes.tsx
│   │       ├── badge.tsx
│   │       ├── breadcrumb.tsx
│   │       ├── button.tsx
│   │       ├── calendar.tsx
│   │       ├── callout.tsx
│   │       ├── card.tsx
│   │       ├── carousel.tsx
│   │       ├── chart.tsx
│   │       ├── checkbox.tsx
│   │       ├── code-block.tsx
│   │       ├── collapsible.tsx
│   │       ├── command.tsx
│   │       ├── context-menu.tsx
│   │       ├── dialog.tsx
│   │       ├── drawer.tsx
│   │       ├── dropdown-menu.tsx
│   │       ├── dynamic-code-block.tsx
│   │       ├── fade-in.tsx
│   │       ├── form.tsx
│   │       ├── hover-card.tsx
│   │       ├── input-otp.tsx
│   │       ├── input.tsx
│   │       ├── label.tsx
│   │       ├── menubar.tsx
│   │       ├── navigation-menu.tsx
│   │       ├── pagination.tsx
│   │       ├── popover.tsx
│   │       ├── progress.tsx
│   │       ├── radio-group.tsx
│   │       ├── resizable.tsx
│   │       ├── scroll-area.tsx
│   │       ├── select.tsx
│   │       ├── separator.tsx
│   │       ├── sheet.tsx
│   │       ├── sidebar.tsx
│   │       ├── skeleton.tsx
│   │       ├── slider.tsx
│   │       ├── sonner.tsx
│   │       ├── sparkles.tsx
│   │       ├── switch.tsx
│   │       ├── table.tsx
│   │       ├── tabs.tsx
│   │       ├── textarea.tsx
│   │       ├── toggle-group.tsx
│   │       ├── toggle.tsx
│   │       ├── tooltip-docs.tsx
│   │       ├── tooltip.tsx
│   │       └── use-copy-button.tsx
│   ├── components.json
│   ├── content
│   │   ├── blogs
│   │   │   ├── 0-supabase-auth-to-planetscale-migration.mdx
│   │   │   ├── 1-3.mdx
│   │   │   ├── authjs-joins-better-auth.mdx
│   │   │   └── seed-round.mdx
│   │   ├── changelogs
│   │   │   ├── 1-2.mdx
│   │   │   └── 1.0.mdx
│   │   └── docs
│   │       ├── adapters
│   │       │   ├── community-adapters.mdx
│   │       │   ├── drizzle.mdx
│   │       │   ├── mongo.mdx
│   │       │   ├── mssql.mdx
│   │       │   ├── mysql.mdx
│   │       │   ├── other-relational-databases.mdx
│   │       │   ├── postgresql.mdx
│   │       │   ├── prisma.mdx
│   │       │   └── sqlite.mdx
│   │       ├── authentication
│   │       │   ├── apple.mdx
│   │       │   ├── atlassian.mdx
│   │       │   ├── cognito.mdx
│   │       │   ├── discord.mdx
│   │       │   ├── dropbox.mdx
│   │       │   ├── email-password.mdx
│   │       │   ├── facebook.mdx
│   │       │   ├── figma.mdx
│   │       │   ├── github.mdx
│   │       │   ├── gitlab.mdx
│   │       │   ├── google.mdx
│   │       │   ├── huggingface.mdx
│   │       │   ├── kakao.mdx
│   │       │   ├── kick.mdx
│   │       │   ├── line.mdx
│   │       │   ├── linear.mdx
│   │       │   ├── linkedin.mdx
│   │       │   ├── microsoft.mdx
│   │       │   ├── naver.mdx
│   │       │   ├── notion.mdx
│   │       │   ├── other-social-providers.mdx
│   │       │   ├── paypal.mdx
│   │       │   ├── reddit.mdx
│   │       │   ├── roblox.mdx
│   │       │   ├── salesforce.mdx
│   │       │   ├── slack.mdx
│   │       │   ├── spotify.mdx
│   │       │   ├── tiktok.mdx
│   │       │   ├── twitch.mdx
│   │       │   ├── twitter.mdx
│   │       │   ├── vk.mdx
│   │       │   └── zoom.mdx
│   │       ├── basic-usage.mdx
│   │       ├── comparison.mdx
│   │       ├── concepts
│   │       │   ├── api.mdx
│   │       │   ├── cli.mdx
│   │       │   ├── client.mdx
│   │       │   ├── cookies.mdx
│   │       │   ├── database.mdx
│   │       │   ├── email.mdx
│   │       │   ├── hooks.mdx
│   │       │   ├── oauth.mdx
│   │       │   ├── plugins.mdx
│   │       │   ├── rate-limit.mdx
│   │       │   ├── session-management.mdx
│   │       │   ├── typescript.mdx
│   │       │   └── users-accounts.mdx
│   │       ├── examples
│   │       │   ├── astro.mdx
│   │       │   ├── next-js.mdx
│   │       │   ├── nuxt.mdx
│   │       │   ├── remix.mdx
│   │       │   └── svelte-kit.mdx
│   │       ├── guides
│   │       │   ├── auth0-migration-guide.mdx
│   │       │   ├── browser-extension-guide.mdx
│   │       │   ├── clerk-migration-guide.mdx
│   │       │   ├── create-a-db-adapter.mdx
│   │       │   ├── next-auth-migration-guide.mdx
│   │       │   ├── optimizing-for-performance.mdx
│   │       │   ├── saml-sso-with-okta.mdx
│   │       │   ├── supabase-migration-guide.mdx
│   │       │   └── your-first-plugin.mdx
│   │       ├── installation.mdx
│   │       ├── integrations
│   │       │   ├── astro.mdx
│   │       │   ├── convex.mdx
│   │       │   ├── elysia.mdx
│   │       │   ├── expo.mdx
│   │       │   ├── express.mdx
│   │       │   ├── fastify.mdx
│   │       │   ├── hono.mdx
│   │       │   ├── lynx.mdx
│   │       │   ├── nestjs.mdx
│   │       │   ├── next.mdx
│   │       │   ├── nitro.mdx
│   │       │   ├── nuxt.mdx
│   │       │   ├── remix.mdx
│   │       │   ├── solid-start.mdx
│   │       │   ├── svelte-kit.mdx
│   │       │   ├── tanstack.mdx
│   │       │   └── waku.mdx
│   │       ├── introduction.mdx
│   │       ├── meta.json
│   │       ├── plugins
│   │       │   ├── 2fa.mdx
│   │       │   ├── admin.mdx
│   │       │   ├── anonymous.mdx
│   │       │   ├── api-key.mdx
│   │       │   ├── autumn.mdx
│   │       │   ├── bearer.mdx
│   │       │   ├── captcha.mdx
│   │       │   ├── community-plugins.mdx
│   │       │   ├── device-authorization.mdx
│   │       │   ├── dodopayments.mdx
│   │       │   ├── dub.mdx
│   │       │   ├── email-otp.mdx
│   │       │   ├── generic-oauth.mdx
│   │       │   ├── have-i-been-pwned.mdx
│   │       │   ├── jwt.mdx
│   │       │   ├── last-login-method.mdx
│   │       │   ├── magic-link.mdx
│   │       │   ├── mcp.mdx
│   │       │   ├── multi-session.mdx
│   │       │   ├── oauth-proxy.mdx
│   │       │   ├── oidc-provider.mdx
│   │       │   ├── one-tap.mdx
│   │       │   ├── one-time-token.mdx
│   │       │   ├── open-api.mdx
│   │       │   ├── organization.mdx
│   │       │   ├── passkey.mdx
│   │       │   ├── phone-number.mdx
│   │       │   ├── polar.mdx
│   │       │   ├── siwe.mdx
│   │       │   ├── sso.mdx
│   │       │   ├── stripe.mdx
│   │       │   └── username.mdx
│   │       └── reference
│   │           ├── contributing.mdx
│   │           ├── faq.mdx
│   │           ├── options.mdx
│   │           ├── resources.mdx
│   │           ├── security.mdx
│   │           └── telemetry.mdx
│   ├── hooks
│   │   └── use-mobile.ts
│   ├── ignore-build.sh
│   ├── lib
│   │   ├── blog.ts
│   │   ├── chat
│   │   │   └── inkeep-qa-schema.ts
│   │   ├── constants.ts
│   │   ├── export-search-indexes.ts
│   │   ├── inkeep-analytics.ts
│   │   ├── is-active.ts
│   │   ├── metadata.ts
│   │   ├── source.ts
│   │   └── utils.ts
│   ├── next.config.js
│   ├── package.json
│   ├── postcss.config.js
│   ├── proxy.ts
│   ├── public
│   │   ├── avatars
│   │   │   └── beka.jpg
│   │   ├── blogs
│   │   │   ├── authjs-joins.png
│   │   │   ├── seed-round.png
│   │   │   └── supabase-ps.png
│   │   ├── branding
│   │   │   ├── better-auth-brand-assets.zip
│   │   │   ├── better-auth-logo-dark.png
│   │   │   ├── better-auth-logo-dark.svg
│   │   │   ├── better-auth-logo-light.png
│   │   │   ├── better-auth-logo-light.svg
│   │   │   ├── better-auth-logo-wordmark-dark.png
│   │   │   ├── better-auth-logo-wordmark-dark.svg
│   │   │   ├── better-auth-logo-wordmark-light.png
│   │   │   └── better-auth-logo-wordmark-light.svg
│   │   ├── extension-id.png
│   │   ├── favicon
│   │   │   ├── android-chrome-192x192.png
│   │   │   ├── android-chrome-512x512.png
│   │   │   ├── apple-touch-icon.png
│   │   │   ├── favicon-16x16.png
│   │   │   ├── favicon-32x32.png
│   │   │   ├── favicon.ico
│   │   │   ├── light
│   │   │   │   ├── android-chrome-192x192.png
│   │   │   │   ├── android-chrome-512x512.png
│   │   │   │   ├── apple-touch-icon.png
│   │   │   │   ├── favicon-16x16.png
│   │   │   │   ├── favicon-32x32.png
│   │   │   │   ├── favicon.ico
│   │   │   │   └── site.webmanifest
│   │   │   └── site.webmanifest
│   │   ├── images
│   │   │   └── blogs
│   │   │       └── better auth (1).png
│   │   ├── logo.png
│   │   ├── logo.svg
│   │   ├── LogoDark.webp
│   │   ├── LogoLight.webp
│   │   ├── og.png
│   │   ├── open-api-reference.png
│   │   ├── people-say
│   │   │   ├── code-with-antonio.jpg
│   │   │   ├── dagmawi-babi.png
│   │   │   ├── dax.png
│   │   │   ├── dev-ed.png
│   │   │   ├── egoist.png
│   │   │   ├── guillermo-rauch.png
│   │   │   ├── jonathan-wilke.png
│   │   │   ├── josh-tried-coding.jpg
│   │   │   ├── kitze.jpg
│   │   │   ├── lazar-nikolov.png
│   │   │   ├── nizzy.png
│   │   │   ├── omar-mcadam.png
│   │   │   ├── ryan-vogel.jpg
│   │   │   ├── saltyatom.jpg
│   │   │   ├── sebastien-chopin.png
│   │   │   ├── shreyas-mididoddi.png
│   │   │   ├── tech-nerd.png
│   │   │   ├── theo.png
│   │   │   ├── vybhav-bhargav.png
│   │   │   └── xavier-pladevall.jpg
│   │   ├── plus.svg
│   │   ├── release-og
│   │   │   ├── 1-2.png
│   │   │   ├── 1-3.png
│   │   │   └── changelog-og.png
│   │   └── v1-og.png
│   ├── README.md
│   ├── scripts
│   │   ├── endpoint-to-doc
│   │   │   ├── index.ts
│   │   │   ├── input.ts
│   │   │   ├── output.mdx
│   │   │   └── readme.md
│   │   └── sync-orama.ts
│   ├── source.config.ts
│   ├── tailwind.config.js
│   ├── tsconfig.json
│   └── turbo.json
├── e2e
│   ├── integration
│   │   ├── package.json
│   │   ├── playwright.config.ts
│   │   ├── solid-vinxi
│   │   │   ├── .gitignore
│   │   │   ├── app.config.ts
│   │   │   ├── e2e
│   │   │   │   ├── test.spec.ts
│   │   │   │   └── utils.ts
│   │   │   ├── package.json
│   │   │   ├── public
│   │   │   │   └── favicon.ico
│   │   │   ├── src
│   │   │   │   ├── app.tsx
│   │   │   │   ├── entry-client.tsx
│   │   │   │   ├── entry-server.tsx
│   │   │   │   ├── global.d.ts
│   │   │   │   ├── lib
│   │   │   │   │   ├── auth-client.ts
│   │   │   │   │   └── auth.ts
│   │   │   │   └── routes
│   │   │   │       ├── [...404].tsx
│   │   │   │       ├── api
│   │   │   │       │   └── auth
│   │   │   │       │       └── [...all].ts
│   │   │   │       └── index.tsx
│   │   │   └── tsconfig.json
│   │   ├── test-utils
│   │   │   ├── package.json
│   │   │   └── src
│   │   │       └── playwright.ts
│   │   └── vanilla-node
│   │       ├── e2e
│   │       │   ├── app.ts
│   │       │   ├── domain.spec.ts
│   │       │   ├── postgres-js.spec.ts
│   │       │   ├── test.spec.ts
│   │       │   └── utils.ts
│   │       ├── index.html
│   │       ├── package.json
│   │       ├── src
│   │       │   ├── main.ts
│   │       │   └── vite-env.d.ts
│   │       ├── tsconfig.json
│   │       └── vite.config.ts
│   └── smoke
│       ├── package.json
│       ├── test
│       │   ├── bun.spec.ts
│       │   ├── cloudflare.spec.ts
│       │   ├── deno.spec.ts
│       │   ├── fixtures
│       │   │   ├── bun-simple.ts
│       │   │   ├── cloudflare
│       │   │   │   ├── .gitignore
│       │   │   │   ├── drizzle
│       │   │   │   │   ├── 0000_clean_vector.sql
│       │   │   │   │   └── meta
│       │   │   │   │       ├── _journal.json
│       │   │   │   │       └── 0000_snapshot.json
│       │   │   │   ├── drizzle.config.ts
│       │   │   │   ├── package.json
│       │   │   │   ├── src
│       │   │   │   │   ├── auth-schema.ts
│       │   │   │   │   ├── db.ts
│       │   │   │   │   └── index.ts
│       │   │   │   ├── test
│       │   │   │   │   ├── apply-migrations.ts
│       │   │   │   │   ├── env.d.ts
│       │   │   │   │   └── index.test.ts
│       │   │   │   ├── tsconfig.json
│       │   │   │   ├── vitest.config.ts
│       │   │   │   ├── worker-configuration.d.ts
│       │   │   │   └── wrangler.json
│       │   │   ├── deno-simple.ts
│       │   │   ├── tsconfig-exact-optional-property-types
│       │   │   │   ├── package.json
│       │   │   │   ├── src
│       │   │   │   │   ├── index.ts
│       │   │   │   │   ├── user-additional-fields.ts
│       │   │   │   │   └── username.ts
│       │   │   │   └── tsconfig.json
│       │   │   ├── tsconfig-verbatim-module-syntax-node10
│       │   │   │   ├── package.json
│       │   │   │   ├── src
│       │   │   │   │   └── index.ts
│       │   │   │   └── tsconfig.json
│       │   │   └── vite
│       │   │       ├── package.json
│       │   │       ├── src
│       │   │       │   ├── client.ts
│       │   │       │   └── server.ts
│       │   │       ├── tsconfig.json
│       │   │       └── vite.config.ts
│       │   ├── ssr.ts
│       │   ├── typecheck.spec.ts
│       │   └── vite.spec.ts
│       └── tsconfig.json
├── LICENSE.md
├── package.json
├── packages
│   ├── better-auth
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── __snapshots__
│   │   │   │   └── init.test.ts.snap
│   │   │   ├── adapters
│   │   │   │   ├── adapter-factory
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── test
│   │   │   │   │   │   ├── __snapshots__
│   │   │   │   │   │   │   └── adapter-factory.test.ts.snap
│   │   │   │   │   │   └── adapter-factory.test.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── create-test-suite.ts
│   │   │   │   ├── drizzle-adapter
│   │   │   │   │   ├── drizzle-adapter.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── test
│   │   │   │   │       ├── .gitignore
│   │   │   │   │       ├── adapter.drizzle.mysql.test.ts
│   │   │   │   │       ├── adapter.drizzle.pg.test.ts
│   │   │   │   │       ├── adapter.drizzle.sqlite.test.ts
│   │   │   │   │       └── generate-schema.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── kysely-adapter
│   │   │   │   │   ├── bun-sqlite-dialect.ts
│   │   │   │   │   ├── dialect.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── kysely-adapter.ts
│   │   │   │   │   ├── node-sqlite-dialect.ts
│   │   │   │   │   ├── test
│   │   │   │   │   │   ├── adapter.kysely.mssql.test.ts
│   │   │   │   │   │   ├── adapter.kysely.mysql.test.ts
│   │   │   │   │   │   ├── adapter.kysely.pg.test.ts
│   │   │   │   │   │   ├── adapter.kysely.sqlite.test.ts
│   │   │   │   │   │   └── node-sqlite-dialect.test.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── memory-adapter
│   │   │   │   │   ├── adapter.memory.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── memory-adapter.ts
│   │   │   │   ├── mongodb-adapter
│   │   │   │   │   ├── adapter.mongo-db.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── mongodb-adapter.ts
│   │   │   │   ├── prisma-adapter
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── prisma-adapter.ts
│   │   │   │   │   └── test
│   │   │   │   │       ├── .gitignore
│   │   │   │   │       ├── base.prisma
│   │   │   │   │       ├── generate-auth-config.ts
│   │   │   │   │       ├── generate-prisma-schema.ts
│   │   │   │   │       ├── get-prisma-client.ts
│   │   │   │   │       ├── prisma.mysql.test.ts
│   │   │   │   │       ├── prisma.pg.test.ts
│   │   │   │   │       ├── prisma.sqlite.test.ts
│   │   │   │   │       └── push-prisma-schema.ts
│   │   │   │   ├── test-adapter.ts
│   │   │   │   ├── test.ts
│   │   │   │   ├── tests
│   │   │   │   │   ├── auth-flow.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── normal.ts
│   │   │   │   │   ├── number-id.ts
│   │   │   │   │   ├── performance.ts
│   │   │   │   │   └── transactions.ts
│   │   │   │   └── utils.ts
│   │   │   ├── api
│   │   │   │   ├── check-endpoint-conflicts.test.ts
│   │   │   │   ├── index.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── middlewares
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── origin-check.test.ts
│   │   │   │   │   └── origin-check.ts
│   │   │   │   ├── rate-limiter
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── rate-limiter.test.ts
│   │   │   │   ├── routes
│   │   │   │   │   ├── account.test.ts
│   │   │   │   │   ├── account.ts
│   │   │   │   │   ├── callback.ts
│   │   │   │   │   ├── email-verification.test.ts
│   │   │   │   │   ├── email-verification.ts
│   │   │   │   │   ├── error.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── ok.ts
│   │   │   │   │   ├── reset-password.test.ts
│   │   │   │   │   ├── reset-password.ts
│   │   │   │   │   ├── session-api.test.ts
│   │   │   │   │   ├── session.ts
│   │   │   │   │   ├── sign-in.test.ts
│   │   │   │   │   ├── sign-in.ts
│   │   │   │   │   ├── sign-out.test.ts
│   │   │   │   │   ├── sign-out.ts
│   │   │   │   │   ├── sign-up.test.ts
│   │   │   │   │   ├── sign-up.ts
│   │   │   │   │   ├── update-user.test.ts
│   │   │   │   │   └── update-user.ts
│   │   │   │   ├── to-auth-endpoints.test.ts
│   │   │   │   └── to-auth-endpoints.ts
│   │   │   ├── auth.test.ts
│   │   │   ├── auth.ts
│   │   │   ├── call.test.ts
│   │   │   ├── client
│   │   │   │   ├── client-ssr.test.ts
│   │   │   │   ├── client.test.ts
│   │   │   │   ├── config.ts
│   │   │   │   ├── fetch-plugins.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── lynx
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── lynx-store.ts
│   │   │   │   ├── parser.ts
│   │   │   │   ├── path-to-object.ts
│   │   │   │   ├── plugins
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── infer-plugin.ts
│   │   │   │   ├── proxy.ts
│   │   │   │   ├── query.ts
│   │   │   │   ├── react
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── react-store.ts
│   │   │   │   ├── session-atom.ts
│   │   │   │   ├── solid
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── solid-store.ts
│   │   │   │   ├── svelte
│   │   │   │   │   └── index.ts
│   │   │   │   ├── test-plugin.ts
│   │   │   │   ├── types.ts
│   │   │   │   ├── url.test.ts
│   │   │   │   ├── vanilla.ts
│   │   │   │   └── vue
│   │   │   │       ├── index.ts
│   │   │   │       └── vue-store.ts
│   │   │   ├── cookies
│   │   │   │   ├── check-cookies.ts
│   │   │   │   ├── cookie-utils.ts
│   │   │   │   ├── cookies.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── crypto
│   │   │   │   ├── buffer.ts
│   │   │   │   ├── hash.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── jwt.ts
│   │   │   │   ├── password.test.ts
│   │   │   │   ├── password.ts
│   │   │   │   └── random.ts
│   │   │   ├── db
│   │   │   │   ├── db.test.ts
│   │   │   │   ├── field.ts
│   │   │   │   ├── get-migration.ts
│   │   │   │   ├── get-schema.ts
│   │   │   │   ├── get-tables.test.ts
│   │   │   │   ├── get-tables.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── internal-adapter.test.ts
│   │   │   │   ├── internal-adapter.ts
│   │   │   │   ├── schema.ts
│   │   │   │   ├── secondary-storage.test.ts
│   │   │   │   ├── to-zod.ts
│   │   │   │   ├── utils.ts
│   │   │   │   └── with-hooks.ts
│   │   │   ├── index.ts
│   │   │   ├── init.test.ts
│   │   │   ├── init.ts
│   │   │   ├── integrations
│   │   │   │   ├── next-js.ts
│   │   │   │   ├── node.ts
│   │   │   │   ├── react-start.ts
│   │   │   │   ├── solid-start.ts
│   │   │   │   └── svelte-kit.ts
│   │   │   ├── oauth2
│   │   │   │   ├── index.ts
│   │   │   │   ├── link-account.test.ts
│   │   │   │   ├── link-account.ts
│   │   │   │   ├── state.ts
│   │   │   │   └── utils.ts
│   │   │   ├── plugins
│   │   │   │   ├── access
│   │   │   │   │   ├── access.test.ts
│   │   │   │   │   ├── access.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── additional-fields
│   │   │   │   │   ├── additional-fields.test.ts
│   │   │   │   │   └── client.ts
│   │   │   │   ├── admin
│   │   │   │   │   ├── access
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── statement.ts
│   │   │   │   │   ├── admin.test.ts
│   │   │   │   │   ├── admin.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── error-codes.ts
│   │   │   │   │   ├── has-permission.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── anonymous
│   │   │   │   │   ├── anon.test.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── api-key
│   │   │   │   │   ├── api-key.test.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── rate-limit.ts
│   │   │   │   │   ├── routes
│   │   │   │   │   │   ├── create-api-key.ts
│   │   │   │   │   │   ├── delete-all-expired-api-keys.ts
│   │   │   │   │   │   ├── delete-api-key.ts
│   │   │   │   │   │   ├── get-api-key.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── list-api-keys.ts
│   │   │   │   │   │   ├── update-api-key.ts
│   │   │   │   │   │   └── verify-api-key.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── bearer
│   │   │   │   │   ├── bearer.test.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── captcha
│   │   │   │   │   ├── captcha.test.ts
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── error-codes.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── utils.ts
│   │   │   │   │   └── verify-handlers
│   │   │   │   │       ├── captchafox.ts
│   │   │   │   │       ├── cloudflare-turnstile.ts
│   │   │   │   │       ├── google-recaptcha.ts
│   │   │   │   │       ├── h-captcha.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── custom-session
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── custom-session.test.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── device-authorization
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── device-authorization.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── schema.ts
│   │   │   │   ├── email-otp
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── email-otp.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── generic-oauth
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── generic-oauth.test.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── haveibeenpwned
│   │   │   │   │   ├── haveibeenpwned.test.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── jwt
│   │   │   │   │   ├── adapter.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── jwt.test.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── sign.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── last-login-method
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── custom-prefix.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── last-login-method.test.ts
│   │   │   │   ├── magic-link
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── magic-link.test.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── mcp
│   │   │   │   │   ├── authorize.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── mcp.test.ts
│   │   │   │   ├── multi-session
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── multi-session.test.ts
│   │   │   │   ├── oauth-proxy
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── oauth-proxy.test.ts
│   │   │   │   ├── oidc-provider
│   │   │   │   │   ├── authorize.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── oidc.test.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── ui.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── one-tap
│   │   │   │   │   ├── client.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── one-time-token
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── one-time-token.test.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── open-api
│   │   │   │   │   ├── generator.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── logo.ts
│   │   │   │   │   └── open-api.test.ts
│   │   │   │   ├── organization
│   │   │   │   │   ├── access
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── statement.ts
│   │   │   │   │   ├── adapter.ts
│   │   │   │   │   ├── call.ts
│   │   │   │   │   ├── client.test.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── error-codes.ts
│   │   │   │   │   ├── has-permission.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── organization-hook.test.ts
│   │   │   │   │   ├── organization.test.ts
│   │   │   │   │   ├── organization.ts
│   │   │   │   │   ├── permission.ts
│   │   │   │   │   ├── routes
│   │   │   │   │   │   ├── crud-access-control.test.ts
│   │   │   │   │   │   ├── crud-access-control.ts
│   │   │   │   │   │   ├── crud-invites.ts
│   │   │   │   │   │   ├── crud-members.test.ts
│   │   │   │   │   │   ├── crud-members.ts
│   │   │   │   │   │   ├── crud-org.test.ts
│   │   │   │   │   │   ├── crud-org.ts
│   │   │   │   │   │   └── crud-team.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── team.test.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── passkey
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── passkey.test.ts
│   │   │   │   ├── phone-number
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── phone-number-error.ts
│   │   │   │   │   └── phone-number.test.ts
│   │   │   │   ├── siwe
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── siwe.test.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── sso
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── sso.test.ts
│   │   │   │   ├── two-factor
│   │   │   │   │   ├── backup-codes
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── constant.ts
│   │   │   │   │   ├── error-code.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── otp
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── totp
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── two-factor.test.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── utils.ts
│   │   │   │   │   └── verify-two-factor.ts
│   │   │   │   └── username
│   │   │   │       ├── client.ts
│   │   │   │       ├── error-codes.ts
│   │   │   │       ├── index.ts
│   │   │   │       ├── schema.ts
│   │   │   │       └── username.test.ts
│   │   │   ├── social-providers
│   │   │   │   └── index.ts
│   │   │   ├── social.test.ts
│   │   │   ├── test-utils
│   │   │   │   ├── headers.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── state.ts
│   │   │   │   └── test-instance.ts
│   │   │   ├── types
│   │   │   │   ├── adapter.ts
│   │   │   │   ├── api.ts
│   │   │   │   ├── helper.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── models.ts
│   │   │   │   ├── plugins.ts
│   │   │   │   └── types.test.ts
│   │   │   └── utils
│   │   │       ├── await-object.ts
│   │   │       ├── boolean.ts
│   │   │       ├── clone.ts
│   │   │       ├── constants.ts
│   │   │       ├── date.ts
│   │   │       ├── ensure-utc.ts
│   │   │       ├── get-request-ip.ts
│   │   │       ├── hashing.ts
│   │   │       ├── hide-metadata.ts
│   │   │       ├── id.ts
│   │   │       ├── import-util.ts
│   │   │       ├── index.ts
│   │   │       ├── is-atom.ts
│   │   │       ├── is-promise.ts
│   │   │       ├── json.ts
│   │   │       ├── merger.ts
│   │   │       ├── middleware-response.ts
│   │   │       ├── misc.ts
│   │   │       ├── password.ts
│   │   │       ├── plugin-helper.ts
│   │   │       ├── shim.ts
│   │   │       ├── time.ts
│   │   │       ├── url.ts
│   │   │       └── wildcard.ts
│   │   ├── tsconfig.json
│   │   ├── tsdown.config.ts
│   │   └── vitest.config.ts
│   ├── cli
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── commands
│   │   │   │   ├── generate.ts
│   │   │   │   ├── info.ts
│   │   │   │   ├── init.ts
│   │   │   │   ├── login.ts
│   │   │   │   ├── mcp.ts
│   │   │   │   ├── migrate.ts
│   │   │   │   └── secret.ts
│   │   │   ├── generators
│   │   │   │   ├── auth-config.ts
│   │   │   │   ├── drizzle.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── kysely.ts
│   │   │   │   ├── prisma.ts
│   │   │   │   └── types.ts
│   │   │   ├── index.ts
│   │   │   └── utils
│   │   │       ├── add-svelte-kit-env-modules.ts
│   │   │       ├── check-package-managers.ts
│   │   │       ├── format-ms.ts
│   │   │       ├── get-config.ts
│   │   │       ├── get-package-info.ts
│   │   │       ├── get-tsconfig-info.ts
│   │   │       └── install-dependencies.ts
│   │   ├── test
│   │   │   ├── __snapshots__
│   │   │   │   ├── auth-schema-mysql-enum.txt
│   │   │   │   ├── auth-schema-mysql-number-id.txt
│   │   │   │   ├── auth-schema-mysql-passkey-number-id.txt
│   │   │   │   ├── auth-schema-mysql-passkey.txt
│   │   │   │   ├── auth-schema-mysql.txt
│   │   │   │   ├── auth-schema-number-id.txt
│   │   │   │   ├── auth-schema-pg-enum.txt
│   │   │   │   ├── auth-schema-pg-passkey.txt
│   │   │   │   ├── auth-schema-sqlite-enum.txt
│   │   │   │   ├── auth-schema-sqlite-number-id.txt
│   │   │   │   ├── auth-schema-sqlite-passkey-number-id.txt
│   │   │   │   ├── auth-schema-sqlite-passkey.txt
│   │   │   │   ├── auth-schema-sqlite.txt
│   │   │   │   ├── auth-schema.txt
│   │   │   │   ├── migrations.sql
│   │   │   │   ├── schema-mongodb.prisma
│   │   │   │   ├── schema-mysql-custom.prisma
│   │   │   │   ├── schema-mysql.prisma
│   │   │   │   ├── schema-numberid.prisma
│   │   │   │   └── schema.prisma
│   │   │   ├── generate-all-db.test.ts
│   │   │   ├── generate.test.ts
│   │   │   ├── get-config.test.ts
│   │   │   ├── info.test.ts
│   │   │   └── migrate.test.ts
│   │   ├── tsconfig.json
│   │   ├── tsconfig.test.json
│   │   └── tsdown.config.ts
│   ├── core
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── api
│   │   │   │   └── index.ts
│   │   │   ├── async_hooks
│   │   │   │   └── index.ts
│   │   │   ├── context
│   │   │   │   ├── endpoint-context.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── transaction.ts
│   │   │   ├── db
│   │   │   │   ├── adapter
│   │   │   │   │   └── index.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── plugin.ts
│   │   │   │   ├── schema
│   │   │   │   │   ├── account.ts
│   │   │   │   │   ├── rate-limit.ts
│   │   │   │   │   ├── session.ts
│   │   │   │   │   ├── shared.ts
│   │   │   │   │   ├── user.ts
│   │   │   │   │   └── verification.ts
│   │   │   │   └── type.ts
│   │   │   ├── env
│   │   │   │   ├── color-depth.ts
│   │   │   │   ├── env-impl.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── logger.test.ts
│   │   │   │   └── logger.ts
│   │   │   ├── error
│   │   │   │   ├── codes.ts
│   │   │   │   └── index.ts
│   │   │   ├── index.ts
│   │   │   ├── 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

--------------------------------------------------------------------------------
/packages/core/src/env/logger.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { describe, it, expect } from "vitest";
 2 | import { shouldPublishLog, type LogLevel } from "./logger";
 3 | 
 4 | describe("shouldPublishLog", () => {
 5 | 	const testCases: {
 6 | 		currentLogLevel: LogLevel;
 7 | 		logLevel: LogLevel;
 8 | 		expected: boolean;
 9 | 	}[] = [
10 | 		{ currentLogLevel: "info", logLevel: "info", expected: true },
11 | 		{ currentLogLevel: "info", logLevel: "warn", expected: false },
12 | 		{ currentLogLevel: "info", logLevel: "error", expected: false },
13 | 		{ currentLogLevel: "info", logLevel: "debug", expected: false },
14 | 		{ currentLogLevel: "warn", logLevel: "info", expected: true },
15 | 		{ currentLogLevel: "warn", logLevel: "warn", expected: true },
16 | 		{ currentLogLevel: "warn", logLevel: "error", expected: false },
17 | 		{ currentLogLevel: "warn", logLevel: "debug", expected: false },
18 | 		{ currentLogLevel: "error", logLevel: "info", expected: true },
19 | 		{ currentLogLevel: "error", logLevel: "warn", expected: true },
20 | 		{ currentLogLevel: "error", logLevel: "error", expected: true },
21 | 		{ currentLogLevel: "error", logLevel: "debug", expected: false },
22 | 		{ currentLogLevel: "debug", logLevel: "info", expected: true },
23 | 		{ currentLogLevel: "debug", logLevel: "warn", expected: true },
24 | 		{ currentLogLevel: "debug", logLevel: "error", expected: true },
25 | 		{ currentLogLevel: "debug", logLevel: "debug", expected: true },
26 | 	];
27 | 
28 | 	testCases.forEach(({ currentLogLevel, logLevel, expected }) => {
29 | 		it(`should return "${expected}" when currentLogLevel is "${currentLogLevel}" and logLevel is "${logLevel}"`, () => {
30 | 			expect(shouldPublishLog(currentLogLevel, logLevel)).toBe(expected);
31 | 		});
32 | 	});
33 | });
34 | 
```

--------------------------------------------------------------------------------
/docs/content/docs/examples/astro.mdx:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: Astro Example
 3 | description: Better Auth Astro example.
 4 | ---
 5 | 
 6 | This is an example of how to use Better Auth with Astro. It uses Solid for building the components.
 7 | 
 8 | 
 9 | **Implements the following features:**
10 | Email & Password . Social Sign-in with Google . Passkeys . Email Verification . Password Reset . Two Factor Authentication . Profile Update . Session Management
11 | 
12 | <ForkButton url="better-auth/examples/tree/main/astro-example"  />
13 | 
14 | <iframe src="https://stackblitz.com/github/better-auth/examples/tree/main/astro-example?codemirror=1&fontsize=14&hidenavigation=1&runonclick=1&hidedevtools=1"
15 |    style={{
16 |       width: "100%",
17 |       height: "500px",
18 |       border: 0,
19 |       borderRadius: "4px",
20 |       overflow: "hidden"
21 |    }}
22 |    title="Better Auth Astro+Solid Example"
23 |    allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
24 |    sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
25 |    >
26 | </iframe>
27 | 
28 | 
29 | ## How to run
30 | 
31 | 1. Clone the code sandbox (or the repo) and open it in your code editor
32 | 2. Provide .env file with the following variables
33 |    ```txt
34 |    GOOGLE_CLIENT_ID=
35 |    GOOGLE_CLIENT_SECRET=
36 |    BETTER_AUTH_SECRET=
37 |    ```
38 |    //if you don't have these, you can get them from the google developer console. If you don't want to use google sign-in, you can remove the google config from the `auth.ts` file.
39 | 
40 | 3. Run the following commands
41 |    ```bash
42 |    pnpm install
43 |    pnpm run dev
44 |    ```
45 | 4. Open the browser and navigate to `http://localhost:3000`
46 | 
```

--------------------------------------------------------------------------------
/docs/content/docs/examples/next-js.mdx:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: Next.js Example
 3 | description: Better Auth Next.js example.
 4 | ---
 5 | 
 6 | This is an example of how to use Better Auth with Next.
 7 | 
 8 | **Implements the following features:**
 9 | Email & Password . Social Sign-in . Passkeys . Email Verification . Password Reset . Two Factor Authentication . Profile Update . Session Management . Organization, Members and Roles
10 | 
11 | See [Demo](https://demo.better-auth.com)
12 | 
13 | <ForkButton url="better-auth/better-auth/tree/main/demo/nextjs"  />
14 | 
15 | <iframe src="https://stackblitz.com/github/better-auth/better-auth/tree/main/demo/nextjs?codemirror=1&fontsize=14&hidenavigation=1&runonclick=1&hidedevtools=1"
16 |    style={{
17 |       width: "100%",
18 |       height: "500px",
19 |       border: 0,
20 |       borderRadius: "4px",
21 |       overflow: "hidden"
22 |    }}
23 |    title="Better Auth Next.js Example"
24 |    allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
25 |    sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
26 |    >
27 | </iframe>
28 | 
29 | 
30 | ## How to run
31 | 
32 | 1. Clone the code sandbox (or the repo) and open it in your code editor
33 | 2. Move .env.example to .env and provide necessary variables
34 | 3. Run the following commands
35 |    ```bash
36 |    pnpm install
37 |    pnpm dev
38 |    ```
39 | 4. Open the browser and navigate to `http://localhost:3000`
40 | 
41 | ### SSO Login Example
42 | 
43 | For this example, we utilize DummyIDP. Initiate the login from the [DummyIDP login](https://dummyidp.com/apps/app_01k16v4vb5yytywqjjvv2b3435/login), click "Proceed", and from here it will direct you to user's dashboard.
44 |  
```

--------------------------------------------------------------------------------
/demo/nextjs/components/ui/password-input.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | "use client";
 2 | 
 3 | import { EyeIcon, EyeOffIcon } from "lucide-react";
 4 | import * as React from "react";
 5 | 
 6 | import { Button } from "@/components/ui/button";
 7 | import { Input } from "@/components/ui/input";
 8 | import { cn } from "@/lib/utils";
 9 | 
10 | const PasswordInput = ({
11 | 	ref,
12 | 	className,
13 | 	...props
14 | }: any & {
15 | 	ref: React.RefObject<HTMLInputElement>;
16 | }) => {
17 | 	const [showPassword, setShowPassword] = React.useState(false);
18 | 	const disabled =
19 | 		props.value === "" || props.value === undefined || props.disabled;
20 | 
21 | 	return (
22 | 		<div className="relative">
23 | 			<Input
24 | 				{...props}
25 | 				type={showPassword ? "text" : "password"}
26 | 				name="password_fake"
27 | 				className={cn("hide-password-toggle pr-10", className)}
28 | 				ref={ref}
29 | 			/>
30 | 			<Button
31 | 				type="button"
32 | 				variant="ghost"
33 | 				size="sm"
34 | 				className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"
35 | 				onClick={() => setShowPassword((prev) => !prev)}
36 | 				disabled={disabled}
37 | 			>
38 | 				{showPassword && !disabled ? (
39 | 					<EyeIcon className="h-4 w-4" aria-hidden="true" />
40 | 				) : (
41 | 					<EyeOffIcon className="h-4 w-4" aria-hidden="true" />
42 | 				)}
43 | 				<span className="sr-only">
44 | 					{showPassword ? "Hide password" : "Show password"}
45 | 				</span>
46 | 			</Button>
47 | 
48 | 			{/* hides browsers password toggles */}
49 | 			<style>{`
50 |                 .hide-password-toggle::-ms-reveal,
51 |                 .hide-password-toggle::-ms-clear {
52 |                     visibility: hidden;
53 |                     pointer-events: none;
54 |                     display: none;
55 |                 }
56 |             `}</style>
57 | 		</div>
58 | 	);
59 | };
60 | PasswordInput.displayName = "PasswordInput";
61 | 
62 | export { PasswordInput };
63 | 
```

--------------------------------------------------------------------------------
/e2e/integration/solid-vinxi/e2e/utils.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type { Page } from "@playwright/test";
 2 | import { type ChildProcessWithoutNullStreams, spawn } from "node:child_process";
 3 | import { fileURLToPath } from "node:url";
 4 | import { terminate } from "@better-auth/test-utils/playwright";
 5 | 
 6 | const root = fileURLToPath(new URL("../", import.meta.url));
 7 | 
 8 | export async function runClient<R>(
 9 | 	page: Page,
10 | 	fn: ({ client }: { client: Window["client"] }) => R,
11 | ): Promise<R> {
12 | 	const client = await page.evaluateHandle<Window["client"]>("window.client");
13 | 	return page.evaluate(fn, { client });
14 | }
15 | 
16 | export function setup() {
17 | 	let clientChild: ChildProcessWithoutNullStreams;
18 | 	const ref: {
19 | 		clientPort: number;
20 | 	} = {
21 | 		clientPort: -1,
22 | 	};
23 | 	return {
24 | 		ref,
25 | 		start: async () => {
26 | 			clientChild = spawn("pnpm", ["run", "dev"], {
27 | 				cwd: root,
28 | 				stdio: "pipe",
29 | 				env: {
30 | 					...process.env,
31 | 					NO_COLOR: "1",
32 | 				},
33 | 			});
34 | 			clientChild.stderr.on("data", (data) => {
35 | 				const message = data.toString();
36 | 				console.error(message);
37 | 			});
38 | 			clientChild.stdout.on("data", (data) => {
39 | 				const message = data.toString();
40 | 				console.log(message);
41 | 			});
42 | 
43 | 			await Promise.all([
44 | 				new Promise<void>((resolve) => {
45 | 					clientChild.stdout.on("data", (data) => {
46 | 						const message = data.toString();
47 | 						// find: http://localhost:XXXX/ for vinxi dev server
48 | 						if (message.includes("http://localhost:")) {
49 | 							const match = message.match(/http:\/\/localhost:(\d+)/);
50 | 							if (match) {
51 | 								ref.clientPort = Number(match[1]);
52 | 								resolve();
53 | 							}
54 | 						}
55 | 					});
56 | 				}),
57 | 			]);
58 | 		},
59 | 		clean: async () => {
60 | 			await terminate(clientChild.pid!);
61 | 		},
62 | 	};
63 | }
64 | 
```

--------------------------------------------------------------------------------
/demo/nextjs/components/ui/scroll-area.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | "use client";
 2 | 
 3 | import * as React from "react";
 4 | import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
 5 | 
 6 | import { cn } from "@/lib/utils";
 7 | 
 8 | const ScrollArea = ({
 9 | 	ref,
10 | 	className,
11 | 	children,
12 | 	...props
13 | }: React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & {
14 | 	ref: React.RefObject<React.ElementRef<typeof ScrollAreaPrimitive.Root>>;
15 | }) => (
16 | 	<ScrollAreaPrimitive.Root
17 | 		ref={ref}
18 | 		className={cn("relative overflow-hidden", className)}
19 | 		{...props}
20 | 	>
21 | 		<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
22 | 			{children}
23 | 		</ScrollAreaPrimitive.Viewport>
24 | 		<ScrollBar />
25 | 		<ScrollAreaPrimitive.Corner />
26 | 	</ScrollAreaPrimitive.Root>
27 | );
28 | ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
29 | 
30 | const ScrollBar = ({
31 | 	ref,
32 | 	className,
33 | 	orientation = "vertical",
34 | 	...props
35 | }: React.ComponentPropsWithoutRef<
36 | 	typeof ScrollAreaPrimitive.ScrollAreaScrollbar
37 | > & {
38 | 	ref: React.RefObject<
39 | 		React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
40 | 	>;
41 | }) => (
42 | 	<ScrollAreaPrimitive.ScrollAreaScrollbar
43 | 		ref={ref}
44 | 		orientation={orientation}
45 | 		className={cn(
46 | 			"flex touch-none select-none transition-colors",
47 | 			orientation === "vertical" &&
48 | 				"h-full w-2.5 border-l border-l-transparent p-px",
49 | 			orientation === "horizontal" &&
50 | 				"h-2.5 flex-col border-t border-t-transparent p-px",
51 | 			className,
52 | 		)}
53 | 		{...props}
54 | 	>
55 | 		<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
56 | 	</ScrollAreaPrimitive.ScrollAreaScrollbar>
57 | );
58 | ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
59 | 
60 | export { ScrollArea, ScrollBar };
61 | 
```

--------------------------------------------------------------------------------
/packages/better-auth/src/plugins/organization/client.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { betterAuth } from "../../auth";
 2 | import { createAuthClient } from "../../client";
 3 | import { inferOrgAdditionalFields, organizationClient } from "./client";
 4 | import { organization } from "./organization";
 5 | import { describe, it } from "vitest";
 6 | 
 7 | describe("organization", () => {
 8 | 	const auth = betterAuth({
 9 | 		plugins: [
10 | 			organization({
11 | 				schema: {
12 | 					organization: {
13 | 						additionalFields: {
14 | 							newField: {
15 | 								type: "string",
16 | 							},
17 | 						},
18 | 					},
19 | 				},
20 | 			}),
21 | 		],
22 | 	});
23 | 
24 | 	it("should infer additional fields", async () => {
25 | 		const client = createAuthClient({
26 | 			plugins: [
27 | 				organizationClient({
28 | 					schema: inferOrgAdditionalFields<typeof auth>(),
29 | 				}),
30 | 			],
31 | 			fetchOptions: {
32 | 				customFetchImpl: async () => new Response(),
33 | 			},
34 | 		});
35 | 		client.organization.create({
36 | 			name: "Test",
37 | 			slug: "test",
38 | 			newField: "123", //this should be allowed
39 | 			//@ts-expect-error - this field is not available
40 | 			unavalibleField: "123", //this should be not allowed
41 | 		});
42 | 	});
43 | 
44 | 	it("should infer filed when schema is provided", () => {
45 | 		const client = createAuthClient({
46 | 			plugins: [
47 | 				organizationClient({
48 | 					schema: inferOrgAdditionalFields({
49 | 						organization: {
50 | 							additionalFields: {
51 | 								newField: {
52 | 									type: "string",
53 | 								},
54 | 							},
55 | 						},
56 | 					}),
57 | 				}),
58 | 			],
59 | 			fetchOptions: {
60 | 				customFetchImpl: async () => new Response(),
61 | 			},
62 | 		});
63 | 
64 | 		client.organization.create({
65 | 			name: "Test",
66 | 			slug: "test",
67 | 			newField: "123", //this should be allowed
68 | 			//@ts-expect-error - this field is not available
69 | 			unavalibleField: "123", //this should be not allowed
70 | 		});
71 | 	});
72 | });
73 | 
```

--------------------------------------------------------------------------------
/e2e/smoke/test/vite.spec.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { describe, it } from "node:test";
 2 | import { fileURLToPath } from "node:url";
 3 | import { join } from "node:path";
 4 | import { spawn } from "node:child_process";
 5 | import { readFile } from "node:fs/promises";
 6 | import * as assert from "node:assert";
 7 | 
 8 | const fixturesDir = fileURLToPath(new URL("./fixtures", import.meta.url));
 9 | 
10 | describe("(vite) client build", () => {
11 | 	it("builds client without better-call imports", async () => {
12 | 		const viteDir = join(fixturesDir, "vite");
13 | 
14 | 		// Run vite build
15 | 		const buildProcess = spawn("npx", ["vite", "build"], {
16 | 			cwd: viteDir,
17 | 			stdio: "pipe",
18 | 		});
19 | 
20 | 		// Wait for build to complete
21 | 		await new Promise<void>((resolve, reject) => {
22 | 			buildProcess.on("close", (code) => {
23 | 				if (code === 0) {
24 | 					resolve();
25 | 				} else {
26 | 					reject(new Error(`Vite build failed with code ${code}`));
27 | 				}
28 | 			});
29 | 
30 | 			buildProcess.on("error", (error) => {
31 | 				reject(error);
32 | 			});
33 | 
34 | 			// Log build output for debugging
35 | 			buildProcess.stdout.on("data", (data) => {
36 | 				console.log(data.toString());
37 | 			});
38 | 
39 | 			buildProcess.stderr.on("data", (data) => {
40 | 				console.error(data.toString());
41 | 			});
42 | 		});
43 | 
44 | 		const clientFile = join(viteDir, "dist", "client.js");
45 | 		const clientContent = await readFile(clientFile, "utf-8");
46 | 
47 | 		assert.ok(
48 | 			!clientContent.includes("createEndpoint"),
49 | 			"Built output should not contain 'better-call' imports",
50 | 		);
51 | 
52 | 		assert.ok(
53 | 			!clientContent.includes("async_hooks"),
54 | 			"Built output should not contain 'async_hooks' imports",
55 | 		);
56 | 
57 | 		assert.ok(
58 | 			!clientContent.includes("AsyncLocalStorage"),
59 | 			"Built output should not contain 'AsyncLocalStorage' imports",
60 | 		);
61 | 	});
62 | });
63 | 
```

--------------------------------------------------------------------------------
/packages/better-auth/src/adapters/prisma-adapter/test/prisma.pg.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { testAdapter } from "../../test-adapter";
 2 | import {
 3 | 	authFlowTestSuite,
 4 | 	normalTestSuite,
 5 | 	numberIdTestSuite,
 6 | 	performanceTestSuite,
 7 | 	transactionsTestSuite,
 8 | } from "../../tests";
 9 | import { prismaAdapter } from "../prisma-adapter";
10 | import { generateAuthConfigFile } from "./generate-auth-config";
11 | import { generatePrismaSchema } from "./generate-prisma-schema";
12 | import { pushPrismaSchema } from "./push-prisma-schema";
13 | import type { BetterAuthOptions } from "@better-auth/core";
14 | import {
15 | 	destroyPrismaClient,
16 | 	getPrismaClient,
17 | 	incrementMigrationCount,
18 | } from "./get-prisma-client";
19 | import { Pool } from "pg";
20 | 
21 | const dialect = "postgresql";
22 | const { execute } = await testAdapter({
23 | 	adapter: async () => {
24 | 		const db = await getPrismaClient(dialect);
25 | 		return prismaAdapter(db, {
26 | 			provider: dialect,
27 | 			debugLogs: { isRunningAdapterTests: true },
28 | 		});
29 | 	},
30 | 	runMigrations: async (options: BetterAuthOptions) => {
31 | 		const db = await getPrismaClient(dialect);
32 | 		const pgDB = new Pool({
33 | 			connectionString: "postgres://user:password@localhost:5434/better_auth",
34 | 		});
35 | 		await pgDB.query(`DROP SCHEMA public CASCADE; CREATE SCHEMA public;`);
36 | 		await pgDB.end();
37 | 		const migrationCount = incrementMigrationCount();
38 | 		await generateAuthConfigFile(options);
39 | 		await generatePrismaSchema(options, db, migrationCount, dialect);
40 | 		await pushPrismaSchema(dialect);
41 | 		destroyPrismaClient({ migrationCount: migrationCount - 1, dialect });
42 | 	},
43 | 	tests: [
44 | 		normalTestSuite(),
45 | 		transactionsTestSuite(),
46 | 		authFlowTestSuite(),
47 | 		numberIdTestSuite(),
48 | 		performanceTestSuite({ dialect }),
49 | 	],
50 | 	onFinish: async () => {},
51 | 	prefixTests: "pg",
52 | });
53 | 
54 | execute();
55 | 
```

--------------------------------------------------------------------------------
/packages/stripe/src/schema.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type { BetterAuthPluginDBSchema } from "@better-auth/core/db";
 2 | import type { StripeOptions } from "./types";
 3 | import { mergeSchema } from "better-auth/db";
 4 | 
 5 | export const subscriptions = {
 6 | 	subscription: {
 7 | 		fields: {
 8 | 			plan: {
 9 | 				type: "string",
10 | 				required: true,
11 | 			},
12 | 			referenceId: {
13 | 				type: "string",
14 | 				required: true,
15 | 			},
16 | 			stripeCustomerId: {
17 | 				type: "string",
18 | 				required: false,
19 | 			},
20 | 			stripeSubscriptionId: {
21 | 				type: "string",
22 | 				required: false,
23 | 			},
24 | 			status: {
25 | 				type: "string",
26 | 				defaultValue: "incomplete",
27 | 			},
28 | 			periodStart: {
29 | 				type: "date",
30 | 				required: false,
31 | 			},
32 | 			periodEnd: {
33 | 				type: "date",
34 | 				required: false,
35 | 			},
36 | 			trialStart: {
37 | 				type: "date",
38 | 				required: false,
39 | 			},
40 | 			trialEnd: {
41 | 				type: "date",
42 | 				required: false,
43 | 			},
44 | 			cancelAtPeriodEnd: {
45 | 				type: "boolean",
46 | 				required: false,
47 | 				defaultValue: false,
48 | 			},
49 | 			seats: {
50 | 				type: "number",
51 | 				required: false,
52 | 			},
53 | 		},
54 | 	},
55 | } satisfies BetterAuthPluginDBSchema;
56 | 
57 | export const user = {
58 | 	user: {
59 | 		fields: {
60 | 			stripeCustomerId: {
61 | 				type: "string",
62 | 				required: false,
63 | 			},
64 | 		},
65 | 	},
66 | } satisfies BetterAuthPluginDBSchema;
67 | 
68 | export const getSchema = (options: StripeOptions) => {
69 | 	let baseSchema = {};
70 | 
71 | 	if (options.subscription?.enabled) {
72 | 		baseSchema = {
73 | 			...subscriptions,
74 | 			...user,
75 | 		};
76 | 	} else {
77 | 		baseSchema = {
78 | 			...user,
79 | 		};
80 | 	}
81 | 
82 | 	if (
83 | 		options.schema &&
84 | 		!options.subscription?.enabled &&
85 | 		"subscription" in options.schema
86 | 	) {
87 | 		const { subscription, ...restSchema } = options.schema;
88 | 		return mergeSchema(baseSchema, restSchema);
89 | 	}
90 | 
91 | 	return mergeSchema(baseSchema, options.schema);
92 | };
93 | 
```

--------------------------------------------------------------------------------
/packages/better-auth/src/adapters/prisma-adapter/test/prisma.sqlite.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { testAdapter } from "../../test-adapter";
 2 | import {
 3 | 	authFlowTestSuite,
 4 | 	normalTestSuite,
 5 | 	numberIdTestSuite,
 6 | 	performanceTestSuite,
 7 | 	transactionsTestSuite,
 8 | } from "../../tests";
 9 | import { prismaAdapter } from "../prisma-adapter";
10 | import { generateAuthConfigFile } from "./generate-auth-config";
11 | import { generatePrismaSchema } from "./generate-prisma-schema";
12 | import { pushPrismaSchema } from "./push-prisma-schema";
13 | import type { BetterAuthOptions } from "@better-auth/core";
14 | import { join } from "path";
15 | import fs from "node:fs/promises";
16 | import {
17 | 	destroyPrismaClient,
18 | 	getPrismaClient,
19 | 	incrementMigrationCount,
20 | } from "./get-prisma-client";
21 | 
22 | const dialect = "sqlite";
23 | const { execute } = await testAdapter({
24 | 	adapter: async () => {
25 | 		const db = await getPrismaClient(dialect);
26 | 		return prismaAdapter(db, {
27 | 			provider: dialect,
28 | 			debugLogs: { isRunningAdapterTests: true },
29 | 		});
30 | 	},
31 | 	runMigrations: async (options: BetterAuthOptions) => {
32 | 		const dbPath = join(import.meta.dirname, "dev.db");
33 | 		try {
34 | 			await fs.unlink(dbPath);
35 | 		} catch {
36 | 			console.log("db path not found");
37 | 		}
38 | 		const db = await getPrismaClient(dialect);
39 | 		const migrationCount = incrementMigrationCount();
40 | 		await generateAuthConfigFile(options);
41 | 		await generatePrismaSchema(options, db, migrationCount, dialect);
42 | 		await pushPrismaSchema(dialect);
43 | 		await db.$disconnect();
44 | 		destroyPrismaClient({ migrationCount: migrationCount - 1, dialect });
45 | 	},
46 | 	tests: [
47 | 		normalTestSuite({}),
48 | 		transactionsTestSuite(),
49 | 		authFlowTestSuite(),
50 | 		numberIdTestSuite({}),
51 | 		performanceTestSuite({ dialect }),
52 | 	],
53 | 	onFinish: async () => {},
54 | 	prefixTests: dialect,
55 | });
56 | 
57 | execute();
58 | 
```

--------------------------------------------------------------------------------
/packages/better-auth/src/adapters/prisma-adapter/test/generate-prisma-schema.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type { PrismaClient } from "@prisma/client";
 2 | import type { BetterAuthOptions } from "@better-auth/core";
 3 | import type { DBAdapter } from "@better-auth/core/db/adapter";
 4 | import { prismaAdapter } from "../prisma-adapter";
 5 | import { join } from "path";
 6 | import fs from "fs/promises";
 7 | 
 8 | export async function generatePrismaSchema(
 9 | 	betterAuthOptions: BetterAuthOptions,
10 | 	db: PrismaClient,
11 | 	iteration: number,
12 | 	dialect: "sqlite" | "postgresql" | "mysql",
13 | ) {
14 | 	const i = async (x: string) => await import(x);
15 | 	const { generateSchema } = (await i(
16 | 		"./../../../../../cli/src/generators/index",
17 | 	)) as {
18 | 		generateSchema: (opts: {
19 | 			adapter: DBAdapter<BetterAuthOptions>;
20 | 			file?: string;
21 | 			options: BetterAuthOptions;
22 | 		}) => Promise<{
23 | 			code: string | undefined;
24 | 			fileName: string;
25 | 			overwrite: boolean | undefined;
26 | 		}>;
27 | 	};
28 | 
29 | 	const prismaDB = prismaAdapter(db, { provider: dialect });
30 | 	let { fileName, code } = await generateSchema({
31 | 		file: join(import.meta.dirname, `schema-${dialect}.prisma`),
32 | 		adapter: prismaDB({}),
33 | 		options: { ...betterAuthOptions, database: prismaDB },
34 | 	});
35 | 	if (dialect === "postgresql") {
36 | 		code = code?.replace(
37 | 			`env("DATABASE_URL")`,
38 | 			'"postgres://user:password@localhost:5434/better_auth"',
39 | 		);
40 | 	} else if (dialect === "mysql") {
41 | 		code = code?.replace(
42 | 			`env("DATABASE_URL")`,
43 | 			'"mysql://user:password@localhost:3308/better_auth"',
44 | 		);
45 | 	}
46 | 	code = code
47 | 		?.split("\n")
48 | 		.map((line, index) => {
49 | 			if (index === 2) {
50 | 				return (
51 | 					line + `\n  output   = "./.tmp/prisma-client-${dialect}-${iteration}"`
52 | 				);
53 | 			}
54 | 			return line;
55 | 		})
56 | 		.join("\n");
57 | 	await fs.writeFile(fileName, code || "", "utf-8");
58 | }
59 | 
```

--------------------------------------------------------------------------------
/packages/better-auth/src/adapters/kysely-adapter/test/adapter.kysely.mysql.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Kysely, MysqlDialect } from "kysely";
 2 | import { testAdapter } from "../../test-adapter";
 3 | import { kyselyAdapter } from "../kysely-adapter";
 4 | import { createPool } from "mysql2/promise";
 5 | import {
 6 | 	authFlowTestSuite,
 7 | 	normalTestSuite,
 8 | 	numberIdTestSuite,
 9 | 	performanceTestSuite,
10 | 	transactionsTestSuite,
11 | } from "../../tests";
12 | import { getMigrations } from "../../../db";
13 | import { assert } from "vitest";
14 | 
15 | const mysqlDB = createPool({
16 | 	uri: "mysql://user:password@localhost:3307/better_auth",
17 | 	timezone: "Z",
18 | });
19 | 
20 | let kyselyDB = new Kysely({
21 | 	dialect: new MysqlDialect(mysqlDB),
22 | });
23 | 
24 | const { execute } = await testAdapter({
25 | 	adapter: () =>
26 | 		kyselyAdapter(kyselyDB, {
27 | 			type: "mysql",
28 | 			debugLogs: { isRunningAdapterTests: true },
29 | 		}),
30 | 	async runMigrations(betterAuthOptions) {
31 | 		await mysqlDB.query("DROP DATABASE IF EXISTS better_auth");
32 | 		await mysqlDB.query("CREATE DATABASE better_auth");
33 | 		await mysqlDB.query("USE better_auth");
34 | 		const opts = Object.assign(betterAuthOptions, { database: mysqlDB });
35 | 		const { runMigrations } = await getMigrations(opts);
36 | 		await runMigrations();
37 | 
38 | 		// ensure migrations were run successfully
39 | 		const [tables_result] = (await mysqlDB.query("SHOW TABLES")) as unknown as [
40 | 			{ Tables_in_better_auth: string }[],
41 | 		];
42 | 		const tables = tables_result.map((table) => table.Tables_in_better_auth);
43 | 		assert(tables.length > 0, "No tables found");
44 | 	},
45 | 	prefixTests: "mysql",
46 | 	tests: [
47 | 		normalTestSuite(),
48 | 		transactionsTestSuite({ disableTests: { ALL: true } }),
49 | 		authFlowTestSuite(),
50 | 		numberIdTestSuite(),
51 | 		performanceTestSuite({ dialect: "mysql" }),
52 | 	],
53 | 	async onFinish() {
54 | 		await mysqlDB.end();
55 | 	},
56 | });
57 | execute();
58 | 
```

--------------------------------------------------------------------------------
/demo/nextjs/components/ui/button.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | import * as React from "react";
 2 | import { Slot } from "@radix-ui/react-slot";
 3 | import { cva, type VariantProps } from "class-variance-authority";
 4 | 
 5 | import { cn } from "@/lib/utils";
 6 | 
 7 | const buttonVariants = cva(
 8 | 	"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
 9 | 	{
10 | 		variants: {
11 | 			variant: {
12 | 				default:
13 | 					"bg-primary text-primary-foreground shadow hover:bg-primary/90",
14 | 				destructive:
15 | 					"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
16 | 				outline:
17 | 					"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
18 | 				secondary:
19 | 					"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
20 | 				ghost: "hover:bg-accent hover:text-accent-foreground",
21 | 				link: "text-primary underline-offset-4 hover:underline",
22 | 			},
23 | 			size: {
24 | 				default: "h-9 px-4 py-2",
25 | 				sm: "h-8 rounded-md px-3 text-xs",
26 | 				lg: "h-10 rounded-md px-8",
27 | 				icon: "h-9 w-9",
28 | 			},
29 | 		},
30 | 		defaultVariants: {
31 | 			variant: "default",
32 | 			size: "default",
33 | 		},
34 | 	},
35 | );
36 | 
37 | export interface ButtonProps
38 | 	extends React.ButtonHTMLAttributes<HTMLButtonElement>,
39 | 		VariantProps<typeof buttonVariants> {
40 | 	asChild?: boolean;
41 | }
42 | 
43 | const Button = ({
44 | 	className,
45 | 	variant,
46 | 	size,
47 | 	asChild = false,
48 | 	...props
49 | }: ButtonProps) => {
50 | 	const Comp = asChild ? Slot : "button";
51 | 	return (
52 | 		<Comp
53 | 			className={cn(buttonVariants({ variant, size, className }))}
54 | 			{...props}
55 | 		/>
56 | 	);
57 | };
58 | Button.displayName = "Button";
59 | 
60 | export { Button, buttonVariants };
61 | 
```

--------------------------------------------------------------------------------
/docs/lib/blog.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { readFile, readdir } from "fs/promises";
 2 | import matter from "gray-matter";
 3 | import { join } from "path";
 4 | import { cache } from "react";
 5 | 
 6 | export interface BlogPost {
 7 | 	_id: string;
 8 | 	slug: string;
 9 | 	title: string;
10 | 	description?: string;
11 | 	date: string;
12 | 	content: string;
13 | 	image?: string;
14 | 	author?: {
15 | 		name: string;
16 | 		avatar?: string;
17 | 		twitter?: string;
18 | 	};
19 | 	tags?: string[];
20 | }
21 | 
22 | const BLOGS_PATH = join(process.cwd(), "docs/content/blogs");
23 | 
24 | export const getBlogPost = cache(
25 | 	async (slug: string): Promise<BlogPost | null> => {
26 | 		try {
27 | 			const filePath = join(BLOGS_PATH, `${slug}.mdx`);
28 | 			const source = await readFile(filePath, "utf-8");
29 | 			const { data, content } = matter(source);
30 | 
31 | 			return {
32 | 				_id: slug,
33 | 				slug,
34 | 				content,
35 | 				title: data.title,
36 | 				description: data.description,
37 | 				date: data.date,
38 | 				image: data.image,
39 | 				author: data.author,
40 | 				tags: data.tags,
41 | 			};
42 | 		} catch (error) {
43 | 			return null;
44 | 		}
45 | 	},
46 | );
47 | 
48 | export const getAllBlogPosts = cache(async (): Promise<BlogPost[]> => {
49 | 	try {
50 | 		const files = await readdir(BLOGS_PATH);
51 | 		const mdxFiles = files.filter((file) => file.endsWith(".mdx"));
52 | 
53 | 		const posts = await Promise.all(
54 | 			mdxFiles.map(async (file) => {
55 | 				const slug = file.replace(/\.mdx$/, "");
56 | 				const post = await getBlogPost(slug);
57 | 				return post;
58 | 			}),
59 | 		);
60 | 
61 | 		return posts
62 | 			.filter((post): post is BlogPost => post !== null)
63 | 			.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
64 | 	} catch (error) {
65 | 		return [];
66 | 	}
67 | });
68 | 
69 | export function formatBlogDate(date: Date) {
70 | 	let d = new Date(date);
71 | 	return d.toLocaleDateString("en-US", {
72 | 		month: "short",
73 | 		day: "numeric",
74 | 		year: "numeric",
75 | 	});
76 | }
77 | 
```

--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: Feature request
 2 | description: Suggest an idea for this project
 3 | body:
 4 |   - type: checkboxes
 5 |     attributes:
 6 |       label: Is this suited for github?
 7 |       description: Feel free to join the discord community [here](https://discord.gg/better-auth), we can usually respond faster to any questions.
 8 |       options:
 9 |         - label: Yes, this is suited for github
10 |   - type: markdown
11 |     attributes:
12 |       value: |
13 |         This template is used for suggesting a feature with better-auth.
14 | 
15 |         Bug reports should be opened in [here](https://github.com/better-auth/better-auth/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml).
16 | 
17 |         Before opening a feature request, please do a [search](https://github.com/better-auth/better-auth/issues) of existing issues and :+1: upvote the existing request instead. This will result in a quicker resolution.
18 |   - type: textarea
19 |     attributes:
20 |       label: Is your feature request related to a problem? Please describe.
21 |       description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
22 |   - type: textarea
23 |     attributes:
24 |       label: Describe the solution you'd like
25 |       description: A clear and concise description of what you want to happen.
26 |     validations:
27 |       required: true
28 |   - type: textarea
29 |     attributes:
30 |       label: Describe alternatives you've considered
31 |       description: A clear and concise description of any alternative solutions or features you've considered.
32 |     validations:
33 |       required: true
34 |   - type: textarea
35 |     attributes:
36 |       label: Additional context
37 |       description: Add any other context or screenshots about the feature request here.
38 | 
```

--------------------------------------------------------------------------------
/docs/components/docs/docs.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | import type { PageTree } from "fumadocs-core/server";
 2 | import { type ReactNode, type HTMLAttributes } from "react";
 3 | import { cn } from "../../lib/utils";
 4 | import { type BaseLayoutProps } from "./shared";
 5 | import { TreeContextProvider } from "fumadocs-ui/provider";
 6 | import { NavProvider } from "./layout/nav";
 7 | import { type PageStyles, StylesProvider } from "fumadocs-ui/provider";
 8 | import ArticleLayout from "../side-bar";
 9 | 
10 | export interface DocsLayoutProps extends BaseLayoutProps {
11 | 	tree: PageTree.Root;
12 | 
13 | 	containerProps?: HTMLAttributes<HTMLDivElement>;
14 | }
15 | 
16 | export function DocsLayout({ children, ...props }: DocsLayoutProps): ReactNode {
17 | 	const variables = cn(
18 | 		"[--fd-tocnav-height:36px] md:[--fd-sidebar-width:268px] lg:[--fd-sidebar-width:286px] xl:[--fd-toc-width:286px] xl:[--fd-tocnav-height:0px]",
19 | 	);
20 | 
21 | 	const pageStyles: PageStyles = {
22 | 		tocNav: cn("xl:hidden"),
23 | 		toc: cn("max-xl:hidden"),
24 | 	};
25 | 
26 | 	return (
27 | 		<TreeContextProvider tree={props.tree}>
28 | 			<NavProvider>
29 | 				<main
30 | 					id="nd-docs-layout"
31 | 					{...props.containerProps}
32 | 					className={cn(
33 | 						"flex flex-1 flex-row pe-(--fd-layout-offset)",
34 | 						variables,
35 | 						props.containerProps?.className,
36 | 					)}
37 | 					style={
38 | 						{
39 | 							"--fd-layout-offset":
40 | 								"max(calc(50vw - var(--fd-layout-width) / 2), 0px)",
41 | 							...props.containerProps?.style,
42 | 						} as object
43 | 					}
44 | 				>
45 | 					<div
46 | 						className={cn(
47 | 							"[--fd-tocnav-height:36px] md:mr-[268px] lg:mr-[286px] xl:[--fd-toc-width:286px] xl:[--fd-tocnav-height:0px] ",
48 | 						)}
49 | 					>
50 | 						<ArticleLayout />
51 | 					</div>
52 | 					<StylesProvider {...pageStyles}>{children}</StylesProvider>
53 | 				</main>
54 | 			</NavProvider>
55 | 		</TreeContextProvider>
56 | 	);
57 | }
58 | 
```

--------------------------------------------------------------------------------
/packages/better-auth/src/types/helper.ts:
--------------------------------------------------------------------------------

```typescript
 1 | export type Primitive =
 2 | 	| string
 3 | 	| number
 4 | 	| symbol
 5 | 	| bigint
 6 | 	| boolean
 7 | 	| null
 8 | 	| undefined;
 9 | export type LiteralString = "" | (string & Record<never, never>);
10 | export type LiteralNumber = 0 | (number & Record<never, never>);
11 | 
12 | export type Awaitable<T> = Promise<T> | T;
13 | export type OmitId<T extends { id: unknown }> = Omit<T, "id">;
14 | 
15 | export type Prettify<T> = Omit<T, never>;
16 | export type PreserveJSDoc<T> = {
17 | 	[K in keyof T]: T[K];
18 | } & {};
19 | export type PrettifyDeep<T> = {
20 | 	[K in keyof T]: T[K] extends (...args: any[]) => any
21 | 		? T[K]
22 | 		: T[K] extends object
23 | 			? T[K] extends Array<any>
24 | 				? T[K]
25 | 				: T[K] extends Date
26 | 					? T[K]
27 | 					: PrettifyDeep<T[K]>
28 | 			: T[K];
29 | } & {};
30 | export type LiteralUnion<LiteralType, BaseType extends Primitive> =
31 | 	| LiteralType
32 | 	| (BaseType & Record<never, never>);
33 | 
34 | export type UnionToIntersection<U> = (
35 | 	U extends any
36 | 		? (k: U) => void
37 | 		: never
38 | ) extends (k: infer I) => void
39 | 	? I
40 | 	: never;
41 | 
42 | export type RequiredKeysOf<BaseType extends object> = Exclude<
43 | 	{
44 | 		[Key in keyof BaseType]: BaseType extends Record<Key, BaseType[Key]>
45 | 			? Key
46 | 			: never;
47 | 	}[keyof BaseType],
48 | 	undefined
49 | >;
50 | 
51 | export type HasRequiredKeys<BaseType extends object> =
52 | 	RequiredKeysOf<BaseType> extends never ? false : true;
53 | export type WithoutEmpty<T> = T extends T ? ({} extends T ? never : T) : never;
54 | 
55 | export type StripEmptyObjects<T> = T extends { [K in keyof T]: never }
56 | 	? never
57 | 	: T extends object
58 | 		? { [K in keyof T as T[K] extends never ? never : K]: T[K] }
59 | 		: T;
60 | export type DeepPartial<T> = T extends Function
61 | 	? T
62 | 	: T extends object
63 | 		? { [K in keyof T]?: DeepPartial<T[K]> }
64 | 		: T;
65 | export type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
66 | 
```

--------------------------------------------------------------------------------
/demo/expo-example/src/components/icons/google.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | import Svg, { Path, SvgProps } from "react-native-svg";
 2 | 
 3 | export function GoogleIcon(props: SvgProps) {
 4 | 	return (
 5 | 		<Svg width="1em" height="1em" viewBox="0 0 128 128">
 6 | 			<Path
 7 | 				fill="#fff"
 8 | 				d="M44.59 4.21a63.28 63.28 0 0 0 4.33 120.9a67.6 67.6 0 0 0 32.36.35a57.13 57.13 0 0 0 25.9-13.46a57.44 57.44 0 0 0 16-26.26a74.3 74.3 0 0 0 1.61-33.58H65.27v24.69h34.47a29.72 29.72 0 0 1-12.66 19.52a36.2 36.2 0 0 1-13.93 5.5a41.3 41.3 0 0 1-15.1 0A37.2 37.2 0 0 1 44 95.74a39.3 39.3 0 0 1-14.5-19.42a38.3 38.3 0 0 1 0-24.63a39.25 39.25 0 0 1 9.18-14.91A37.17 37.17 0 0 1 76.13 27a34.3 34.3 0 0 1 13.64 8q5.83-5.8 11.64-11.63c2-2.09 4.18-4.08 6.15-6.22A61.2 61.2 0 0 0 87.2 4.59a64 64 0 0 0-42.61-.38"
 9 | 			></Path>
10 | 			<Path
11 | 				fill="#e33629"
12 | 				d="M44.59 4.21a64 64 0 0 1 42.61.37a61.2 61.2 0 0 1 20.35 12.62c-2 2.14-4.11 4.14-6.15 6.22Q95.58 29.23 89.77 35a34.3 34.3 0 0 0-13.64-8a37.17 37.17 0 0 0-37.46 9.74a39.25 39.25 0 0 0-9.18 14.91L8.76 35.6A63.53 63.53 0 0 1 44.59 4.21"
13 | 			></Path>
14 | 			<Path
15 | 				fill="#f8bd00"
16 | 				d="M3.26 51.5a63 63 0 0 1 5.5-15.9l20.73 16.09a38.3 38.3 0 0 0 0 24.63q-10.36 8-20.73 16.08a63.33 63.33 0 0 1-5.5-40.9"
17 | 			></Path>
18 | 			<Path
19 | 				fill="#587dbd"
20 | 				d="M65.27 52.15h59.52a74.3 74.3 0 0 1-1.61 33.58a57.44 57.44 0 0 1-16 26.26c-6.69-5.22-13.41-10.4-20.1-15.62a29.72 29.72 0 0 0 12.66-19.54H65.27c-.01-8.22 0-16.45 0-24.68"
21 | 			></Path>
22 | 			<Path
23 | 				fill="#319f43"
24 | 				d="M8.75 92.4q10.37-8 20.73-16.08A39.3 39.3 0 0 0 44 95.74a37.2 37.2 0 0 0 14.08 6.08a41.3 41.3 0 0 0 15.1 0a36.2 36.2 0 0 0 13.93-5.5c6.69 5.22 13.41 10.4 20.1 15.62a57.13 57.13 0 0 1-25.9 13.47a67.6 67.6 0 0 1-32.36-.35a63 63 0 0 1-23-11.59A63.7 63.7 0 0 1 8.75 92.4"
25 | 			></Path>
26 | 		</Svg>
27 | 	);
28 | }
29 | 
```

--------------------------------------------------------------------------------
/demo/expo-example/src/global.css:
--------------------------------------------------------------------------------

```css
 1 | @tailwind base;
 2 | @tailwind components;
 3 | @tailwind utilities;
 4 | 
 5 | @layer base {
 6 | 	:root {
 7 | 		--background: 0 0% 100%;
 8 | 		--foreground: 224 71.4% 4.1%;
 9 | 		--card: 0 0% 100%;
10 | 		--card-foreground: 224 71.4% 4.1%;
11 | 		--popover: 0 0% 100%;
12 | 		--popover-foreground: 224 71.4% 4.1%;
13 | 		--primary: 220.9 39.3% 11%;
14 | 		--primary-foreground: 210 20% 98%;
15 | 		--secondary: 220 14.3% 95.9%;
16 | 		--secondary-foreground: 220.9 39.3% 11%;
17 | 		--muted: 220 14.3% 95.9%;
18 | 		--muted-foreground: 220 8.9% 46.1%;
19 | 		--accent: 220 14.3% 95.9%;
20 | 		--accent-foreground: 220.9 39.3% 11%;
21 | 		--destructive: 0 84.2% 60.2%;
22 | 		--destructive-foreground: 210 20% 98%;
23 | 		--border: 220 13% 91%;
24 | 		--input: 220 13% 91%;
25 | 		--ring: 224 71.4% 4.1%;
26 | 		--radius: 0.5rem;
27 | 		--chart-1: 12 76% 61%;
28 | 		--chart-2: 173 58% 39%;
29 | 		--chart-3: 197 37% 24%;
30 | 		--chart-4: 43 74% 66%;
31 | 		--chart-5: 27 87% 67%;
32 | 	}
33 | 
34 | 	.dark {
35 | 		--background: 224 71.4% 4.1%;
36 | 		--foreground: 210 20% 98%;
37 | 		--card: 224 71.4% 4.1%;
38 | 		--card-foreground: 210 20% 98%;
39 | 		--popover: 224 71.4% 4.1%;
40 | 		--popover-foreground: 210 20% 98%;
41 | 		--primary: 210 20% 98%;
42 | 		--primary-foreground: 220.9 39.3% 11%;
43 | 		--secondary: 215 27.9% 16.9%;
44 | 		--secondary-foreground: 210 20% 98%;
45 | 		--muted: 215 27.9% 16.9%;
46 | 		--muted-foreground: 217.9 10.6% 64.9%;
47 | 		--accent: 215 27.9% 16.9%;
48 | 		--accent-foreground: 210 20% 98%;
49 | 		--destructive: 0 62.8% 30.6%;
50 | 		--destructive-foreground: 210 20% 98%;
51 | 		--border: 215 27.9% 16.9%;
52 | 		--input: 215 27.9% 16.9%;
53 | 		--ring: 216 12.2% 83.9%;
54 | 		--chart-1: 220 70% 50%;
55 | 		--chart-2: 160 60% 45%;
56 | 		--chart-3: 30 80% 55%;
57 | 		--chart-4: 280 65% 60%;
58 | 		--chart-5: 340 75% 55%;
59 | 	}
60 | }
61 | 
62 | @layer base {
63 | 	* {
64 | 		@apply border-border;
65 | 	}
66 | 	body {
67 | 		@apply bg-background text-foreground;
68 | 	}
69 | }
70 | 
```

--------------------------------------------------------------------------------
/demo/nextjs/components/ui/resizable.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | "use client";
 2 | 
 3 | import { DragHandleDots2Icon } from "@radix-ui/react-icons";
 4 | import * as ResizablePrimitive from "react-resizable-panels";
 5 | 
 6 | import { cn } from "@/lib/utils";
 7 | 
 8 | const ResizablePanelGroup = ({
 9 | 	className,
10 | 	...props
11 | }: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (
12 | 	<ResizablePrimitive.PanelGroup
13 | 		className={cn(
14 | 			"flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
15 | 			className,
16 | 		)}
17 | 		{...props}
18 | 	/>
19 | );
20 | 
21 | const ResizablePanel = ResizablePrimitive.Panel;
22 | 
23 | const ResizableHandle = ({
24 | 	withHandle,
25 | 	className,
26 | 	...props
27 | }: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
28 | 	withHandle?: boolean;
29 | }) => (
30 | 	<ResizablePrimitive.PanelResizeHandle
31 | 		className={cn(
32 | 			"relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
33 | 			className,
34 | 		)}
35 | 		{...props}
36 | 	>
37 | 		{withHandle && (
38 | 			<div className="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
39 | 				<DragHandleDots2Icon className="h-2.5 w-2.5" />
40 | 			</div>
41 | 		)}
42 | 	</ResizablePrimitive.PanelResizeHandle>
43 | );
44 | 
45 | export { ResizablePanelGroup, ResizablePanel, ResizableHandle };
46 | 
```

--------------------------------------------------------------------------------
/packages/better-auth/src/adapters/prisma-adapter/test/prisma.mysql.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { testAdapter } from "../../test-adapter";
 2 | import {
 3 | 	authFlowTestSuite,
 4 | 	normalTestSuite,
 5 | 	numberIdTestSuite,
 6 | 	performanceTestSuite,
 7 | 	transactionsTestSuite,
 8 | } from "../../tests";
 9 | import { prismaAdapter } from "../prisma-adapter";
10 | import { generateAuthConfigFile } from "./generate-auth-config";
11 | import { generatePrismaSchema } from "./generate-prisma-schema";
12 | import { pushPrismaSchema } from "./push-prisma-schema";
13 | import type { BetterAuthOptions } from "@better-auth/core";
14 | import {
15 | 	destroyPrismaClient,
16 | 	getPrismaClient,
17 | 	incrementMigrationCount,
18 | } from "./get-prisma-client";
19 | import { createPool } from "mysql2/promise";
20 | 
21 | const dialect = "mysql";
22 | const { execute } = await testAdapter({
23 | 	adapter: async () => {
24 | 		const db = await getPrismaClient(dialect);
25 | 		return prismaAdapter(db, {
26 | 			provider: dialect,
27 | 			debugLogs: { isRunningAdapterTests: true },
28 | 		});
29 | 	},
30 | 	runMigrations: async (options: BetterAuthOptions) => {
31 | 		const mysqlDB = createPool({
32 | 			uri: "mysql://user:password@localhost:3308/better_auth",
33 | 			timezone: "Z",
34 | 		});
35 | 		await mysqlDB.query("DROP DATABASE IF EXISTS better_auth");
36 | 		await mysqlDB.query("CREATE DATABASE better_auth");
37 | 		await mysqlDB.end();
38 | 		const db = await getPrismaClient(dialect);
39 | 		const migrationCount = incrementMigrationCount();
40 | 		await generateAuthConfigFile(options);
41 | 		await generatePrismaSchema(options, db, migrationCount, dialect);
42 | 		await pushPrismaSchema(dialect);
43 | 		destroyPrismaClient({ migrationCount: migrationCount - 1, dialect });
44 | 	},
45 | 	tests: [
46 | 		normalTestSuite(),
47 | 		transactionsTestSuite(),
48 | 		authFlowTestSuite(),
49 | 		numberIdTestSuite(),
50 | 		performanceTestSuite({ dialect }),
51 | 	],
52 | 	onFinish: async () => {},
53 | 	prefixTests: dialect,
54 | });
55 | 
56 | execute();
57 | 
```

--------------------------------------------------------------------------------
/docs/components/ui/tabs.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | "use client";
 2 | 
 3 | import * as React from "react";
 4 | import * as TabsPrimitive from "@radix-ui/react-tabs";
 5 | 
 6 | import { cn } from "@/lib/utils";
 7 | 
 8 | function Tabs({
 9 | 	className,
10 | 	...props
11 | }: React.ComponentProps<typeof TabsPrimitive.Root>) {
12 | 	return (
13 | 		<TabsPrimitive.Root
14 | 			data-slot="tabs"
15 | 			className={cn("flex flex-col gap-2", className)}
16 | 			{...props}
17 | 		/>
18 | 	);
19 | }
20 | 
21 | function TabsList({
22 | 	className,
23 | 	...props
24 | }: React.ComponentProps<typeof TabsPrimitive.List>) {
25 | 	return (
26 | 		<TabsPrimitive.List
27 | 			data-slot="tabs-list"
28 | 			className={cn(
29 | 				"bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-1",
30 | 				className,
31 | 			)}
32 | 			{...props}
33 | 		/>
34 | 	);
35 | }
36 | 
37 | function TabsTrigger({
38 | 	className,
39 | 	...props
40 | }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
41 | 	return (
42 | 		<TabsPrimitive.Trigger
43 | 			data-slot="tabs-trigger"
44 | 			className={cn(
45 | 				"data-[state=active]:bg-background data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring inline-flex flex-1 items-center justify-center gap-1.5 rounded-md px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
46 | 				className,
47 | 			)}
48 | 			{...props}
49 | 		/>
50 | 	);
51 | }
52 | 
53 | function TabsContent({
54 | 	className,
55 | 	...props
56 | }: React.ComponentProps<typeof TabsPrimitive.Content>) {
57 | 	return (
58 | 		<TabsPrimitive.Content
59 | 			data-slot="tabs-content"
60 | 			className={cn("flex-1 outline-none", className)}
61 | 			{...props}
62 | 		/>
63 | 	);
64 | }
65 | 
66 | export { Tabs, TabsList, TabsTrigger, TabsContent };
67 | 
```

--------------------------------------------------------------------------------
/docs/content/blogs/seed-round.mdx:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: "Announcing our $5M seed round"
 3 | description: "We raised $5M seed led by Peak XV Partners"
 4 | date: 2025-06-24
 5 | author:
 6 |   name: "Bereket Engida"
 7 |   avatar: "/blogs/bereket.png"
 8 |   twitter: "iambereket"
 9 | image: "/blogs/seed-round.png"
10 | tags: ["seed round", "authentication", "funding"]
11 | ---
12 | 
13 | ## Announcing our $5M seed round
14 | 
15 | We’re excited to share that Better Auth has raised a $5 million seed round led by Peak XV Partners (formerly Sequoia Capital India & SEA), with participation from Y Combinator, Chapter One, P1 Ventures, and a group of incredible investors and angels.
16 | 
17 | This funding fuels the next phase of **Better Auth**.
18 | 
19 | From the start we are obsessed with making it possible for developers to **own their auth**. To **democratize high quality authentication** and make rolling your own auth not just doable, but the obvious choice.
20 | 
21 | It started with building the framework. Since then, we’ve seen incredible growth and support from the community. Thank you everyone for being part of this journey. It’s still early days, and there’s so much more to build. This funding will allow us to have more people invloved and to push the boundaries of what's possible.
22 | 
23 | On top of the framework, we’re also building the infrastructure to cover the gaps we couldn't cover in the framework:
24 | 
25 | * A unified dashboard to manage users and user analytics
26 | * Enterprise-grade security: bot, abuse, and fraud protection
27 | * Authentication Email and SMS service
28 | * Fast, globally distributed session storage
29 | * and more.
30 | 
31 | [Join the waitlist](https://better-auth.build) to get early access to the infrastructure.
32 | 
33 | And if you're excited about making auth accessible - we're hiring!
34 | 
35 | Reach out to [[email protected]](mailto:[email protected]).
```

--------------------------------------------------------------------------------
/packages/better-auth/src/client/react/react-store.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { listenKeys } from "nanostores";
 2 | import { useCallback, useRef, useSyncExternalStore } from "react";
 3 | import type { Store, StoreValue } from "nanostores";
 4 | import type { DependencyList } from "react";
 5 | 
 6 | type StoreKeys<T> = T extends { setKey: (k: infer K, v: any) => unknown }
 7 | 	? K
 8 | 	: never;
 9 | 
10 | export interface UseStoreOptions<SomeStore> {
11 | 	/**
12 | 	 * @default
13 | 	 * ```ts
14 | 	 * [store, options.keys]
15 | 	 * ```
16 | 	 */
17 | 	deps?: DependencyList;
18 | 
19 | 	/**
20 | 	 * Will re-render components only on specific key changes.
21 | 	 */
22 | 	keys?: StoreKeys<SomeStore>[];
23 | }
24 | 
25 | /**
26 |  * Subscribe to store changes and get store's value.
27 |  *
28 |  * Can be used with store builder too.
29 |  *
30 |  * ```js
31 |  * import { useStore } from 'nanostores/react'
32 |  *
33 |  * import { router } from '../store/router'
34 |  *
35 |  * export const Layout = () => {
36 |  *   let page = useStore(router)
37 |  *   if (page.route === 'home') {
38 |  *     return <HomePage />
39 |  *   } else {
40 |  *     return <Error404 />
41 |  *   }
42 |  * }
43 |  * ```
44 |  *
45 |  * @param store Store instance.
46 |  * @returns Store value.
47 |  */
48 | export function useStore<SomeStore extends Store>(
49 | 	store: SomeStore,
50 | 	options: UseStoreOptions<SomeStore> = {},
51 | ): StoreValue<SomeStore> {
52 | 	let snapshotRef = useRef<StoreValue<SomeStore>>(store.get());
53 | 
54 | 	const { keys, deps = [store, keys] } = options;
55 | 
56 | 	let subscribe = useCallback((onChange: () => void) => {
57 | 		const emitChange = (value: StoreValue<SomeStore>) => {
58 | 			if (snapshotRef.current === value) return;
59 | 			snapshotRef.current = value;
60 | 			onChange();
61 | 		};
62 | 
63 | 		emitChange(store.value);
64 | 		if (keys?.length) {
65 | 			return listenKeys(store as any, keys, emitChange);
66 | 		}
67 | 		return store.listen(emitChange);
68 | 	}, deps);
69 | 
70 | 	let get = () => snapshotRef.current as StoreValue<SomeStore>;
71 | 
72 | 	return useSyncExternalStore(subscribe, get, get);
73 | }
74 | 
```

--------------------------------------------------------------------------------
/packages/better-auth/src/plugins/captcha/verify-handlers/google-recaptcha.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { betterFetch } from "@better-fetch/fetch";
 2 | import { middlewareResponse } from "../../../utils/middleware-response";
 3 | import { EXTERNAL_ERROR_CODES, INTERNAL_ERROR_CODES } from "../error-codes";
 4 | import { encodeToURLParams } from "../utils";
 5 | 
 6 | type Params = {
 7 | 	siteVerifyURL: string;
 8 | 	secretKey: string;
 9 | 	captchaResponse: string;
10 | 	minScore?: number;
11 | 	remoteIP?: string;
12 | };
13 | 
14 | type SiteVerifyResponse = {
15 | 	success: boolean;
16 | 	challenge_ts: string;
17 | 	hostname: string;
18 | 	"error-codes":
19 | 		| Array<
20 | 				| "missing-input-secret"
21 | 				| "invalid-input-secret"
22 | 				| "missing-input-response"
23 | 				| "invalid-input-response"
24 | 				| "bad-request"
25 | 				| "timeout-or-duplicate"
26 | 		  >
27 | 		| undefined;
28 | };
29 | 
30 | type SiteVerifyV3Response = SiteVerifyResponse & {
31 | 	score: number;
32 | };
33 | 
34 | const isV3 = (
35 | 	response: SiteVerifyResponse | SiteVerifyV3Response,
36 | ): response is SiteVerifyV3Response => {
37 | 	return "score" in response && typeof response.score === "number";
38 | };
39 | 
40 | export const googleRecaptcha = async ({
41 | 	siteVerifyURL,
42 | 	captchaResponse,
43 | 	secretKey,
44 | 	minScore = 0.5,
45 | 	remoteIP,
46 | }: Params) => {
47 | 	const response = await betterFetch<SiteVerifyResponse | SiteVerifyV3Response>(
48 | 		siteVerifyURL,
49 | 		{
50 | 			method: "POST",
51 | 			headers: { "Content-Type": "application/x-www-form-urlencoded" },
52 | 			body: encodeToURLParams({
53 | 				secret: secretKey,
54 | 				response: captchaResponse,
55 | 				...(remoteIP && { remoteip: remoteIP }),
56 | 			}),
57 | 		},
58 | 	);
59 | 
60 | 	if (!response.data || response.error) {
61 | 		throw new Error(INTERNAL_ERROR_CODES.SERVICE_UNAVAILABLE);
62 | 	}
63 | 
64 | 	if (
65 | 		!response.data.success ||
66 | 		(isV3(response.data) && response.data.score < minScore)
67 | 	) {
68 | 		return middlewareResponse({
69 | 			message: EXTERNAL_ERROR_CODES.VERIFICATION_FAILED,
70 | 			status: 403,
71 | 		});
72 | 	}
73 | 
74 | 	return undefined;
75 | };
76 | 
```

--------------------------------------------------------------------------------
/packages/better-auth/src/client/lynx/lynx-store.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { listenKeys } from "nanostores";
 2 | import { useCallback, useRef, useSyncExternalStore } from "@lynx-js/react";
 3 | import type { Store, StoreValue } from "nanostores";
 4 | import type { DependencyList } from "@lynx-js/react";
 5 | 
 6 | type StoreKeys<T> = T extends { setKey: (k: infer K, v: any) => unknown }
 7 | 	? K
 8 | 	: never;
 9 | 
10 | export interface UseStoreOptions<SomeStore> {
11 | 	/**
12 | 	 * @default
13 | 	 * ```ts
14 | 	 * [store, options.keys]
15 | 	 * ```
16 | 	 */
17 | 	deps?: DependencyList;
18 | 
19 | 	/**
20 | 	 * Will re-render components only on specific key changes.
21 | 	 */
22 | 	keys?: StoreKeys<SomeStore>[];
23 | }
24 | 
25 | /**
26 |  * Subscribe to store changes and get store's value.
27 |  *
28 |  * Can be used with store builder too.
29 |  *
30 |  * ```js
31 |  * import { useStore } from 'nanostores/react'
32 |  *
33 |  * import { router } from '../store/router'
34 |  *
35 |  * export const Layout = () => {
36 |  *   let page = useStore(router)
37 |  *   if (page.route === 'home') {
38 |  *     return <HomePage />
39 |  *   } else {
40 |  *     return <Error404 />
41 |  *   }
42 |  * }
43 |  * ```
44 |  *
45 |  * @param store Store instance.
46 |  * @returns Store value.
47 |  */
48 | export function useStore<SomeStore extends Store>(
49 | 	store: SomeStore,
50 | 	options: UseStoreOptions<SomeStore> = {},
51 | ): StoreValue<SomeStore> {
52 | 	let snapshotRef = useRef<StoreValue<SomeStore>>(store.get());
53 | 
54 | 	const { keys, deps = [store, keys] } = options;
55 | 
56 | 	let subscribe = useCallback((onChange: () => void) => {
57 | 		const emitChange = (value: StoreValue<SomeStore>) => {
58 | 			if (snapshotRef.current === value) return;
59 | 			snapshotRef.current = value;
60 | 			onChange();
61 | 		};
62 | 
63 | 		emitChange(store.value);
64 | 		if (keys?.length) {
65 | 			return listenKeys(store as any, keys, emitChange);
66 | 		}
67 | 		return store.listen(emitChange);
68 | 	}, deps);
69 | 
70 | 	let get = () => snapshotRef.current as StoreValue<SomeStore>;
71 | 
72 | 	return useSyncExternalStore(subscribe, get, get);
73 | }
74 | 
```

--------------------------------------------------------------------------------
/docs/content/docs/authentication/vk.mdx:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: VK
 3 | description: VK ID Provider
 4 | ---
 5 | 
 6 | <Steps>
 7 |     <Step>
 8 |         ### Get your VK ID credentials
 9 |         To use VK ID sign in, you need a client ID and client secret. You can get them from the [VK ID Developer Portal](https://id.vk.com/about/business/go/docs).
10 | 
11 |         Make sure to set the redirect URL to `http://localhost:3000/api/auth/callback/vk` for local development. For production, you should set it to the URL of your application. If you change the base path of the auth routes, you should update the redirect URL accordingly.
12 |     </Step>
13 | 
14 |     <Step>
15 |         ### Configure the provider
16 |         To configure the provider, you need to import the provider and pass it to the `socialProviders` option of the auth instance.
17 |         ```ts title="auth.ts"
18 |         import { betterAuth } from "better-auth";
19 | 
20 |         export const auth = betterAuth({
21 |           socialProviders: {
22 |             vk: { // [!code highlight]
23 |               clientId: process.env.VK_CLIENT_ID as string, // [!code highlight]
24 |               clientSecret: process.env.VK_CLIENT_SECRET as string, // [!code highlight]
25 |             },
26 |           },
27 |         });
28 |         ```
29 |     </Step>
30 |     <Step>
31 |         ### Sign In with VK
32 |         To sign in with VK, you can use the `signIn.social` function provided by the client. The `signIn` function takes an object with the following properties:
33 |         - `provider`: The provider to use. It should be set to `vk`.
34 | 
35 | 
36 |         ```ts title="auth-client.ts"
37 |         import { createAuthClient } from "better-auth/client";
38 |         const authClient = createAuthClient();
39 | 
40 |         const signIn = async () => {
41 |           const data = await authClient.signIn.social({
42 |             provider: "vk",
43 |           });
44 |         };
45 |         ```
46 |     </Step>
47 | 
48 | </Steps>
49 | 
```

--------------------------------------------------------------------------------
/packages/better-auth/src/plugins/captcha/verify-handlers/h-captcha.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { betterFetch } from "@better-fetch/fetch";
 2 | import { middlewareResponse } from "../../../utils/middleware-response";
 3 | import { EXTERNAL_ERROR_CODES, INTERNAL_ERROR_CODES } from "../error-codes";
 4 | import { encodeToURLParams } from "../utils";
 5 | 
 6 | type Params = {
 7 | 	siteVerifyURL: string;
 8 | 	secretKey: string;
 9 | 	captchaResponse: string;
10 | 	siteKey?: string;
11 | 	remoteIP?: string;
12 | };
13 | 
14 | type SiteVerifyResponse = {
15 | 	success: boolean;
16 | 	challenge_ts: number;
17 | 	hostname: string;
18 | 	credit: true | false | undefined;
19 | 	"error-codes":
20 | 		| Array<
21 | 				| "missing-input-secret"
22 | 				| "invalid-input-secret"
23 | 				| "missing-input-response"
24 | 				| "invalid-input-response"
25 | 				| "expired-input-response"
26 | 				| "already-seen-response"
27 | 				| "bad-request"
28 | 				| "missing-remoteip"
29 | 				| "invalid-remoteip"
30 | 				| "not-using-dummy-passcode"
31 | 				| "sitekey-secret-mismatch"
32 | 		  >
33 | 		| undefined;
34 | 	score: number | undefined; // ENTERPRISE feature: a score denoting malicious activity.
35 | 	score_reason: Array<unknown> | undefined; // ENTERPRISE feature: reason(s) for score.
36 | };
37 | 
38 | export const hCaptcha = async ({
39 | 	siteVerifyURL,
40 | 	captchaResponse,
41 | 	secretKey,
42 | 	siteKey,
43 | 	remoteIP,
44 | }: Params) => {
45 | 	const response = await betterFetch<SiteVerifyResponse>(siteVerifyURL, {
46 | 		method: "POST",
47 | 		headers: { "Content-Type": "application/x-www-form-urlencoded" },
48 | 		body: encodeToURLParams({
49 | 			secret: secretKey,
50 | 			response: captchaResponse,
51 | 			...(siteKey && { sitekey: siteKey }),
52 | 			...(remoteIP && { remoteip: remoteIP }),
53 | 		}),
54 | 	});
55 | 
56 | 	if (!response.data || response.error) {
57 | 		throw new Error(INTERNAL_ERROR_CODES.SERVICE_UNAVAILABLE);
58 | 	}
59 | 
60 | 	if (!response.data.success) {
61 | 		return middlewareResponse({
62 | 			message: EXTERNAL_ERROR_CODES.VERIFICATION_FAILED,
63 | 			status: 403,
64 | 		});
65 | 	}
66 | 
67 | 	return undefined;
68 | };
69 | 
```

--------------------------------------------------------------------------------
/docs/components/ui/tooltip.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | "use client";
 2 | 
 3 | import * as React from "react";
 4 | import * as TooltipPrimitive from "@radix-ui/react-tooltip";
 5 | 
 6 | import { cn } from "@/lib/utils";
 7 | 
 8 | function TooltipProvider({
 9 | 	delayDuration = 0,
10 | 	...props
11 | }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
12 | 	return (
13 | 		<TooltipPrimitive.Provider
14 | 			data-slot="tooltip-provider"
15 | 			delayDuration={delayDuration}
16 | 			{...props}
17 | 		/>
18 | 	);
19 | }
20 | 
21 | function Tooltip({
22 | 	...props
23 | }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
24 | 	return (
25 | 		<TooltipProvider>
26 | 			<TooltipPrimitive.Root data-slot="tooltip" {...props} />
27 | 		</TooltipProvider>
28 | 	);
29 | }
30 | 
31 | function TooltipTrigger({
32 | 	...props
33 | }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
34 | 	return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
35 | }
36 | 
37 | function TooltipContent({
38 | 	className,
39 | 	sideOffset = 0,
40 | 	children,
41 | 	...props
42 | }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
43 | 	return (
44 | 		<TooltipPrimitive.Portal>
45 | 			<TooltipPrimitive.Content
46 | 				data-slot="tooltip-content"
47 | 				sideOffset={sideOffset}
48 | 				className={cn(
49 | 					"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit rounded-md px-3 py-1.5 text-xs text-balance",
50 | 					className,
51 | 				)}
52 | 				{...props}
53 | 			>
54 | 				{children}
55 | 				<TooltipPrimitive.Arrow className="-z-10 relative bg-primary fill-primary size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
56 | 			</TooltipPrimitive.Content>
57 | 		</TooltipPrimitive.Portal>
58 | 	);
59 | }
60 | 
61 | export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
62 | 
```

--------------------------------------------------------------------------------
/packages/better-auth/src/plugins/bearer/bearer.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { describe, expect, it } from "vitest";
 2 | import { getTestInstance } from "../../test-utils/test-instance";
 3 | 
 4 | describe("bearer", async () => {
 5 | 	const { client, auth, testUser } = await getTestInstance(
 6 | 		{},
 7 | 		{
 8 | 			disableTestUser: true,
 9 | 		},
10 | 	);
11 | 
12 | 	let token: string;
13 | 	it("should get session", async () => {
14 | 		await client.signUp.email(
15 | 			{
16 | 				email: testUser.email,
17 | 				password: testUser.password,
18 | 				name: testUser.name,
19 | 			},
20 | 			{
21 | 				onSuccess: (ctx) => {
22 | 					token = ctx.response.headers.get("set-auth-token") || "";
23 | 				},
24 | 			},
25 | 		);
26 | 		const session = await client.getSession({
27 | 			fetchOptions: {
28 | 				headers: {
29 | 					Authorization: `Bearer ${token}`,
30 | 				},
31 | 			},
32 | 		});
33 | 		expect(session.data?.session).toBeDefined();
34 | 	});
35 | 
36 | 	it("should list session", async () => {
37 | 		const sessions = await client.listSessions({
38 | 			fetchOptions: {
39 | 				headers: {
40 | 					Authorization: `Bearer ${token}`,
41 | 				},
42 | 			},
43 | 		});
44 | 		expect(sessions.data).toHaveLength(1);
45 | 	});
46 | 
47 | 	it("should work on server actions", async () => {
48 | 		const session = await auth.api.getSession({
49 | 			headers: new Headers({
50 | 				authorization: `Bearer ${token}`,
51 | 			}),
52 | 		});
53 | 		expect(session?.session).toBeDefined();
54 | 	});
55 | 
56 | 	it("should work with ", async () => {
57 | 		const session = await client.getSession({
58 | 			fetchOptions: {
59 | 				headers: {
60 | 					authorization: `Bearer ${token.split(".")[0]}`,
61 | 				},
62 | 			},
63 | 		});
64 | 		expect(session.data?.session).toBeDefined();
65 | 	});
66 | 
67 | 	it("should work if valid cookie is provided even if authorization header isn't valid", async () => {
68 | 		const session = await client.getSession({
69 | 			fetchOptions: {
70 | 				headers: {
71 | 					Authorization: `Bearer invalid.token`,
72 | 					cookie: `better-auth.session_token=${token}`,
73 | 				},
74 | 			},
75 | 		});
76 | 		expect(session.data?.session).toBeDefined();
77 | 	});
78 | });
79 | 
```

--------------------------------------------------------------------------------
/packages/cli/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "@better-auth/cli",
 3 |   "version": "1.4.0-beta.10",
 4 |   "type": "module",
 5 |   "description": "The CLI for Better Auth",
 6 |   "module": "dist/index.js",
 7 |   "repository": {
 8 |     "type": "git",
 9 |     "url": "https://github.com/better-auth/better-auth",
10 |     "directory": "packages/cli"
11 |   },
12 |   "homepage": "https://www.better-auth.com/docs/concepts/cli",
13 |   "main": "./dist/index.js",
14 |   "scripts": {
15 |     "build": "tsdown",
16 |     "start": "node ./dist/index.js",
17 |     "dev": "tsx ./src/index.ts",
18 |     "test": "vitest",
19 |     "typecheck": "tsc --project tsconfig.json"
20 |   },
21 |   "publishConfig": {
22 |     "access": "public",
23 |     "executableFiles": [
24 |       "./dist/index.js"
25 |     ]
26 |   },
27 |   "license": "MIT",
28 |   "keywords": [
29 |     "auth",
30 |     "cli",
31 |     "typescript",
32 |     "better-auth"
33 |   ],
34 |   "exports": "./dist/index.js",
35 |   "bin": "./dist/index.js",
36 |   "devDependencies": {
37 |     "@types/semver": "^7.7.1",
38 |     "tsx": "^4.20.5",
39 |     "typescript": "catalog:",
40 |     "tsdown": "catalog:"
41 |   },
42 |   "dependencies": {
43 |     "@babel/core": "^7.28.4",
44 |     "@babel/preset-react": "^7.27.1",
45 |     "@babel/preset-typescript": "^7.27.1",
46 |     "@better-auth/utils": "0.3.0",
47 |     "@clack/prompts": "^0.11.0",
48 |     "@mrleebo/prisma-ast": "^0.13.0",
49 |     "@prisma/client": "^5.22.0",
50 |     "@types/better-sqlite3": "^7.6.13",
51 |     "@types/prompts": "^2.4.9",
52 |     "better-auth": "workspace:*",
53 |     "better-sqlite3": "^12.2.0",
54 |     "c12": "^3.2.0",
55 |     "chalk": "^5.6.2",
56 |     "commander": "^12.1.0",
57 |     "dotenv": "^17.2.2",
58 |     "drizzle-orm": "^0.33.0",
59 |     "get-tsconfig": "^4.10.1",
60 |     "jiti": "^2.6.0",
61 |     "open": "^10.2.0",
62 |     "prettier": "^3.6.2",
63 |     "prisma": "^5.22.0",
64 |     "prompts": "^2.4.2",
65 |     "semver": "^7.7.2",
66 |     "tinyexec": "^0.3.2",
67 |     "yocto-spinner": "^0.2.3",
68 |     "zod": "^4.1.5"
69 |   },
70 |   "files": [
71 |     "dist"
72 |   ]
73 | }
74 | 
```

--------------------------------------------------------------------------------
/packages/core/src/types/plugin-client.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type { BetterAuthPlugin } from "./plugin";
 2 | import type {
 3 | 	BetterFetch,
 4 | 	BetterFetchOption,
 5 | 	BetterFetchPlugin,
 6 | } from "@better-fetch/fetch";
 7 | import type { LiteralString } from "./helper";
 8 | import type { BetterAuthOptions } from "./init-options";
 9 | import type { WritableAtom, Atom } from "nanostores";
10 | 
11 | export interface ClientStore {
12 | 	notify: (signal: string) => void;
13 | 	listen: (signal: string, listener: () => void) => void;
14 | 	atoms: Record<string, WritableAtom<any>>;
15 | }
16 | 
17 | export type ClientAtomListener = {
18 | 	matcher: (path: string) => boolean;
19 | 	signal: "$sessionSignal" | Omit<string, "$sessionSignal">;
20 | };
21 | 
22 | export interface BetterAuthClientOptions {
23 | 	fetchOptions?: BetterFetchOption;
24 | 	plugins?: BetterAuthClientPlugin[];
25 | 	baseURL?: string;
26 | 	basePath?: string;
27 | 	disableDefaultFetchPlugins?: boolean;
28 | 	$InferAuth?: BetterAuthOptions;
29 | }
30 | 
31 | export interface BetterAuthClientPlugin {
32 | 	id: LiteralString;
33 | 	/**
34 | 	 * only used for type inference. don't pass the
35 | 	 * actual plugin
36 | 	 */
37 | 	$InferServerPlugin?: BetterAuthPlugin;
38 | 	/**
39 | 	 * Custom actions
40 | 	 */
41 | 	getActions?: (
42 | 		$fetch: BetterFetch,
43 | 		$store: ClientStore,
44 | 		/**
45 | 		 * better-auth client options
46 | 		 */
47 | 		options: BetterAuthClientOptions | undefined,
48 | 	) => Record<string, any>;
49 | 	/**
50 | 	 * State atoms that'll be resolved by each framework
51 | 	 * auth store.
52 | 	 */
53 | 	getAtoms?: ($fetch: BetterFetch) => Record<string, Atom<any>>;
54 | 	/**
55 | 	 * specify path methods for server plugin inferred
56 | 	 * endpoints to force a specific method.
57 | 	 */
58 | 	pathMethods?: Record<string, "POST" | "GET">;
59 | 	/**
60 | 	 * Better fetch plugins
61 | 	 */
62 | 	fetchPlugins?: BetterFetchPlugin[];
63 | 	/**
64 | 	 * a list of recaller based on a matcher function.
65 | 	 * The signal name needs to match a signal in this
66 | 	 * plugin or any plugin the user might have added.
67 | 	 */
68 | 	atomListeners?: ClientAtomListener[];
69 | }
70 | 
```

--------------------------------------------------------------------------------
/demo/nextjs/components/ui/tooltip.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | "use client";
 2 | 
 3 | import * as React from "react";
 4 | import * as TooltipPrimitive from "@radix-ui/react-tooltip";
 5 | 
 6 | import { cn } from "@/lib/utils";
 7 | 
 8 | function TooltipProvider({
 9 | 	delayDuration = 0,
10 | 	...props
11 | }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
12 | 	return (
13 | 		<TooltipPrimitive.Provider
14 | 			data-slot="tooltip-provider"
15 | 			delayDuration={delayDuration}
16 | 			{...props}
17 | 		/>
18 | 	);
19 | }
20 | 
21 | function Tooltip({
22 | 	...props
23 | }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
24 | 	return (
25 | 		<TooltipProvider>
26 | 			<TooltipPrimitive.Root data-slot="tooltip" {...props} />
27 | 		</TooltipProvider>
28 | 	);
29 | }
30 | 
31 | function TooltipTrigger({
32 | 	...props
33 | }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
34 | 	return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
35 | }
36 | 
37 | function TooltipContent({
38 | 	className,
39 | 	sideOffset = 0,
40 | 	children,
41 | 	...props
42 | }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
43 | 	return (
44 | 		<TooltipPrimitive.Portal>
45 | 			<TooltipPrimitive.Content
46 | 				data-slot="tooltip-content"
47 | 				sideOffset={sideOffset}
48 | 				className={cn(
49 | 					"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit rounded-md px-3 py-1.5 text-xs text-balance",
50 | 					className,
51 | 				)}
52 | 				{...props}
53 | 			>
54 | 				{children}
55 | 				<TooltipPrimitive.Arrow className="-z-10 relative bg-primary dark:bg-stone-900 dark:fill-stone-900 fill-primary size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px]" />
56 | 			</TooltipPrimitive.Content>
57 | 		</TooltipPrimitive.Portal>
58 | 	);
59 | }
60 | 
61 | export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
62 | 
```

--------------------------------------------------------------------------------
/docs/components/ui/toggle-group.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | "use client";
 2 | 
 3 | import * as React from "react";
 4 | import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
 5 | import { type VariantProps } from "class-variance-authority";
 6 | 
 7 | import { cn } from "@/lib/utils";
 8 | import { toggleVariants } from "@/components/ui/toggle";
 9 | 
10 | const ToggleGroupContext = React.createContext<
11 | 	VariantProps<typeof toggleVariants>
12 | >({
13 | 	size: "default",
14 | 	variant: "default",
15 | });
16 | 
17 | function ToggleGroup({
18 | 	className,
19 | 	variant,
20 | 	size,
21 | 	children,
22 | 	...props
23 | }: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &
24 | 	VariantProps<typeof toggleVariants>) {
25 | 	return (
26 | 		<ToggleGroupPrimitive.Root
27 | 			data-slot="toggle-group"
28 | 			data-variant={variant}
29 | 			data-size={size}
30 | 			className={cn(
31 | 				"group/toggle-group flex w-fit items-center rounded-md data-[variant=outline]:shadow-xs",
32 | 				className,
33 | 			)}
34 | 			{...props}
35 | 		>
36 | 			<ToggleGroupContext.Provider value={{ variant, size }}>
37 | 				{children}
38 | 			</ToggleGroupContext.Provider>
39 | 		</ToggleGroupPrimitive.Root>
40 | 	);
41 | }
42 | 
43 | function ToggleGroupItem({
44 | 	className,
45 | 	children,
46 | 	variant,
47 | 	size,
48 | 	...props
49 | }: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &
50 | 	VariantProps<typeof toggleVariants>) {
51 | 	const context = React.useContext(ToggleGroupContext);
52 | 
53 | 	return (
54 | 		<ToggleGroupPrimitive.Item
55 | 			data-slot="toggle-group-item"
56 | 			data-variant={context.variant || variant}
57 | 			data-size={context.size || size}
58 | 			className={cn(
59 | 				toggleVariants({
60 | 					variant: context.variant || variant,
61 | 					size: context.size || size,
62 | 				}),
63 | 				"min-w-0 flex-1 shrink-0 rounded-none shadow-none first:rounded-l-md last:rounded-r-md focus:z-10 focus-visible:z-10 data-[variant=outline]:border-l-0 data-[variant=outline]:first:border-l",
64 | 				className,
65 | 			)}
66 | 			{...props}
67 | 		>
68 | 			{children}
69 | 		</ToggleGroupPrimitive.Item>
70 | 	);
71 | }
72 | 
73 | export { ToggleGroup, ToggleGroupItem };
74 | 
```

--------------------------------------------------------------------------------
/docs/components/ui/callout.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | import { CircleCheck, CircleX, Info, TriangleAlert } from "lucide-react";
 2 | import { forwardRef, type HTMLAttributes, type ReactNode } from "react";
 3 | import { cn } from "@/lib/utils";
 4 | import { cva } from "class-variance-authority";
 5 | 
 6 | type CalloutProps = Omit<
 7 | 	HTMLAttributes<HTMLDivElement>,
 8 | 	"title" | "type" | "icon"
 9 | > & {
10 | 	title?: ReactNode;
11 | 	/**
12 | 	 * @defaultValue info
13 | 	 */
14 | 	type?: "info" | "warn" | "error" | "success" | "warning";
15 | 
16 | 	/**
17 | 	 * Force an icon
18 | 	 */
19 | 	icon?: ReactNode;
20 | };
21 | 
22 | const calloutVariants = cva(
23 | 	"my-4 flex gap-2 rounded-lg border border-s-2 bg-fd-card p-3 text-sm text-fd-card-foreground shadow-md border-dashed rounded-none",
24 | 	{
25 | 		variants: {
26 | 			type: {
27 | 				info: "border-s-blue-500/50",
28 | 				warn: "border-s-orange-500/50",
29 | 				error: "border-s-red-500/50",
30 | 				success: "border-s-green-500/50",
31 | 			},
32 | 		},
33 | 	},
34 | );
35 | 
36 | export const Callout = forwardRef<HTMLDivElement, CalloutProps>(
37 | 	({ className, children, title, type = "info", icon, ...props }, ref) => {
38 | 		if (type === "warning") type = "warn";
39 | 
40 | 		return (
41 | 			<div
42 | 				ref={ref}
43 | 				className={cn(
44 | 					calloutVariants({
45 | 						type: type,
46 | 					}),
47 | 					className,
48 | 				)}
49 | 				{...props}
50 | 			>
51 | 				{icon ??
52 | 					{
53 | 						info: <Info className="size-5 fill-blue-500 text-fd-card" />,
54 | 						warn: (
55 | 							<TriangleAlert className="size-5 fill-orange-500 text-fd-card" />
56 | 						),
57 | 						error: <CircleX className="size-5 fill-red-500 text-fd-card" />,
58 | 						success: (
59 | 							<CircleCheck className="size-5 fill-green-500 text-fd-card" />
60 | 						),
61 | 					}[type]}
62 | 				<div className="min-w-0 flex flex-col gap-2 flex-1">
63 | 					{title ? <p className="font-medium !my-0">{title}</p> : null}
64 | 					<div className="text-fd-muted-foreground prose-no-margin empty:hidden">
65 | 						{children}
66 | 					</div>
67 | 				</div>
68 | 			</div>
69 | 		);
70 | 	},
71 | );
72 | 
73 | Callout.displayName = "Callout";
74 | 
```

--------------------------------------------------------------------------------
/packages/better-auth/src/plugins/last-login-method/client.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type { BetterAuthClientPlugin } from "@better-auth/core";
 2 | 
 3 | /**
 4 |  * Configuration for the client-side last login method plugin
 5 |  */
 6 | export interface LastLoginMethodClientConfig {
 7 | 	/**
 8 | 	 * Name of the cookie to read the last login method from
 9 | 	 * @default "better-auth.last_used_login_method"
10 | 	 */
11 | 	cookieName?: string;
12 | }
13 | 
14 | function getCookieValue(name: string): string | null {
15 | 	if (typeof document === "undefined") {
16 | 		return null;
17 | 	}
18 | 
19 | 	const cookie = document.cookie
20 | 		.split("; ")
21 | 		.find((row) => row.startsWith(`${name}=`));
22 | 
23 | 	return cookie ? cookie.split("=")[1]! : null;
24 | }
25 | 
26 | /**
27 |  * Client-side plugin to retrieve the last used login method
28 |  */
29 | export const lastLoginMethodClient = (
30 | 	config: LastLoginMethodClientConfig = {},
31 | ) => {
32 | 	const cookieName = config.cookieName || "better-auth.last_used_login_method";
33 | 
34 | 	return {
35 | 		id: "last-login-method-client",
36 | 		getActions() {
37 | 			return {
38 | 				/**
39 | 				 * Get the last used login method from cookies
40 | 				 * @returns The last used login method or null if not found
41 | 				 */
42 | 				getLastUsedLoginMethod: (): string | null => {
43 | 					return getCookieValue(cookieName);
44 | 				},
45 | 				/**
46 | 				 * Clear the last used login method cookie
47 | 				 * This sets the cookie with an expiration date in the past
48 | 				 */
49 | 				clearLastUsedLoginMethod: (): void => {
50 | 					if (typeof document !== "undefined") {
51 | 						document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
52 | 					}
53 | 				},
54 | 				/**
55 | 				 * Check if a specific login method was the last used
56 | 				 * @param method The method to check
57 | 				 * @returns True if the method was the last used, false otherwise
58 | 				 */
59 | 				isLastUsedLoginMethod: (method: string): boolean => {
60 | 					const lastMethod = getCookieValue(cookieName);
61 | 					return lastMethod === method;
62 | 				},
63 | 			};
64 | 		},
65 | 	} satisfies BetterAuthClientPlugin;
66 | };
67 | 
```

--------------------------------------------------------------------------------
/docs/content/docs/authentication/naver.mdx:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: Naver
 3 | description: Naver provider setup and usage.
 4 | ---
 5 | 
 6 | <Steps>
 7 |     <Step> 
 8 |         ### Get your Naver Credentials
 9 |         To use Naver sign in, you need a client ID and client secret. You can get them from the [Naver Developers](https://developers.naver.com/).
10 | 
11 |         Make sure to set the redirect URL to `http://localhost:3000/api/auth/callback/naver` for local development. For production, you should set it to the URL of your application. If you change the base path of the auth routes, you should update the redirect URL accordingly.
12 |     </Step>
13 |     <Step>
14 |         ### Configure the provider
15 |         To configure the provider, you need to import the provider and pass it to the `socialProviders` option of the auth instance.
16 | 
17 |         ```ts title="auth.ts"
18 |         import { betterAuth } from "better-auth"
19 | 
20 |         export const auth = betterAuth({
21 |             socialProviders: {
22 |                 naver: { // [!code highlight]
23 |                     clientId: process.env.NAVER_CLIENT_ID as string, // [!code highlight]
24 |                     clientSecret: process.env.NAVER_CLIENT_SECRET as string, // [!code highlight]
25 |                 }, // [!code highlight]
26 |             }
27 |         })
28 |         ```
29 |     </Step>
30 |     <Step>
31 |         ### Sign In with Naver
32 |         To sign in with Naver, you can use the `signIn.social` function provided by the client. The `signIn` function takes an object with the following properties:
33 |         - `provider`: The provider to use. It should be set to `naver`.
34 | 
35 |         ```ts title="auth-client.ts"
36 |         import { createAuthClient } from "better-auth/client"
37 |         const authClient =  createAuthClient()
38 | 
39 |         const signIn = async () => {
40 |             const data = await authClient.signIn.social({
41 |                 provider: "naver"
42 |             })
43 |         }
44 |         ```
45 |     </Step>
46 | 
47 | </Steps>
48 | 
```

--------------------------------------------------------------------------------
/docs/content/docs/authentication/kakao.mdx:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: Kakao
 3 | description: Kakao provider setup and usage.
 4 | ---
 5 | 
 6 | <Steps>
 7 |     <Step> 
 8 |         ### Get your Kakao Credentials
 9 |         To use Kakao sign in, you need a client ID and client secret. You can get them from the [Kakao Developer Portal](https://developers.kakao.com).
10 | 
11 |         Make sure to set the redirect URL to `http://localhost:3000/api/auth/callback/kakao` for local development. For production, you should set it to the URL of your application. If you change the base path of the auth routes, you should update the redirect URL accordingly.
12 |     </Step>
13 |     <Step>
14 |         ### Configure the provider
15 |         To configure the provider, you need to import the provider and pass it to the `socialProviders` option of the auth instance.
16 | 
17 |         ```ts title="auth.ts"
18 |         import { betterAuth } from "better-auth"
19 | 
20 |         export const auth = betterAuth({
21 |             socialProviders: {
22 |                 kakao: { // [!code highlight]
23 |                     clientId: process.env.KAKAO_CLIENT_ID as string, // [!code highlight]
24 |                     clientSecret: process.env.KAKAO_CLIENT_SECRET as string, // [!code highlight]
25 |                 }, // [!code highlight]
26 |             }
27 |         })
28 |         ```
29 |     </Step>
30 |     <Step>
31 |         ### Sign In with Kakao
32 |         To sign in with Kakao, you can use the `signIn.social` function provided by the client. The `signIn` function takes an object with the following properties:
33 |         - `provider`: The provider to use. It should be set to `kakao`.
34 | 
35 |         ```ts title="auth-client.ts"
36 |         import { createAuthClient } from "better-auth/client"
37 |         const authClient =  createAuthClient()
38 | 
39 |         const signIn = async () => {
40 |             const data = await authClient.signIn.social({
41 |                 provider: "kakao"
42 |             })
43 |         }
44 |         ```
45 |     </Step>
46 | 
47 | </Steps>
48 | 
```

--------------------------------------------------------------------------------
/docs/components/resource-section.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | "use client";
 2 | import { cn } from "@/lib/utils";
 3 | import { useState } from "react";
 4 | import { ResourceCard } from "./resource-card";
 5 | 
 6 | interface Resource {
 7 | 	title: string;
 8 | 	description: string;
 9 | 	href: string;
10 | 	tags: string[];
11 | }
12 | 
13 | interface ResourceProps {
14 | 	resources: Resource[];
15 | 	className?: string;
16 | }
17 | 
18 | export function Resource({ className, resources }: ResourceProps) {
19 | 	const [activeTag, setActiveTag] = useState<string | null>(null);
20 | 	const tags = Array.from(
21 | 		new Set(resources.flatMap((resource) => resource.tags)),
22 | 	);
23 | 	const filterResources = (activeTag: string | null): Resource[] => {
24 | 		if (!activeTag) return resources;
25 | 		return resources.filter((resource) => resource.tags.includes(activeTag));
26 | 	};
27 | 	return (
28 | 		<div>
29 | 			<div className={cn("space-y-4", className)}>
30 | 				<div className="flex flex-wrap gap-2">
31 | 					<button
32 | 						onClick={() => setActiveTag(null)}
33 | 						className={cn(
34 | 							"inline-flex items-center rounded-md px-3 py-1 text-sm font-medium transition-colors",
35 | 							activeTag === null
36 | 								? "bg-primary text-primary-foreground"
37 | 								: "bg-secondary/10 text-secondary-foreground hover:bg-secondary/20",
38 | 						)}
39 | 					>
40 | 						All
41 | 					</button>
42 | 					{tags.map((tag) => (
43 | 						<button
44 | 							key={tag}
45 | 							onClick={() => setActiveTag(tag)}
46 | 							className={cn(
47 | 								"inline-flex items-center rounded-md px-3 py-1 text-sm font-medium transition-colors",
48 | 								activeTag === tag
49 | 									? "bg-primary text-primary-foreground"
50 | 									: "bg-secondary/10 text-secondary-foreground hover:bg-secondary/20",
51 | 							)}
52 | 						>
53 | 							{tag}
54 | 						</button>
55 | 					))}
56 | 				</div>
57 | 			</div>
58 | 			<div className="grid gap-4 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 mt-2">
59 | 				{filterResources(activeTag).map((resource) => (
60 | 					<ResourceCard key={resource.href} {...resource} />
61 | 				))}
62 | 			</div>
63 | 		</div>
64 | 	);
65 | }
66 | 
```

--------------------------------------------------------------------------------
/docs/components/docs/layout/nav.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | "use client";
 2 | import Link, { type LinkProps } from "fumadocs-core/link";
 3 | import {
 4 | 	createContext,
 5 | 	type ReactNode,
 6 | 	useContext,
 7 | 	useEffect,
 8 | 	useMemo,
 9 | 	useState,
10 | } from "react";
11 | import { cn } from "../../../lib/utils";
12 | import { useI18n } from "fumadocs-ui/provider";
13 | 
14 | export interface NavProviderProps {
15 | 	/**
16 | 	 * Use transparent background
17 | 	 *
18 | 	 * @defaultValue none
19 | 	 */
20 | 	transparentMode?: "always" | "top" | "none";
21 | }
22 | 
23 | export interface TitleProps {
24 | 	title?: ReactNode;
25 | 
26 | 	/**
27 | 	 * Redirect url of title
28 | 	 * @defaultValue '/'
29 | 	 */
30 | 	url?: string;
31 | }
32 | 
33 | interface NavContextType {
34 | 	isTransparent: boolean;
35 | }
36 | 
37 | const NavContext = createContext<NavContextType>({
38 | 	isTransparent: false,
39 | });
40 | 
41 | export function NavProvider({
42 | 	transparentMode = "none",
43 | 	children,
44 | }: NavProviderProps & { children: ReactNode }) {
45 | 	const [transparent, setTransparent] = useState(transparentMode !== "none");
46 | 
47 | 	useEffect(() => {
48 | 		if (transparentMode !== "top") return;
49 | 
50 | 		const listener = () => {
51 | 			if (document.documentElement.hasAttribute("data-anchor-scrolling")) {
52 | 				return;
53 | 			}
54 | 			setTransparent(window.scrollY < 10);
55 | 		};
56 | 
57 | 		listener();
58 | 		window.addEventListener("scroll", listener, { passive: true });
59 | 		return () => {
60 | 			window.removeEventListener("scroll", listener);
61 | 		};
62 | 	}, [transparentMode]);
63 | 
64 | 	return (
65 | 		<NavContext.Provider
66 | 			value={useMemo(() => ({ isTransparent: transparent }), [transparent])}
67 | 		>
68 | 			{children}
69 | 		</NavContext.Provider>
70 | 	);
71 | }
72 | 
73 | export function useNav(): NavContextType {
74 | 	return useContext(NavContext);
75 | }
76 | 
77 | export function Title({
78 | 	title,
79 | 	url,
80 | 	...props
81 | }: TitleProps & Omit<LinkProps, "title">) {
82 | 	const { locale } = useI18n();
83 | 
84 | 	return (
85 | 		<Link
86 | 			href={url ?? (locale ? `/${locale}` : "/")}
87 | 			{...props}
88 | 			className={cn(
89 | 				"inline-flex items-center gap-2.5 font-semibold",
90 | 				props.className,
91 | 			)}
92 | 		>
93 | 			{title}
94 | 		</Link>
95 | 	);
96 | }
97 | 
```

--------------------------------------------------------------------------------
/packages/better-auth/src/db/get-tables.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { describe, expect, it } from "vitest";
 2 | import { getAuthTables } from "./get-tables";
 3 | 
 4 | describe("getAuthTables", () => {
 5 | 	it("should use correct field name for refreshTokenExpiresAt", () => {
 6 | 		const tables = getAuthTables({
 7 | 			account: {
 8 | 				fields: {
 9 | 					refreshTokenExpiresAt: "custom_refresh_token_expires_at",
10 | 				},
11 | 			},
12 | 		});
13 | 
14 | 		const accountTable = tables.account;
15 | 		const refreshTokenExpiresAtField =
16 | 			accountTable!.fields.refreshTokenExpiresAt!;
17 | 
18 | 		expect(refreshTokenExpiresAtField.fieldName).toBe(
19 | 			"custom_refresh_token_expires_at",
20 | 		);
21 | 	});
22 | 
23 | 	it("should not use accessTokenExpiresAt field name for refreshTokenExpiresAt", () => {
24 | 		const tables = getAuthTables({
25 | 			account: {
26 | 				fields: {
27 | 					accessTokenExpiresAt: "custom_access_token_expires_at",
28 | 					refreshTokenExpiresAt: "custom_refresh_token_expires_at",
29 | 				},
30 | 			},
31 | 		});
32 | 
33 | 		const accountTable = tables.account;
34 | 		const refreshTokenExpiresAtField =
35 | 			accountTable!.fields.refreshTokenExpiresAt!;
36 | 		const accessTokenExpiresAtField =
37 | 			accountTable!.fields.accessTokenExpiresAt!;
38 | 
39 | 		expect(refreshTokenExpiresAtField.fieldName).toBe(
40 | 			"custom_refresh_token_expires_at",
41 | 		);
42 | 		expect(accessTokenExpiresAtField.fieldName).toBe(
43 | 			"custom_access_token_expires_at",
44 | 		);
45 | 		expect(refreshTokenExpiresAtField.fieldName).not.toBe(
46 | 			accessTokenExpiresAtField.fieldName,
47 | 		);
48 | 	});
49 | 
50 | 	it("should use default field names when no custom names provided", () => {
51 | 		const tables = getAuthTables({});
52 | 
53 | 		const accountTable = tables.account;
54 | 		const refreshTokenExpiresAtField =
55 | 			accountTable!.fields.refreshTokenExpiresAt!;
56 | 		const accessTokenExpiresAtField =
57 | 			accountTable!.fields.accessTokenExpiresAt!;
58 | 
59 | 		expect(refreshTokenExpiresAtField.fieldName).toBe("refreshTokenExpiresAt");
60 | 		expect(accessTokenExpiresAtField.fieldName).toBe("accessTokenExpiresAt");
61 | 	});
62 | });
63 | 
```

--------------------------------------------------------------------------------
/docs/content/docs/authentication/kick.mdx:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: Kick
 3 | description: Kick provider setup and usage.
 4 | ---
 5 | 
 6 | <Steps>
 7 |     <Step> 
 8 |         ### Get your Kick Credentials
 9 |         To use Kick sign in, you need a client ID and client secret. You can get them from the [Kick Developer Portal](https://kick.com/settings/developer).
10 | 
11 |         Make sure to set the redirect URL to `http://localhost:3000/api/auth/callback/kick` for local development. For production, you should set it to the URL of your application. If you change the base path of the auth routes, you should update the redirect URL accordingly.
12 |     </Step>
13 | 
14 |   <Step>
15 |         ### Configure the provider
16 |         To configure the provider, you need to import the provider and pass it to the `socialProviders` option of the auth instance.
17 | 
18 |         ```ts title="auth.ts"  
19 |         import { betterAuth } from "better-auth"
20 |         
21 |         export const auth = betterAuth({
22 |             socialProviders: {
23 |                 kick: { // [!code highlight]
24 |                     clientId: process.env.KICK_CLIENT_ID as string, // [!code highlight]
25 |                     clientSecret: process.env.KICK_CLIENT_SECRET as string, // [!code highlight]
26 |                 }, // [!code highlight]
27 |             }
28 |         })
29 |         ```
30 |     </Step>
31 |        <Step>
32 |         ### Sign In with Kick
33 |         To sign in with Kick, you can use the `signIn.social` function provided by the client. The `signIn` function takes an object with the following properties:
34 |         - `provider`: The provider to use. It should be set to `kick`.
35 | 
36 |         ```ts title="auth-client.ts"  
37 |         import { createAuthClient } from "better-auth/client"
38 |         const authClient =  createAuthClient()
39 |         
40 |         const signIn = async () => {
41 |             const data = await authClient.signIn.social({
42 |                 provider: "kick"
43 |             })
44 |         }
45 |         ```
46 |     </Step>
47 | </Steps>
48 | 
```

--------------------------------------------------------------------------------
/docs/components/ui/slider.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | "use client";
 2 | 
 3 | import * as React from "react";
 4 | import * as SliderPrimitive from "@radix-ui/react-slider";
 5 | 
 6 | import { cn } from "@/lib/utils";
 7 | 
 8 | function Slider({
 9 | 	className,
10 | 	defaultValue,
11 | 	value,
12 | 	min = 0,
13 | 	max = 100,
14 | 	...props
15 | }: React.ComponentProps<typeof SliderPrimitive.Root>) {
16 | 	const _values = React.useMemo(
17 | 		() =>
18 | 			Array.isArray(value)
19 | 				? value
20 | 				: Array.isArray(defaultValue)
21 | 					? defaultValue
22 | 					: [min, max],
23 | 		[value, defaultValue, min, max],
24 | 	);
25 | 
26 | 	return (
27 | 		<SliderPrimitive.Root
28 | 			data-slot="slider"
29 | 			defaultValue={defaultValue}
30 | 			value={value}
31 | 			min={min}
32 | 			max={max}
33 | 			className={cn(
34 | 				"relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
35 | 				className,
36 | 			)}
37 | 			{...props}
38 | 		>
39 | 			<SliderPrimitive.Track
40 | 				data-slot="slider-track"
41 | 				className={cn(
42 | 					"bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5",
43 | 				)}
44 | 			>
45 | 				<SliderPrimitive.Range
46 | 					data-slot="slider-range"
47 | 					className={cn(
48 | 						"bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full",
49 | 					)}
50 | 				/>
51 | 			</SliderPrimitive.Track>
52 | 			{Array.from({ length: _values.length }, (_, index) => (
53 | 				<SliderPrimitive.Thumb
54 | 					data-slot="slider-thumb"
55 | 					key={index}
56 | 					className="border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
57 | 				/>
58 | 			))}
59 | 		</SliderPrimitive.Root>
60 | 	);
61 | }
62 | 
63 | export { Slider };
64 | 
```

--------------------------------------------------------------------------------
/docs/components/docs/ui/scroll-area.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
 2 | import * as React from "react";
 3 | import { cn } from "../../../lib/utils";
 4 | 
 5 | const ScrollArea = React.forwardRef<
 6 | 	React.ComponentRef<typeof ScrollAreaPrimitive.Root>,
 7 | 	React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
 8 | >(({ className, children, ...props }, ref) => (
 9 | 	<ScrollAreaPrimitive.Root
10 | 		ref={ref}
11 | 		className={cn("overflow-hidden", className)}
12 | 		{...props}
13 | 	>
14 | 		{children}
15 | 		<ScrollAreaPrimitive.Corner />
16 | 		<ScrollBar orientation="vertical" />
17 | 	</ScrollAreaPrimitive.Root>
18 | ));
19 | 
20 | ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
21 | 
22 | const ScrollViewport = React.forwardRef<
23 | 	React.ComponentRef<typeof ScrollAreaPrimitive.Viewport>,
24 | 	React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Viewport>
25 | >(({ className, children, ...props }, ref) => (
26 | 	<ScrollAreaPrimitive.Viewport
27 | 		ref={ref}
28 | 		className={cn("size-full rounded-[inherit]", className)}
29 | 		{...props}
30 | 	>
31 | 		{children}
32 | 	</ScrollAreaPrimitive.Viewport>
33 | ));
34 | 
35 | ScrollViewport.displayName = ScrollAreaPrimitive.Viewport.displayName;
36 | 
37 | const ScrollBar = React.forwardRef<
38 | 	React.ComponentRef<typeof ScrollAreaPrimitive.Scrollbar>,
39 | 	React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Scrollbar>
40 | >(({ className, orientation = "vertical", ...props }, ref) => (
41 | 	<ScrollAreaPrimitive.Scrollbar
42 | 		ref={ref}
43 | 		orientation={orientation}
44 | 		className={cn(
45 | 			"flex select-none data-[state=hidden]:animate-fd-fade-out",
46 | 			orientation === "vertical" && "h-full w-1.5",
47 | 			orientation === "horizontal" && "h-1.5 flex-col",
48 | 			className,
49 | 		)}
50 | 		{...props}
51 | 	>
52 | 		<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-fd-border" />
53 | 	</ScrollAreaPrimitive.Scrollbar>
54 | ));
55 | ScrollBar.displayName = ScrollAreaPrimitive.Scrollbar.displayName;
56 | 
57 | export { ScrollArea, ScrollBar, ScrollViewport };
58 | 
```

--------------------------------------------------------------------------------
/e2e/smoke/test/ssr.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { betterAuth } from "better-auth";
 2 | import { describe, test } from "node:test";
 3 | import { DatabaseSync } from "node:sqlite";
 4 | import { apiKey } from "better-auth/plugins";
 5 | import { createAuthClient } from "better-auth/client";
 6 | import { apiKeyClient } from "better-auth/client/plugins";
 7 | import { getMigrations } from "better-auth/db";
 8 | import assert from "node:assert/strict";
 9 | 
10 | describe("server side client", () => {
11 | 	test("can use api key on server side", async () => {
12 | 		const database = new DatabaseSync(":memory:");
13 | 		const auth = betterAuth({
14 | 			baseURL: "http://localhost:3000",
15 | 			database,
16 | 			socialProviders: {
17 | 				github: {
18 | 					clientId: process.env.GITHUB_CLIENT_ID as string,
19 | 					clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
20 | 				},
21 | 			},
22 | 			emailAndPassword: {
23 | 				enabled: true,
24 | 			},
25 | 			plugins: [
26 | 				apiKey({
27 | 					rateLimit: {
28 | 						enabled: false,
29 | 					},
30 | 				}),
31 | 			],
32 | 		});
33 | 
34 | 		const { runMigrations } = await getMigrations(auth.options);
35 | 		await runMigrations();
36 | 
37 | 		const authClient: ReturnType<typeof createAuthClient> = createAuthClient({
38 | 			baseURL: "http://localhost:3000",
39 | 			plugins: [apiKeyClient()],
40 | 			fetchOptions: {
41 | 				customFetchImpl: async (url, init) => {
42 | 					return auth.handler(new Request(url, init));
43 | 				},
44 | 			},
45 | 		});
46 | 
47 | 		const { user } = await auth.api.signUpEmail({
48 | 			body: {
49 | 				name: "Alex",
50 | 				email: "[email protected]",
51 | 				password: "hello123",
52 | 			},
53 | 		});
54 | 
55 | 		const { key, id, userId } = await auth.api.createApiKey({
56 | 			body: {
57 | 				name: "my-api-key",
58 | 				userId: user.id,
59 | 			},
60 | 		});
61 | 
62 | 		const ret = database.prepare(`SELECT * FROM apiKey;`).all();
63 | 		assert.equal(ret.length, 1);
64 | 		const first = ret.at(-1)!;
65 | 		assert.equal(first.id, id);
66 | 		assert.equal(first.userId, userId);
67 | 
68 | 		await authClient.getSession({
69 | 			fetchOptions: {
70 | 				headers: {
71 | 					"x-api-key": key,
72 | 				},
73 | 			},
74 | 		});
75 | 	});
76 | });
77 | 
```

--------------------------------------------------------------------------------
/docs/components/builder/tabs.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | "use client";
 2 | 
 3 | import { useState } from "react";
 4 | import { cn } from "@/lib/utils";
 5 | 
 6 | type Tab = {
 7 | 	title: string;
 8 | 	value: string;
 9 | 	content?: string | React.ReactNode | any;
10 | };
11 | 
12 | export const AuthTabs = ({ tabs: propTabs }: { tabs: Tab[] }) => {
13 | 	const [active, setActive] = useState<Tab>(propTabs[0]);
14 | 	const [tabs, setTabs] = useState<Tab[]>(propTabs);
15 | 	const isActive = (tab: Tab) => {
16 | 		return tab.value === tabs[0].value;
17 | 	};
18 | 	const moveSelectedTabToTop = (idx: number) => {
19 | 		const newTabs = [...propTabs];
20 | 		const selectedTab = newTabs.splice(idx, 1);
21 | 		newTabs.unshift(selectedTab[0]);
22 | 		setTabs(newTabs);
23 | 		setActive(newTabs[0]);
24 | 	};
25 | 
26 | 	return (
27 | 		<>
28 | 			<div
29 | 				className={cn(
30 | 					"flex flex-row items-center justify-start mt-0 relative no-visible-scrollbar border-x w-full border-t max-w-max bg-opacity-0",
31 | 				)}
32 | 			>
33 | 				{propTabs.map((tab, idx) => (
34 | 					<button
35 | 						key={tab.title}
36 | 						onClick={() => {
37 | 							moveSelectedTabToTop(idx);
38 | 						}}
39 | 						className={cn(
40 | 							"relative px-4 py-2 rounded-full opacity-80 hover:opacity-100",
41 | 						)}
42 | 					>
43 | 						{active.value === tab.value && (
44 | 							<div
45 | 								className={cn(
46 | 									"absolute inset-0 bg-gray-200 dark:bg-zinc-900/90 opacity-100",
47 | 								)}
48 | 							/>
49 | 						)}
50 | 
51 | 						<span
52 | 							className={cn(
53 | 								"relative block text-black dark:text-white",
54 | 								active.value === tab.value
55 | 									? "text-opacity-100 font-medium"
56 | 									: "opacity-40 ",
57 | 							)}
58 | 						>
59 | 							{tab.title}
60 | 						</span>
61 | 					</button>
62 | 				))}
63 | 			</div>
64 | 			<div className="relative w-full h-full">
65 | 				{tabs.map((tab, idx) => (
66 | 					<div
67 | 						key={tab.value}
68 | 						style={{
69 | 							scale: 1 - idx * 0.1,
70 | 							zIndex: -idx,
71 | 							opacity: idx < 3 ? 1 - idx * 0.1 : 0,
72 | 						}}
73 | 						className={cn("h-full", isActive(tab) ? "" : "hidden")}
74 | 					>
75 | 						{tab.content}
76 | 					</div>
77 | 				))}
78 | 			</div>
79 | 		</>
80 | 	);
81 | };
82 | 
```

--------------------------------------------------------------------------------
/docs/content/docs/authentication/twitch.mdx:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: Twitch
 3 | description: Twitch provider setup and usage.
 4 | ---
 5 | 
 6 | <Steps>
 7 |     <Step> 
 8 |         ### Get your Twitch Credentials
 9 |         To use Twitch sign in, you need a client ID and client secret. You can get them from the [Twitch Developer Portal](https://dev.twitch.tv/console/apps).
10 | 
11 |         Make sure to set the redirect URL to `http://localhost:3000/api/auth/callback/twitch` for local development. For production, you should set it to the URL of your application. If you change the base path of the auth routes, you should update the redirect URL accordingly.
12 |     </Step>
13 | 
14 |   <Step>
15 |         ### Configure the provider
16 |         To configure the provider, you need to import the provider and pass it to the `socialProviders` option of the auth instance.
17 | 
18 |         ```ts title="auth.ts"  
19 |         import { betterAuth } from "better-auth"
20 |         
21 |         export const auth = betterAuth({
22 |             socialProviders: {
23 |                 twitch: { // [!code highlight]
24 |                     clientId: process.env.TWITCH_CLIENT_ID as string, // [!code highlight]
25 |                     clientSecret: process.env.TWITCH_CLIENT_SECRET as string, // [!code highlight]
26 |                 }, // [!code highlight]
27 |             }
28 |         })
29 |         ```
30 |     </Step>
31 |        <Step>
32 |         ### Sign In with Twitch 
33 |         To sign in with Twitch, you can use the `signIn.social` function provided by the client. The `signIn` function takes an object with the following properties:
34 |         - `provider`: The provider to use. It should be set to `twitch`.
35 | 
36 |         ```ts title="auth-client.ts"  
37 |         import { createAuthClient } from "better-auth/client"
38 |         const authClient =  createAuthClient()
39 |         
40 |         const signIn = async () => {
41 |             const data = await authClient.signIn.social({
42 |                 provider: "twitch"
43 |             })
44 |         }
45 |         ```
46 |     </Step>
47 | </Steps>
48 | 
```

--------------------------------------------------------------------------------
/demo/expo-example/src/app/dashboard.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
 2 | import { Button } from "@/components/ui/button";
 3 | import { Card, CardFooter, CardHeader } from "@/components/ui/card";
 4 | import { Text } from "@/components/ui/text";
 5 | import { authClient } from "@/lib/auth-client";
 6 | import { View } from "react-native";
 7 | import Ionicons from "@expo/vector-icons/AntDesign";
 8 | import { router } from "expo-router";
 9 | import { useEffect } from "react";
10 | import { useStore } from "@nanostores/react";
11 | 
12 | export default function Dashboard() {
13 | 	const { data: session, isPending } = useStore(authClient.useSession);
14 | 	useEffect(() => {
15 | 		if (!session && !isPending) {
16 | 			router.push("/");
17 | 		}
18 | 	}, [session, isPending]);
19 | 	return (
20 | 		<Card className="w-10/12">
21 | 			<CardHeader>
22 | 				<View className="flex-row items-center gap-2">
23 | 					<Avatar alt="user-image">
24 | 						<AvatarImage
25 | 							source={{
26 | 								uri: session?.user?.image || "",
27 | 							}}
28 | 						/>
29 | 						<AvatarFallback>
30 | 							<Text>{session?.user?.name[0]}</Text>
31 | 						</AvatarFallback>
32 | 					</Avatar>
33 | 					<View>
34 | 						<Text className="font-bold">{session?.user?.name}</Text>
35 | 						<Text className="text-sm">{session?.user?.email}</Text>
36 | 					</View>
37 | 				</View>
38 | 			</CardHeader>
39 | 			<CardFooter className="justify-between">
40 | 				<Button
41 | 					variant="default"
42 | 					size="sm"
43 | 					className="flex-row items-center gap-2	"
44 | 				>
45 | 					<Ionicons name="edit" size={16} color="white" />
46 | 					<Text>Edit User</Text>
47 | 				</Button>
48 | 				<Button
49 | 					variant="secondary"
50 | 					className="flex-row items-center gap-2"
51 | 					size="sm"
52 | 					onPress={async () => {
53 | 						await authClient.signOut({
54 | 							fetchOptions: {
55 | 								onSuccess: () => {
56 | 									router.push("/");
57 | 								},
58 | 							},
59 | 						});
60 | 					}}
61 | 				>
62 | 					<Ionicons name="logout" size={14} color="black" />
63 | 					<Text>Sign Out</Text>
64 | 				</Button>
65 | 			</CardFooter>
66 | 		</Card>
67 | 	);
68 | }
69 | 
```

--------------------------------------------------------------------------------
/demo/nextjs/components/ui/tabs.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | "use client";
 2 | 
 3 | import * as React from "react";
 4 | import * as TabsPrimitive from "@radix-ui/react-tabs";
 5 | 
 6 | import { cn } from "@/lib/utils";
 7 | 
 8 | const Tabs = TabsPrimitive.Root;
 9 | 
10 | const TabsList = ({
11 | 	ref,
12 | 	className,
13 | 	...props
14 | }: React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {
15 | 	ref: React.RefObject<React.ElementRef<typeof TabsPrimitive.List>>;
16 | }) => (
17 | 	<TabsPrimitive.List
18 | 		ref={ref}
19 | 		className={cn(
20 | 			"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
21 | 			className,
22 | 		)}
23 | 		{...props}
24 | 	/>
25 | );
26 | TabsList.displayName = TabsPrimitive.List.displayName;
27 | 
28 | const TabsTrigger = ({
29 | 	ref,
30 | 	className,
31 | 	...props
32 | }: React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> & {
33 | 	ref: React.RefObject<React.ElementRef<typeof TabsPrimitive.Trigger>>;
34 | }) => (
35 | 	<TabsPrimitive.Trigger
36 | 		ref={ref}
37 | 		className={cn(
38 | 			"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
39 | 			className,
40 | 		)}
41 | 		{...props}
42 | 	/>
43 | );
44 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
45 | 
46 | const TabsContent = ({
47 | 	ref,
48 | 	className,
49 | 	...props
50 | }: React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content> & {
51 | 	ref: React.RefObject<React.ElementRef<typeof TabsPrimitive.Content>>;
52 | }) => (
53 | 	<TabsPrimitive.Content
54 | 		ref={ref}
55 | 		className={cn(
56 | 			"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
57 | 			className,
58 | 		)}
59 | 		{...props}
60 | 	/>
61 | );
62 | TabsContent.displayName = TabsPrimitive.Content.displayName;
63 | 
64 | export { Tabs, TabsList, TabsTrigger, TabsContent };
65 | 
```
Page 5/68FirstPrevNextLast