#
tokens: 49342/50000 22/1099 files (page 13/69)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 13 of 69. 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-decelration
│       │   │   │   ├── package.json
│       │   │   │   ├── src
│       │   │   │   │   ├── demo.ts
│       │   │   │   │   └── index.ts
│       │   │   │   └── tsconfig.json
│       │   │   ├── tsconfig-exact-optional-property-types
│       │   │   │   ├── package.json
│       │   │   │   ├── src
│       │   │   │   │   ├── index.ts
│       │   │   │   │   ├── organization.ts
│       │   │   │   │   ├── user-additional-fields.ts
│       │   │   │   │   └── username.ts
│       │   │   │   └── tsconfig.json
│       │   │   ├── tsconfig-isolated-module-bundler
│       │   │   │   ├── package.json
│       │   │   │   ├── src
│       │   │   │   │   └── index.ts
│       │   │   │   └── tsconfig.json
│       │   │   ├── tsconfig-verbatim-module-syntax-node10
│       │   │   │   ├── package.json
│       │   │   │   ├── src
│       │   │   │   │   └── index.ts
│       │   │   │   └── tsconfig.json
│       │   │   └── vite
│       │   │       ├── package.json
│       │   │       ├── src
│       │   │       │   ├── client.ts
│       │   │       │   └── server.ts
│       │   │       ├── tsconfig.json
│       │   │       └── vite.config.ts
│       │   ├── ssr.ts
│       │   ├── typecheck.spec.ts
│       │   └── vite.spec.ts
│       └── tsconfig.json
├── LICENSE.md
├── package.json
├── packages
│   ├── better-auth
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── __snapshots__
│   │   │   │   └── init.test.ts.snap
│   │   │   ├── adapters
│   │   │   │   ├── adapter-factory
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── test
│   │   │   │   │   │   ├── __snapshots__
│   │   │   │   │   │   │   └── adapter-factory.test.ts.snap
│   │   │   │   │   │   └── adapter-factory.test.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── create-test-suite.ts
│   │   │   │   ├── drizzle-adapter
│   │   │   │   │   ├── drizzle-adapter.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── test
│   │   │   │   │       ├── .gitignore
│   │   │   │   │       ├── adapter.drizzle.mysql.test.ts
│   │   │   │   │       ├── adapter.drizzle.pg.test.ts
│   │   │   │   │       ├── adapter.drizzle.sqlite.test.ts
│   │   │   │   │       └── generate-schema.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── kysely-adapter
│   │   │   │   │   ├── bun-sqlite-dialect.ts
│   │   │   │   │   ├── dialect.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── kysely-adapter.ts
│   │   │   │   │   ├── node-sqlite-dialect.ts
│   │   │   │   │   ├── test
│   │   │   │   │   │   ├── adapter.kysely.mssql.test.ts
│   │   │   │   │   │   ├── adapter.kysely.mysql.test.ts
│   │   │   │   │   │   ├── adapter.kysely.pg.test.ts
│   │   │   │   │   │   ├── adapter.kysely.sqlite.test.ts
│   │   │   │   │   │   └── node-sqlite-dialect.test.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── memory-adapter
│   │   │   │   │   ├── adapter.memory.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── memory-adapter.ts
│   │   │   │   ├── mongodb-adapter
│   │   │   │   │   ├── adapter.mongo-db.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── mongodb-adapter.ts
│   │   │   │   ├── prisma-adapter
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── prisma-adapter.ts
│   │   │   │   │   └── test
│   │   │   │   │       ├── .gitignore
│   │   │   │   │       ├── base.prisma
│   │   │   │   │       ├── generate-auth-config.ts
│   │   │   │   │       ├── generate-prisma-schema.ts
│   │   │   │   │       ├── get-prisma-client.ts
│   │   │   │   │       ├── prisma.mysql.test.ts
│   │   │   │   │       ├── prisma.pg.test.ts
│   │   │   │   │       ├── prisma.sqlite.test.ts
│   │   │   │   │       └── push-prisma-schema.ts
│   │   │   │   ├── test-adapter.ts
│   │   │   │   ├── test.ts
│   │   │   │   ├── tests
│   │   │   │   │   ├── auth-flow.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── normal.ts
│   │   │   │   │   ├── number-id.ts
│   │   │   │   │   ├── performance.ts
│   │   │   │   │   └── transactions.ts
│   │   │   │   └── utils.ts
│   │   │   ├── api
│   │   │   │   ├── check-endpoint-conflicts.test.ts
│   │   │   │   ├── index.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── middlewares
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── origin-check.test.ts
│   │   │   │   │   └── origin-check.ts
│   │   │   │   ├── rate-limiter
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── rate-limiter.test.ts
│   │   │   │   ├── routes
│   │   │   │   │   ├── account.test.ts
│   │   │   │   │   ├── account.ts
│   │   │   │   │   ├── callback.ts
│   │   │   │   │   ├── email-verification.test.ts
│   │   │   │   │   ├── email-verification.ts
│   │   │   │   │   ├── error.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── ok.ts
│   │   │   │   │   ├── reset-password.test.ts
│   │   │   │   │   ├── reset-password.ts
│   │   │   │   │   ├── session-api.test.ts
│   │   │   │   │   ├── session.ts
│   │   │   │   │   ├── sign-in.test.ts
│   │   │   │   │   ├── sign-in.ts
│   │   │   │   │   ├── sign-out.test.ts
│   │   │   │   │   ├── sign-out.ts
│   │   │   │   │   ├── sign-up.test.ts
│   │   │   │   │   ├── sign-up.ts
│   │   │   │   │   ├── update-user.test.ts
│   │   │   │   │   └── update-user.ts
│   │   │   │   ├── to-auth-endpoints.test.ts
│   │   │   │   └── to-auth-endpoints.ts
│   │   │   ├── auth.test.ts
│   │   │   ├── auth.ts
│   │   │   ├── call.test.ts
│   │   │   ├── client
│   │   │   │   ├── client-ssr.test.ts
│   │   │   │   ├── client.test.ts
│   │   │   │   ├── config.ts
│   │   │   │   ├── fetch-plugins.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── lynx
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── lynx-store.ts
│   │   │   │   ├── parser.ts
│   │   │   │   ├── path-to-object.ts
│   │   │   │   ├── plugins
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── infer-plugin.ts
│   │   │   │   ├── proxy.ts
│   │   │   │   ├── query.ts
│   │   │   │   ├── react
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── react-store.ts
│   │   │   │   ├── session-atom.ts
│   │   │   │   ├── solid
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── solid-store.ts
│   │   │   │   ├── svelte
│   │   │   │   │   └── index.ts
│   │   │   │   ├── test-plugin.ts
│   │   │   │   ├── types.ts
│   │   │   │   ├── url.test.ts
│   │   │   │   ├── vanilla.ts
│   │   │   │   └── vue
│   │   │   │       ├── index.ts
│   │   │   │       └── vue-store.ts
│   │   │   ├── 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
│   │   │   │   ├── 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
│   │   └── vitest.setup.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/cli/test/__snapshots__/auth-schema-sqlite-passkey-number-id.txt:
--------------------------------------------------------------------------------

```
 1 | import { sql } from "drizzle-orm";
 2 | import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
 3 | 
 4 | export const custom_user = sqliteTable("custom_user", {
 5 |   id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }),
 6 |   name: text("name").notNull(),
 7 |   email: text("email").notNull().unique(),
 8 |   emailVerified: integer("email_verified", { mode: "boolean" })
 9 |     .default(false)
10 |     .notNull(),
11 |   image: text("image"),
12 |   createdAt: integer("created_at", { mode: "timestamp_ms" })
13 |     .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
14 |     .notNull(),
15 |   updatedAt: integer("updated_at", { mode: "timestamp_ms" })
16 |     .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
17 |     .$onUpdate(() => /* @__PURE__ */ new Date())
18 |     .notNull(),
19 | });
20 | 
21 | export const custom_session = sqliteTable("custom_session", {
22 |   id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }),
23 |   expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(),
24 |   token: text("token").notNull().unique(),
25 |   createdAt: integer("created_at", { mode: "timestamp_ms" })
26 |     .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
27 |     .notNull(),
28 |   updatedAt: integer("updated_at", { mode: "timestamp_ms" })
29 |     .$onUpdate(() => /* @__PURE__ */ new Date())
30 |     .notNull(),
31 |   ipAddress: text("ip_address"),
32 |   userAgent: text("user_agent"),
33 |   userId: integer("user_id")
34 |     .notNull()
35 |     .references(() => custom_user.id, { onDelete: "cascade" }),
36 | });
37 | 
38 | export const custom_account = sqliteTable("custom_account", {
39 |   id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }),
40 |   accountId: text("account_id").notNull(),
41 |   providerId: text("provider_id").notNull(),
42 |   userId: integer("user_id")
43 |     .notNull()
44 |     .references(() => custom_user.id, { onDelete: "cascade" }),
45 |   accessToken: text("access_token"),
46 |   refreshToken: text("refresh_token"),
47 |   idToken: text("id_token"),
48 |   accessTokenExpiresAt: integer("access_token_expires_at", {
49 |     mode: "timestamp_ms",
50 |   }),
51 |   refreshTokenExpiresAt: integer("refresh_token_expires_at", {
52 |     mode: "timestamp_ms",
53 |   }),
54 |   scope: text("scope"),
55 |   password: text("password"),
56 |   createdAt: integer("created_at", { mode: "timestamp_ms" })
57 |     .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
58 |     .notNull(),
59 |   updatedAt: integer("updated_at", { mode: "timestamp_ms" })
60 |     .$onUpdate(() => /* @__PURE__ */ new Date())
61 |     .notNull(),
62 | });
63 | 
64 | export const custom_verification = sqliteTable("custom_verification", {
65 |   id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }),
66 |   identifier: text("identifier").notNull(),
67 |   value: text("value").notNull(),
68 |   expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(),
69 |   createdAt: integer("created_at", { mode: "timestamp_ms" })
70 |     .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
71 |     .notNull(),
72 |   updatedAt: integer("updated_at", { mode: "timestamp_ms" })
73 |     .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
74 |     .$onUpdate(() => /* @__PURE__ */ new Date())
75 |     .notNull(),
76 | });
77 | 
78 | export const passkey = sqliteTable("passkey", {
79 |   id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }),
80 |   name: text("name"),
81 |   publicKey: text("public_key").notNull(),
82 |   userId: integer("user_id")
83 |     .notNull()
84 |     .references(() => custom_user.id, { onDelete: "cascade" }),
85 |   credentialID: text("credential_id").notNull(),
86 |   counter: integer("counter").notNull(),
87 |   deviceType: text("device_type").notNull(),
88 |   backedUp: integer("backed_up", { mode: "boolean" }).notNull(),
89 |   transports: text("transports"),
90 |   createdAt: integer("created_at", { mode: "timestamp_ms" }),
91 |   aaguid: text("aaguid"),
92 | });
93 | 
```

--------------------------------------------------------------------------------
/docs/content/docs/integrations/svelte-kit.mdx:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: SvelteKit Integration
  3 | description: Integrate Better Auth with SvelteKit.
  4 | ---
  5 | 
  6 | Before you start, make sure you have a Better Auth instance configured. If you haven't done that yet, check out the [installation](/docs/installation).
  7 | 
  8 | ### Mount the handler
  9 | 
 10 | We need to mount the handler to SvelteKit server hook.
 11 | 
 12 | ```ts title="hooks.server.ts"
 13 | import { auth } from "$lib/auth";
 14 | import { svelteKitHandler } from "better-auth/svelte-kit";
 15 | import { building } from "$app/environment";
 16 | 
 17 | export async function handle({ event, resolve }) {
 18 |   return svelteKitHandler({ event, resolve, auth, building });
 19 | }
 20 | ```
 21 | 
 22 | ### Populate session data in the event (`event.locals`)
 23 | 
 24 | The `svelteKitHandler` does not automatically populate `event.locals.user` or `event.locals.session`. If you want to access the current session in your server code (e.g., in `+layout.server.ts`, actions, or endpoints), populate `event.locals` in your `handle` hook:
 25 | 
 26 | ```ts title="hooks.server.ts"
 27 | import { auth } from "$lib/auth";
 28 | import { svelteKitHandler } from "better-auth/svelte-kit";
 29 | import { building } from "$app/environment";
 30 | 
 31 | export async function handle({ event, resolve }) {
 32 |   // Fetch current session from Better Auth
 33 |   const session = await auth.api.getSession({
 34 |     headers: event.request.headers,
 35 |   });
 36 | 
 37 |   // Make session and user available on server
 38 |   if (session) {
 39 |     event.locals.session = session.session;
 40 |     event.locals.user = session.user;
 41 |   }
 42 | 
 43 |   return svelteKitHandler({ event, resolve, auth, building });
 44 | }
 45 | ```
 46 | 
 47 | ### Server Action Cookies
 48 | 
 49 | To ensure cookies are properly set when you call functions like `signInEmail` or `signUpEmail` in a server action, you should use the `sveltekitCookies` plugin. This plugin will automatically handle setting cookies for you in SvelteKit.
 50 | 
 51 | You need to add it as a plugin to your Better Auth instance.
 52 | 
 53 | <Callout>
 54 |   The `getRequestEvent` function is available in SvelteKit `2.20.0` and later.
 55 |   Make sure you are using a compatible version.
 56 | </Callout>
 57 | 
 58 | ```ts title="lib/auth.ts"
 59 | import { betterAuth } from "better-auth";
 60 | import { sveltekitCookies } from "better-auth/svelte-kit";
 61 | import { getRequestEvent } from "$app/server";
 62 | 
 63 | export const auth = betterAuth({
 64 |   // ... your config
 65 |   plugins: [sveltekitCookies(getRequestEvent)], // make sure this is the last plugin in the array
 66 | });
 67 | ```
 68 | 
 69 | ## Create a client
 70 | 
 71 | Create a client instance. You can name the file anything you want. Here we are creating `client.ts` file inside the `lib/` directory.
 72 | 
 73 | ```ts title="auth-client.ts"
 74 | import { createAuthClient } from "better-auth/svelte"; // make sure to import from better-auth/svelte
 75 | 
 76 | export const authClient = createAuthClient({
 77 |   // you can pass client configuration here
 78 | });
 79 | ```
 80 | 
 81 | Once you have created the client, you can use it to sign up, sign in, and perform other actions.
 82 | Some of the actions are reactive. The client use [nano-store](https://github.com/nanostores/nanostores) to store the state and reflect changes when there is a change like a user signing in or out affecting the session state.
 83 | 
 84 | ### Example usage
 85 | 
 86 | ```svelte
 87 | <script lang="ts">
 88 |   import { authClient } from "$lib/client";
 89 |   const session = authClient.useSession();
 90 | </script>
 91 |     <div>
 92 |       {#if $session.data}
 93 |         <div>
 94 |           <p>
 95 |             {$session.data.user.name}
 96 |           </p>
 97 |           <button
 98 |             on:click={async () => {
 99 |               await authClient.signOut();
100 |             }}
101 |           >
102 |             Sign Out
103 |           </button>
104 |         </div>
105 |       {:else}
106 |         <button
107 |           on:click={async () => {
108 |             await authClient.signIn.social({
109 |               provider: "github",
110 |             });
111 |           }}
112 |         >
113 |           Continue with GitHub
114 |         </button>
115 |       {/if}
116 |     </div>
117 | ```
118 | 
```

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

```typescript
  1 | "use client";
  2 | 
  3 | import * as React from "react";
  4 | import { Drawer as DrawerPrimitive } from "vaul";
  5 | 
  6 | import { cn } from "@/lib/utils";
  7 | 
  8 | function Drawer({
  9 | 	...props
 10 | }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
 11 | 	return <DrawerPrimitive.Root data-slot="drawer" {...props} />;
 12 | }
 13 | 
 14 | function DrawerTrigger({
 15 | 	...props
 16 | }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
 17 | 	return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />;
 18 | }
 19 | 
 20 | function DrawerPortal({
 21 | 	...props
 22 | }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
 23 | 	return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />;
 24 | }
 25 | 
 26 | function DrawerClose({
 27 | 	...props
 28 | }: React.ComponentProps<typeof DrawerPrimitive.Close>) {
 29 | 	return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />;
 30 | }
 31 | 
 32 | function DrawerOverlay({
 33 | 	className,
 34 | 	...props
 35 | }: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
 36 | 	return (
 37 | 		<DrawerPrimitive.Overlay
 38 | 			data-slot="drawer-overlay"
 39 | 			className={cn(
 40 | 				"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80",
 41 | 				className,
 42 | 			)}
 43 | 			{...props}
 44 | 		/>
 45 | 	);
 46 | }
 47 | 
 48 | function DrawerContent({
 49 | 	className,
 50 | 	children,
 51 | 	...props
 52 | }: React.ComponentProps<typeof DrawerPrimitive.Content>) {
 53 | 	return (
 54 | 		<DrawerPortal data-slot="drawer-portal">
 55 | 			<DrawerOverlay />
 56 | 			<DrawerPrimitive.Content
 57 | 				data-slot="drawer-content"
 58 | 				className={cn(
 59 | 					"group/drawer-content bg-background fixed z-50 flex h-auto flex-col",
 60 | 					"data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg",
 61 | 					"data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg",
 62 | 					"data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:sm:max-w-sm",
 63 | 					"data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:sm:max-w-sm",
 64 | 					className,
 65 | 				)}
 66 | 				{...props}
 67 | 			>
 68 | 				<div className="bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
 69 | 				{children}
 70 | 			</DrawerPrimitive.Content>
 71 | 		</DrawerPortal>
 72 | 	);
 73 | }
 74 | 
 75 | function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) {
 76 | 	return (
 77 | 		<div
 78 | 			data-slot="drawer-header"
 79 | 			className={cn("flex flex-col gap-1.5 p-4", className)}
 80 | 			{...props}
 81 | 		/>
 82 | 	);
 83 | }
 84 | 
 85 | function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) {
 86 | 	return (
 87 | 		<div
 88 | 			data-slot="drawer-footer"
 89 | 			className={cn("mt-auto flex flex-col gap-2 p-4", className)}
 90 | 			{...props}
 91 | 		/>
 92 | 	);
 93 | }
 94 | 
 95 | function DrawerTitle({
 96 | 	className,
 97 | 	...props
 98 | }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
 99 | 	return (
100 | 		<DrawerPrimitive.Title
101 | 			data-slot="drawer-title"
102 | 			className={cn("text-foreground font-semibold", className)}
103 | 			{...props}
104 | 		/>
105 | 	);
106 | }
107 | 
108 | function DrawerDescription({
109 | 	className,
110 | 	...props
111 | }: React.ComponentProps<typeof DrawerPrimitive.Description>) {
112 | 	return (
113 | 		<DrawerPrimitive.Description
114 | 			data-slot="drawer-description"
115 | 			className={cn("text-muted-foreground text-sm", className)}
116 | 			{...props}
117 | 		/>
118 | 	);
119 | }
120 | 
121 | export {
122 | 	Drawer,
123 | 	DrawerPortal,
124 | 	DrawerOverlay,
125 | 	DrawerTrigger,
126 | 	DrawerClose,
127 | 	DrawerContent,
128 | 	DrawerHeader,
129 | 	DrawerFooter,
130 | 	DrawerTitle,
131 | 	DrawerDescription,
132 | };
133 | 
```

--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------

```json
  1 | {
  2 |   "name": "@better-auth/docs",
  3 |   "version": "0.0.0",
  4 |   "private": true,
  5 |   "type": "module",
  6 |   "scripts": {
  7 |     "build": "next build && pnpm run scripts:sync-orama",
  8 |     "dev": "next dev",
  9 |     "start": "next start",
 10 |     "postinstall": "fumadocs-mdx",
 11 |     "scripts:endpoint-to-doc": "bun ./scripts/endpoint-to-doc/index.ts",
 12 |     "scripts:sync-orama": "node ./scripts/sync-orama.ts"
 13 |   },
 14 |   "dependencies": {
 15 |     "@ai-sdk/openai-compatible": "^1.0.20",
 16 |     "@ai-sdk/react": "^2.0.64",
 17 |     "@better-auth/utils": "0.3.0",
 18 |     "@better-fetch/fetch": "catalog:",
 19 |     "@hookform/resolvers": "^5.2.1",
 20 |     "@oramacloud/client": "^2.1.4",
 21 |     "@radix-ui/react-accordion": "^1.2.12",
 22 |     "@radix-ui/react-alert-dialog": "^1.1.15",
 23 |     "@radix-ui/react-aspect-ratio": "^1.1.7",
 24 |     "@radix-ui/react-avatar": "^1.1.10",
 25 |     "@radix-ui/react-checkbox": "^1.3.3",
 26 |     "@radix-ui/react-collapsible": "^1.1.12",
 27 |     "@radix-ui/react-context-menu": "^2.2.16",
 28 |     "@radix-ui/react-dialog": "^1.1.15",
 29 |     "@radix-ui/react-dropdown-menu": "^2.1.16",
 30 |     "@radix-ui/react-hover-card": "^1.1.15",
 31 |     "@radix-ui/react-icons": "^1.3.2",
 32 |     "@radix-ui/react-label": "^2.1.7",
 33 |     "@radix-ui/react-menubar": "^1.1.16",
 34 |     "@radix-ui/react-navigation-menu": "^1.2.14",
 35 |     "@radix-ui/react-popover": "^1.1.15",
 36 |     "@radix-ui/react-presence": "^1.1.5",
 37 |     "@radix-ui/react-progress": "^1.1.7",
 38 |     "@radix-ui/react-radio-group": "^1.3.8",
 39 |     "@radix-ui/react-scroll-area": "^1.2.10",
 40 |     "@radix-ui/react-select": "^2.2.6",
 41 |     "@radix-ui/react-separator": "^1.1.7",
 42 |     "@radix-ui/react-slider": "^1.3.6",
 43 |     "@radix-ui/react-slot": "^1.2.3",
 44 |     "@radix-ui/react-switch": "^1.2.6",
 45 |     "@radix-ui/react-tabs": "^1.1.13",
 46 |     "@radix-ui/react-toggle": "^1.1.10",
 47 |     "@radix-ui/react-toggle-group": "^1.1.11",
 48 |     "@radix-ui/react-tooltip": "^1.2.8",
 49 |     "@scalar/nextjs-api-reference": "^0.8.17",
 50 |     "@vercel/analytics": "^1.5.0",
 51 |     "@vercel/og": "^0.8.5",
 52 |     "ai": "^5.0.64",
 53 |     "class-variance-authority": "^0.7.1",
 54 |     "clsx": "^2.1.1",
 55 |     "cmdk": "1.1.1",
 56 |     "date-fns": "^4.1.0",
 57 |     "dotenv": "^17.2.2",
 58 |     "embla-carousel-react": "^8.6.0",
 59 |     "foxact": "^0.2.49",
 60 |     "framer-motion": "^12.23.12",
 61 |     "fumadocs-core": "15.8.3",
 62 |     "fumadocs-docgen": "2.1.0",
 63 |     "fumadocs-mdx": "11.8.3",
 64 |     "fumadocs-typescript": "^4.0.6",
 65 |     "fumadocs-ui": "15.8.3",
 66 |     "geist": "^1.4.2",
 67 |     "gray-matter": "^4.0.3",
 68 |     "hast-util-to-jsx-runtime": "^2.3.6",
 69 |     "highlight.js": "^11.11.1",
 70 |     "input-otp": "^1.4.2",
 71 |     "jotai": "^2.13.1",
 72 |     "js-beautify": "^1.15.4",
 73 |     "jsrsasign": "^11.1.0",
 74 |     "lucide-react": "^0.542.0",
 75 |     "motion": "^12.23.12",
 76 |     "next": "16.0.0-beta.0",
 77 |     "next-themes": "^0.4.6",
 78 |     "prism-react-renderer": "^2.4.1",
 79 |     "react": "19.2.0",
 80 |     "react-day-picker": "9.9.0",
 81 |     "react-dom": "19.2.0",
 82 |     "react-hook-form": "^7.62.0",
 83 |     "react-markdown": "^10.1.0",
 84 |     "react-remove-scroll": "^2.7.1",
 85 |     "react-resizable-panels": "^3.0.5",
 86 |     "react-use-measure": "^2.1.7",
 87 |     "recharts": "^3.1.2",
 88 |     "rehype-highlight": "^7.0.2",
 89 |     "remark": "^15.0.1",
 90 |     "remark-gfm": "^4.0.1",
 91 |     "remark-mdx": "^3.1.1",
 92 |     "remark-rehype": "^11.1.2",
 93 |     "remark-stringify": "^11.0.0",
 94 |     "shiki": "^3.13.0",
 95 |     "sonner": "^2.0.7",
 96 |     "tailwind-merge": "^3.3.1",
 97 |     "tailwindcss-animate": "^1.0.7",
 98 |     "unist-util-visit": "^5.0.0",
 99 |     "vaul": "^1.1.2",
100 |     "zod": "^4.1.5"
101 |   },
102 |   "devDependencies": {
103 |     "@tailwindcss/postcss": "^4.1.13",
104 |     "@types/jsrsasign": "^10.5.15",
105 |     "@types/mdx": "^2.0.13",
106 |     "@types/react": "^19.2.2",
107 |     "@types/react-dom": "^19.2.1",
108 |     "mini-svg-data-uri": "^1.4.4",
109 |     "postcss": "^8.5.6",
110 |     "tailwindcss": "^4.1.13",
111 |     "typescript": "^5.9.2"
112 |   }
113 | }
114 | 
```

--------------------------------------------------------------------------------
/packages/core/src/social-providers/github.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { betterFetch } from "@better-fetch/fetch";
  2 | import type { OAuthProvider, ProviderOptions } from "../oauth2";
  3 | import {
  4 | 	createAuthorizationURL,
  5 | 	refreshAccessToken,
  6 | 	validateAuthorizationCode,
  7 | } from "../oauth2";
  8 | 
  9 | export interface GithubProfile {
 10 | 	login: string;
 11 | 	id: string;
 12 | 	node_id: string;
 13 | 	avatar_url: string;
 14 | 	gravatar_id: string;
 15 | 	url: string;
 16 | 	html_url: string;
 17 | 	followers_url: string;
 18 | 	following_url: string;
 19 | 	gists_url: string;
 20 | 	starred_url: string;
 21 | 	subscriptions_url: string;
 22 | 	organizations_url: string;
 23 | 	repos_url: string;
 24 | 	events_url: string;
 25 | 	received_events_url: string;
 26 | 	type: string;
 27 | 	site_admin: boolean;
 28 | 	name: string;
 29 | 	company: string;
 30 | 	blog: string;
 31 | 	location: string;
 32 | 	email: string;
 33 | 	hireable: boolean;
 34 | 	bio: string;
 35 | 	twitter_username: string;
 36 | 	public_repos: string;
 37 | 	public_gists: string;
 38 | 	followers: string;
 39 | 	following: string;
 40 | 	created_at: string;
 41 | 	updated_at: string;
 42 | 	private_gists: string;
 43 | 	total_private_repos: string;
 44 | 	owned_private_repos: string;
 45 | 	disk_usage: string;
 46 | 	collaborators: string;
 47 | 	two_factor_authentication: boolean;
 48 | 	plan: {
 49 | 		name: string;
 50 | 		space: string;
 51 | 		private_repos: string;
 52 | 		collaborators: string;
 53 | 	};
 54 | }
 55 | 
 56 | export interface GithubOptions extends ProviderOptions<GithubProfile> {
 57 | 	clientId: string;
 58 | }
 59 | export const github = (options: GithubOptions) => {
 60 | 	const tokenEndpoint = "https://github.com/login/oauth/access_token";
 61 | 	return {
 62 | 		id: "github",
 63 | 		name: "GitHub",
 64 | 		createAuthorizationURL({ state, scopes, loginHint, redirectURI }) {
 65 | 			const _scopes = options.disableDefaultScope
 66 | 				? []
 67 | 				: ["read:user", "user:email"];
 68 | 			options.scope && _scopes.push(...options.scope);
 69 | 			scopes && _scopes.push(...scopes);
 70 | 			return createAuthorizationURL({
 71 | 				id: "github",
 72 | 				options,
 73 | 				authorizationEndpoint: "https://github.com/login/oauth/authorize",
 74 | 				scopes: _scopes,
 75 | 				state,
 76 | 				redirectURI,
 77 | 				loginHint,
 78 | 				prompt: options.prompt,
 79 | 			});
 80 | 		},
 81 | 		validateAuthorizationCode: async ({ code, redirectURI }) => {
 82 | 			return validateAuthorizationCode({
 83 | 				code,
 84 | 				redirectURI,
 85 | 				options,
 86 | 				tokenEndpoint,
 87 | 			});
 88 | 		},
 89 | 		refreshAccessToken: options.refreshAccessToken
 90 | 			? options.refreshAccessToken
 91 | 			: async (refreshToken) => {
 92 | 					return refreshAccessToken({
 93 | 						refreshToken,
 94 | 						options: {
 95 | 							clientId: options.clientId,
 96 | 							clientKey: options.clientKey,
 97 | 							clientSecret: options.clientSecret,
 98 | 						},
 99 | 						tokenEndpoint: "https://github.com/login/oauth/access_token",
100 | 					});
101 | 				},
102 | 		async getUserInfo(token) {
103 | 			if (options.getUserInfo) {
104 | 				return options.getUserInfo(token);
105 | 			}
106 | 			const { data: profile, error } = await betterFetch<GithubProfile>(
107 | 				"https://api.github.com/user",
108 | 				{
109 | 					headers: {
110 | 						"User-Agent": "better-auth",
111 | 						authorization: `Bearer ${token.accessToken}`,
112 | 					},
113 | 				},
114 | 			);
115 | 			if (error) {
116 | 				return null;
117 | 			}
118 | 			const { data: emails } = await betterFetch<
119 | 				{
120 | 					email: string;
121 | 					primary: boolean;
122 | 					verified: boolean;
123 | 					visibility: "public" | "private";
124 | 				}[]
125 | 			>("https://api.github.com/user/emails", {
126 | 				headers: {
127 | 					Authorization: `Bearer ${token.accessToken}`,
128 | 					"User-Agent": "better-auth",
129 | 				},
130 | 			});
131 | 
132 | 			if (!profile.email && emails) {
133 | 				profile.email = (emails.find((e) => e.primary) ?? emails[0])
134 | 					?.email as string;
135 | 			}
136 | 			const emailVerified =
137 | 				emails?.find((e) => e.email === profile.email)?.verified ?? false;
138 | 
139 | 			const userMap = await options.mapProfileToUser?.(profile);
140 | 			return {
141 | 				user: {
142 | 					id: profile.id,
143 | 					name: profile.name || profile.login,
144 | 					email: profile.email,
145 | 					image: profile.avatar_url,
146 | 					emailVerified,
147 | 					...userMap,
148 | 				},
149 | 				data: profile,
150 | 			};
151 | 		},
152 | 		options,
153 | 	} satisfies OAuthProvider<GithubProfile>;
154 | };
155 | 
```

--------------------------------------------------------------------------------
/docs/content/docs/plugins/bearer.mdx:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: Bearer Token Authentication
  3 | description: Authenticate API requests using Bearer tokens instead of browser cookies
  4 | ---
  5 | 
  6 | The Bearer plugin enables authentication using Bearer tokens as an alternative to browser cookies. It intercepts requests, adding the Bearer token to the Authorization header before forwarding them to your API.
  7 | 
  8 | <Callout type="warn">
  9 |   Use this cautiously; it is intended only for APIs that don't support cookies or require Bearer tokens for authentication. Improper implementation could easily lead to security vulnerabilities.
 10 | </Callout>
 11 | 
 12 | ## Installing the Bearer Plugin
 13 | 
 14 | Add the Bearer plugin to your authentication setup:
 15 | 
 16 | ```ts title="auth.ts"
 17 | import { betterAuth } from "better-auth";
 18 | import { bearer } from "better-auth/plugins";
 19 | 
 20 | export const auth = betterAuth({
 21 |     plugins: [bearer()]
 22 | });
 23 | ```
 24 | 
 25 | ## How to Use Bearer Tokens
 26 | 
 27 | ### 1. Obtain the Bearer Token
 28 | 
 29 | After a successful sign-in, you'll receive a session token in the response headers. Store this token securely (e.g., in `localStorage`):
 30 | 
 31 | ```ts title="auth-client.ts"
 32 | const { data } = await authClient.signIn.email({
 33 |     email: "[email protected]",
 34 |     password: "securepassword"
 35 | }, {
 36 |   onSuccess: (ctx)=>{
 37 |     const authToken = ctx.response.headers.get("set-auth-token") // get the token from the response headers
 38 |     // Store the token securely (e.g., in localStorage)
 39 |     localStorage.setItem("bearer_token", authToken);
 40 |   }
 41 | });
 42 | ```
 43 | 
 44 | You can also set this up globally in your auth client:
 45 | 
 46 | ```ts title="auth-client.ts"
 47 | export const authClient = createAuthClient({
 48 |     fetchOptions: {
 49 |         onSuccess: (ctx) => {
 50 |             const authToken = ctx.response.headers.get("set-auth-token") // get the token from the response headers
 51 |             // Store the token securely (e.g., in localStorage)
 52 |             if(authToken){
 53 |               localStorage.setItem("bearer_token", authToken);
 54 |             }
 55 |         }
 56 |     }
 57 | });
 58 | ```
 59 | 
 60 | 
 61 | You may want to clear the token based on the response status code or other conditions:
 62 | 
 63 | ### 2. Configure the Auth Client
 64 | 
 65 | Set up your auth client to include the Bearer token in all requests:
 66 | 
 67 | ```ts title="auth-client.ts"
 68 | export const authClient = createAuthClient({
 69 |     fetchOptions: {
 70 |         auth: {
 71 |            type:"Bearer",
 72 |            token: () => localStorage.getItem("bearer_token") || "" // get the token from localStorage
 73 |         }
 74 |     }
 75 | });
 76 | ```
 77 | 
 78 | ### 3. Make Authenticated Requests
 79 | 
 80 | Now you can make authenticated API calls:
 81 | 
 82 | ```ts title="auth-client.ts"
 83 | // This request is automatically authenticated
 84 | const { data } = await authClient.listSessions();
 85 | ```
 86 | 
 87 | ### 4. Per-Request Token (Optional)
 88 | 
 89 | You can also provide the token for individual requests:
 90 | 
 91 | ```ts title="auth-client.ts"
 92 | const { data } = await authClient.listSessions({
 93 |     fetchOptions: {
 94 |         headers: {
 95 |             Authorization: `Bearer ${token}`
 96 |         }
 97 |     }
 98 | });
 99 | ```
100 | 
101 | ### 5. Using Bearer Tokens Outside the Auth Client
102 | 
103 | The Bearer token can be used to authenticate any request to your API, even when not using the auth client:
104 | 
105 | ```ts title="api-call.ts"
106 | const token = localStorage.getItem("bearer_token");
107 | 
108 | const response = await fetch("https://api.example.com/data", {
109 |   headers: {
110 |     Authorization: `Bearer ${token}`
111 |   }
112 | });
113 | 
114 | const data = await response.json();
115 | ```
116 | 
117 | And in the server, you can use the `auth.api.getSession` function to authenticate requests:
118 | 
119 | ```ts title="server.ts"
120 | import { auth } from "@/auth";
121 | 
122 | export async function handler(req, res) {
123 |   const session = await auth.api.getSession({
124 |     headers: req.headers
125 |   });
126 |   
127 |   if (!session) {
128 |     return res.status(401).json({ error: "Unauthorized" });
129 |   }
130 |   
131 |   // Process authenticated request
132 |   // ...
133 | }
134 | ```
135 | 
136 | 
137 | ## Options
138 | 
139 | **requireSignature** (boolean): Require the token to be signed. Default: `false`.
140 | 
```

--------------------------------------------------------------------------------
/packages/core/src/social-providers/gitlab.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { betterFetch } from "@better-fetch/fetch";
  2 | import type { OAuthProvider, ProviderOptions } from "../oauth2";
  3 | import {
  4 | 	createAuthorizationURL,
  5 | 	validateAuthorizationCode,
  6 | 	refreshAccessToken,
  7 | } from "../oauth2";
  8 | 
  9 | export interface GitlabProfile extends Record<string, any> {
 10 | 	id: number;
 11 | 	username: string;
 12 | 	email: string;
 13 | 	name: string;
 14 | 	state: string;
 15 | 	avatar_url: string;
 16 | 	web_url: string;
 17 | 	created_at: string;
 18 | 	bio: string;
 19 | 	location?: string;
 20 | 	public_email: string;
 21 | 	skype: string;
 22 | 	linkedin: string;
 23 | 	twitter: string;
 24 | 	website_url: string;
 25 | 	organization: string;
 26 | 	job_title: string;
 27 | 	pronouns: string;
 28 | 	bot: boolean;
 29 | 	work_information?: string;
 30 | 	followers: number;
 31 | 	following: number;
 32 | 	local_time: string;
 33 | 	last_sign_in_at: string;
 34 | 	confirmed_at: string;
 35 | 	theme_id: number;
 36 | 	last_activity_on: string;
 37 | 	color_scheme_id: number;
 38 | 	projects_limit: number;
 39 | 	current_sign_in_at: string;
 40 | 	identities: Array<{
 41 | 		provider: string;
 42 | 		extern_uid: string;
 43 | 	}>;
 44 | 	can_create_group: boolean;
 45 | 	can_create_project: boolean;
 46 | 	two_factor_enabled: boolean;
 47 | 	external: boolean;
 48 | 	private_profile: boolean;
 49 | 	commit_email: string;
 50 | 	shared_runners_minutes_limit: number;
 51 | 	extra_shared_runners_minutes_limit: number;
 52 | }
 53 | 
 54 | export interface GitlabOptions extends ProviderOptions<GitlabProfile> {
 55 | 	clientId: string;
 56 | 	issuer?: string;
 57 | }
 58 | 
 59 | const cleanDoubleSlashes = (input: string = "") => {
 60 | 	return input
 61 | 		.split("://")
 62 | 		.map((str) => str.replace(/\/{2,}/g, "/"))
 63 | 		.join("://");
 64 | };
 65 | 
 66 | const issuerToEndpoints = (issuer?: string) => {
 67 | 	let baseUrl = issuer || "https://gitlab.com";
 68 | 	return {
 69 | 		authorizationEndpoint: cleanDoubleSlashes(`${baseUrl}/oauth/authorize`),
 70 | 		tokenEndpoint: cleanDoubleSlashes(`${baseUrl}/oauth/token`),
 71 | 		userinfoEndpoint: cleanDoubleSlashes(`${baseUrl}/api/v4/user`),
 72 | 	};
 73 | };
 74 | 
 75 | export const gitlab = (options: GitlabOptions) => {
 76 | 	const { authorizationEndpoint, tokenEndpoint, userinfoEndpoint } =
 77 | 		issuerToEndpoints(options.issuer);
 78 | 	const issuerId = "gitlab";
 79 | 	const issuerName = "Gitlab";
 80 | 	return {
 81 | 		id: issuerId,
 82 | 		name: issuerName,
 83 | 		createAuthorizationURL: async ({
 84 | 			state,
 85 | 			scopes,
 86 | 			codeVerifier,
 87 | 			loginHint,
 88 | 			redirectURI,
 89 | 		}) => {
 90 | 			const _scopes = options.disableDefaultScope ? [] : ["read_user"];
 91 | 			options.scope && _scopes.push(...options.scope);
 92 | 			scopes && _scopes.push(...scopes);
 93 | 			return await createAuthorizationURL({
 94 | 				id: issuerId,
 95 | 				options,
 96 | 				authorizationEndpoint,
 97 | 				scopes: _scopes,
 98 | 				state,
 99 | 				redirectURI,
100 | 				codeVerifier,
101 | 				loginHint,
102 | 			});
103 | 		},
104 | 		validateAuthorizationCode: async ({ code, redirectURI, codeVerifier }) => {
105 | 			return validateAuthorizationCode({
106 | 				code,
107 | 				redirectURI,
108 | 				options,
109 | 				codeVerifier,
110 | 				tokenEndpoint,
111 | 			});
112 | 		},
113 | 		refreshAccessToken: options.refreshAccessToken
114 | 			? options.refreshAccessToken
115 | 			: async (refreshToken) => {
116 | 					return refreshAccessToken({
117 | 						refreshToken,
118 | 						options: {
119 | 							clientId: options.clientId,
120 | 							clientKey: options.clientKey,
121 | 							clientSecret: options.clientSecret,
122 | 						},
123 | 						tokenEndpoint: tokenEndpoint,
124 | 					});
125 | 				},
126 | 		async getUserInfo(token) {
127 | 			if (options.getUserInfo) {
128 | 				return options.getUserInfo(token);
129 | 			}
130 | 			const { data: profile, error } = await betterFetch<GitlabProfile>(
131 | 				userinfoEndpoint,
132 | 				{ headers: { authorization: `Bearer ${token.accessToken}` } },
133 | 			);
134 | 			if (error || profile.state !== "active" || profile.locked) {
135 | 				return null;
136 | 			}
137 | 			const userMap = await options.mapProfileToUser?.(profile);
138 | 			return {
139 | 				user: {
140 | 					id: profile.id,
141 | 					name: profile.name ?? profile.username,
142 | 					email: profile.email,
143 | 					image: profile.avatar_url,
144 | 					emailVerified: true,
145 | 					...userMap,
146 | 				},
147 | 				data: profile,
148 | 			};
149 | 		},
150 | 		options,
151 | 	} satisfies OAuthProvider<GitlabProfile>;
152 | };
153 | 
```

--------------------------------------------------------------------------------
/docs/content/docs/adapters/drizzle.mdx:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: Drizzle ORM Adapter
  3 | description: Integrate Better Auth with Drizzle ORM.
  4 | ---
  5 | 
  6 | Drizzle ORM is a powerful and flexible ORM for Node.js and TypeScript. It provides a simple and intuitive API for working with databases, and supports a wide range of databases including MySQL, PostgreSQL, SQLite, and more.
  7 | Read more here: [Drizzle ORM](https://orm.drizzle.team/).
  8 | 
  9 | ## Example Usage
 10 | 
 11 | Make sure you have Drizzle installed and configured.
 12 | Then, you can use the Drizzle adapter to connect to your database.
 13 | 
 14 | ```ts title="auth.ts"
 15 | import { betterAuth } from "better-auth";
 16 | import { drizzleAdapter } from "better-auth/adapters/drizzle";
 17 | import { db } from "./database.ts";
 18 | 
 19 | export const auth = betterAuth({
 20 |   database: drizzleAdapter(db, {
 21 |     // [!code highlight]
 22 |     provider: "sqlite", // or "pg" or "mysql" // [!code highlight]
 23 |   }), // [!code highlight]
 24 |   //... the rest of your config
 25 | });
 26 | ```
 27 | 
 28 | ## Schema generation & migration
 29 | 
 30 | The [Better Auth CLI](/docs/concepts/cli) allows you to generate or migrate
 31 | your database schema based on your Better Auth configuration and plugins.
 32 | 
 33 | To generate the schema required by Better Auth, run the following command:
 34 | 
 35 | ```bash title="Schema Generation"
 36 | npx @better-auth/cli@latest generate
 37 | ```
 38 | 
 39 | To generate and apply the migration, run the following commands:
 40 | 
 41 | ```bash title="Schema Migration"
 42 | npx drizzle-kit generate # generate the migration file
 43 | npx drizzle-kit migrate # apply the migration
 44 | ```
 45 | 
 46 | ## Modifying Table Names
 47 | 
 48 | The Drizzle adapter expects the schema you define to match the table names. For example, if your Drizzle schema maps the `user` table to `users`, you need to manually pass the schema and map it to the user table.
 49 | 
 50 | ```ts
 51 | import { betterAuth } from "better-auth";
 52 | import { db } from "./drizzle";
 53 | import { drizzleAdapter } from "better-auth/adapters/drizzle";
 54 | import { schema } from "./schema";
 55 | 
 56 | export const auth = betterAuth({
 57 |   database: drizzleAdapter(db, {
 58 |     provider: "sqlite", // or "pg" or "mysql"
 59 |     schema: {
 60 |       ...schema,
 61 |       user: schema.users,
 62 |     },
 63 |   }),
 64 | });
 65 | ```
 66 | 
 67 | You can either modify the provided schema values like the example above,
 68 | or you can mutate the auth config's `modelName` property directly.
 69 | For example:
 70 | 
 71 | ```ts
 72 | export const auth = betterAuth({
 73 |   database: drizzleAdapter(db, {
 74 |     provider: "sqlite", // or "pg" or "mysql"
 75 |     schema,
 76 |   }),
 77 |   user: {
 78 |     modelName: "users", // [!code highlight]
 79 |   }
 80 | });
 81 | ```
 82 | 
 83 | ## Modifying Field Names
 84 | 
 85 | We map field names based on property you passed to your Drizzle schema.
 86 | For example, if you want to modify the `email` field to `email_address`,
 87 | you simply need to change the Drizzle schema to:
 88 | 
 89 | ```ts
 90 | export const user = mysqlTable("user", {
 91 |   // Changed field name without changing the schema property name
 92 |   // This allows drizzle & better-auth to still use the original field name,
 93 |   // while your DB uses the modified field name
 94 |   email: varchar("email_address", { length: 255 }).notNull().unique(), // [!code highlight]
 95 |   // ... others
 96 | });
 97 | ```
 98 | 
 99 | You can either modify the Drizzle schema like the example above,
100 | or you can mutate the auth config's `fields` property directly.
101 | For example:
102 | 
103 | ```ts
104 | export const auth = betterAuth({
105 |   database: drizzleAdapter(db, {
106 |     provider: "sqlite", // or "pg" or "mysql"
107 |     schema,
108 |   }),
109 |   user: {
110 |     fields: {
111 |       email: "email_address", // [!code highlight]
112 |     }
113 |   }
114 | });
115 | ```
116 | 
117 | ## Using Plural Table Names
118 | 
119 | If all your tables are using plural form, you can just pass the `usePlural` option:
120 | 
121 | ```ts
122 | export const auth = betterAuth({
123 |   database: drizzleAdapter(db, {
124 |     ...
125 |     usePlural: true,
126 |   }),
127 | });
128 | ```
129 | 
130 | ## Performance Tips
131 | 
132 | If you're looking for performance improvements or tips, take a look at our guide to <Link href="/docs/guides/optimizing-for-performance">performance optimizations</Link>.
133 | 
```

--------------------------------------------------------------------------------
/packages/better-auth/src/plugins/multi-session/multi-session.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { describe, expect, it } from "vitest";
  2 | import { getTestInstance } from "../../test-utils/test-instance";
  3 | import { multiSession } from ".";
  4 | import { multiSessionClient } from "./client";
  5 | import { parseSetCookieHeader } from "../../cookies";
  6 | 
  7 | describe("multi-session", async () => {
  8 | 	const { client, testUser, cookieSetter } = await getTestInstance(
  9 | 		{
 10 | 			plugins: [
 11 | 				multiSession({
 12 | 					maximumSessions: 2,
 13 | 				}),
 14 | 			],
 15 | 		},
 16 | 		{
 17 | 			clientOptions: {
 18 | 				plugins: [multiSessionClient()],
 19 | 			},
 20 | 		},
 21 | 	);
 22 | 
 23 | 	let headers = new Headers();
 24 | 	const testUser2 = {
 25 | 		email: "[email protected]",
 26 | 		password: "password",
 27 | 		name: "Name",
 28 | 	};
 29 | 
 30 | 	it("should set multi session when there is set-cookie header", async () => {
 31 | 		await client.signIn.email(
 32 | 			{
 33 | 				email: testUser.email,
 34 | 				password: testUser.password,
 35 | 			},
 36 | 			{
 37 | 				onResponse(context) {
 38 | 					const setCookieString = context.response.headers.get("set-cookie");
 39 | 					const setCookies = parseSetCookieHeader(setCookieString || "");
 40 | 					const sessionToken = setCookies
 41 | 						.get("better-auth.session_token")
 42 | 						?.value.split(".")[0];
 43 | 					const multiSession = setCookies.get(
 44 | 						`better-auth.session_token_multi-${sessionToken?.toLowerCase()}`,
 45 | 					)?.value;
 46 | 					expect(sessionToken).not.toBe(null);
 47 | 					expect(multiSession).not.toBe(null);
 48 | 					expect(multiSession).toContain(sessionToken);
 49 | 					expect(setCookieString).toContain("better-auth.session_token_multi-");
 50 | 				},
 51 | 				onSuccess: cookieSetter(headers),
 52 | 			},
 53 | 		);
 54 | 		await client.signUp.email(testUser2, {
 55 | 			onSuccess: cookieSetter(headers),
 56 | 		});
 57 | 	});
 58 | 
 59 | 	it("should get active session", async () => {
 60 | 		const session = await client.getSession({
 61 | 			fetchOptions: {
 62 | 				headers,
 63 | 			},
 64 | 		});
 65 | 		expect(session.data?.user.email).toBe(testUser2.email);
 66 | 	});
 67 | 
 68 | 	let sessionToken = "";
 69 | 	it("should list all device sessions", async () => {
 70 | 		const res = await client.multiSession.listDeviceSessions({
 71 | 			fetchOptions: {
 72 | 				headers,
 73 | 			},
 74 | 		});
 75 | 		if (res.data) {
 76 | 			sessionToken =
 77 | 				res.data.find((s) => s.user.email === testUser.email)?.session.token ||
 78 | 				"";
 79 | 		}
 80 | 		expect(res.data).toHaveLength(2);
 81 | 	});
 82 | 
 83 | 	it("should set active session", async () => {
 84 | 		const res = await client.multiSession.setActive({
 85 | 			sessionToken,
 86 | 			fetchOptions: {
 87 | 				headers,
 88 | 			},
 89 | 		});
 90 | 		expect(res.data?.user.email).toBe(testUser.email);
 91 | 	});
 92 | 
 93 | 	it("should revoke a session and set the next active", async () => {
 94 | 		const testUser3 = {
 95 | 			email: "[email protected]",
 96 | 			password: "password",
 97 | 			name: "Name",
 98 | 		};
 99 | 		let token = "";
100 | 		const signUpRes = await client.signUp.email(testUser3, {
101 | 			onSuccess: (ctx) => {
102 | 				const header = ctx.response.headers.get("set-cookie");
103 | 				expect(header).toContain("better-auth.session_token");
104 | 				const cookies = parseSetCookieHeader(header || "");
105 | 				token =
106 | 					cookies.get("better-auth.session_token")?.value.split(".")[0] || "";
107 | 			},
108 | 		});
109 | 		await client.multiSession.revoke(
110 | 			{
111 | 				sessionToken: token,
112 | 			},
113 | 			{
114 | 				onSuccess(context) {
115 | 					expect(context.response.headers.get("set-cookie")).toContain(
116 | 						`better-auth.session_token=`,
117 | 					);
118 | 				},
119 | 				headers,
120 | 			},
121 | 		);
122 | 		const res = await client.multiSession.listDeviceSessions({
123 | 			fetchOptions: {
124 | 				headers,
125 | 			},
126 | 		});
127 | 		expect(res.data).toHaveLength(2);
128 | 	});
129 | 
130 | 	it("should sign-out all sessions", async () => {
131 | 		const newHeaders = new Headers();
132 | 		await client.signOut({
133 | 			fetchOptions: {
134 | 				headers,
135 | 				onSuccess: cookieSetter(newHeaders),
136 | 			},
137 | 		});
138 | 		const res = await client.multiSession.listDeviceSessions({
139 | 			fetchOptions: {
140 | 				headers,
141 | 			},
142 | 		});
143 | 		expect(res.data).toHaveLength(0);
144 | 		const res2 = await client.multiSession.listDeviceSessions({
145 | 			fetchOptions: {
146 | 				headers: newHeaders,
147 | 			},
148 | 		});
149 | 		expect(res2.data).toHaveLength(0);
150 | 	});
151 | });
152 | 
```

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

```typescript
  1 | import { describe, expect, it } from "vitest";
  2 | import { getTestInstance } from "../../test-utils/test-instance";
  3 | import { type Passkey, passkey } from ".";
  4 | import { createAuthClient } from "../../client";
  5 | import { passkeyClient } from "./client";
  6 | 
  7 | describe("passkey", async () => {
  8 | 	const { auth, signInWithTestUser, customFetchImpl } = await getTestInstance({
  9 | 		plugins: [passkey()],
 10 | 	});
 11 | 
 12 | 	it("should generate register options", async () => {
 13 | 		const { headers } = await signInWithTestUser();
 14 | 		const options = await auth.api.generatePasskeyRegistrationOptions({
 15 | 			headers: headers,
 16 | 		});
 17 | 
 18 | 		expect(options).toBeDefined();
 19 | 		expect(options).toHaveProperty("challenge");
 20 | 		expect(options).toHaveProperty("rp");
 21 | 		expect(options).toHaveProperty("user");
 22 | 		expect(options).toHaveProperty("pubKeyCredParams");
 23 | 
 24 | 		const client = createAuthClient({
 25 | 			plugins: [passkeyClient()],
 26 | 			baseURL: "http://localhost:3000/api/auth",
 27 | 			fetchOptions: {
 28 | 				headers: headers,
 29 | 				customFetchImpl,
 30 | 			},
 31 | 		});
 32 | 
 33 | 		await client.$fetch("/passkey/generate-register-options", {
 34 | 			headers: headers,
 35 | 			method: "GET",
 36 | 			onResponse(context) {
 37 | 				const setCookie = context.response.headers.get("Set-Cookie");
 38 | 				expect(setCookie).toBeDefined();
 39 | 				expect(setCookie).toContain("better-auth-passkey");
 40 | 			},
 41 | 		});
 42 | 	});
 43 | 
 44 | 	it("should generate authenticate options", async () => {
 45 | 		const { headers } = await signInWithTestUser();
 46 | 		const options = await auth.api.generatePasskeyAuthenticationOptions({
 47 | 			headers: headers,
 48 | 		});
 49 | 		expect(options).toBeDefined();
 50 | 		expect(options).toHaveProperty("challenge");
 51 | 		expect(options).toHaveProperty("rpId");
 52 | 		expect(options).toHaveProperty("allowCredentials");
 53 | 		expect(options).toHaveProperty("userVerification");
 54 | 	});
 55 | 
 56 | 	it("should generate authenticate options without session (discoverable credentials)", async () => {
 57 | 		// Test without any session/auth headers - simulating a new sign-in with discoverable credentials
 58 | 		const options = await auth.api.generatePasskeyAuthenticationOptions({});
 59 | 		expect(options).toBeDefined();
 60 | 		expect(options).toHaveProperty("challenge");
 61 | 		expect(options).toHaveProperty("rpId");
 62 | 		expect(options).toHaveProperty("userVerification");
 63 | 	});
 64 | 
 65 | 	it("should list user passkeys", async () => {
 66 | 		const { headers, user } = await signInWithTestUser();
 67 | 		const context = await auth.$context;
 68 | 		await context.adapter.create<Omit<Passkey, "id">, Passkey>({
 69 | 			model: "passkey",
 70 | 			data: {
 71 | 				userId: user.id,
 72 | 				publicKey: "mockPublicKey",
 73 | 				name: "mockName",
 74 | 				counter: 0,
 75 | 				deviceType: "singleDevice",
 76 | 				credentialID: "mockCredentialID",
 77 | 				createdAt: new Date(),
 78 | 				backedUp: false,
 79 | 				transports: "mockTransports",
 80 | 				aaguid: "mockAAGUID",
 81 | 			} satisfies Omit<Passkey, "id">,
 82 | 		});
 83 | 
 84 | 		const passkeys = await auth.api.listPasskeys({
 85 | 			headers: headers,
 86 | 		});
 87 | 
 88 | 		expect(Array.isArray(passkeys)).toBe(true);
 89 | 		expect(passkeys[0]).toHaveProperty("id");
 90 | 		expect(passkeys[0]).toHaveProperty("userId");
 91 | 		expect(passkeys[0]).toHaveProperty("publicKey");
 92 | 		expect(passkeys[0]).toHaveProperty("credentialID");
 93 | 		expect(passkeys[0]).toHaveProperty("aaguid");
 94 | 	});
 95 | 
 96 | 	it("should update a passkey", async () => {
 97 | 		const { headers } = await signInWithTestUser();
 98 | 		const passkeys = await auth.api.listPasskeys({
 99 | 			headers: headers,
100 | 		});
101 | 		const passkey = passkeys[0]!;
102 | 		const updateResult = await auth.api.updatePasskey({
103 | 			headers: headers,
104 | 			body: {
105 | 				id: passkey.id,
106 | 				name: "newName",
107 | 			},
108 | 		});
109 | 
110 | 		expect(updateResult.passkey.name).toBe("newName");
111 | 	});
112 | 
113 | 	it("should delete a passkey", async () => {
114 | 		const { headers } = await signInWithTestUser();
115 | 		const deleteResult = await auth.api.deletePasskey({
116 | 			headers: headers,
117 | 			body: {
118 | 				id: "mockPasskeyId",
119 | 			},
120 | 		});
121 | 		expect(deleteResult).toBe(null);
122 | 	});
123 | });
124 | 
```

--------------------------------------------------------------------------------
/docs/content/docs/adapters/community-adapters.mdx:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: Community Adapters
 3 | description: Integrate Better Auth with community made database adapters.
 4 | ---
 5 | 
 6 | This page showcases a list of recommended community made database adapters.
 7 | We encourage you to create any missing database adapters and maybe get added to the list!
 8 | 
 9 | | Adapter                                                                                                 | Database Dialect                           | Author                                                                                                                                                            |
10 | | ------------------------------------------------------------------------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
11 | | [convex-better-auth](https://github.com/get-convex/better-auth)                                         | [Convex Database](https://www.convex.dev/) | <img src="https://github.com/erquhart.png" className="rounded-full w-6 h-6 border opacity-70 m-0 inline mr-1" /> [erquhart](https://github.com/erquhart)          |
12 | | [surreal-better-auth](https://github.com/oskar-gmerek/surreal-better-auth)                              | [SurrealDB](https://surrealdb.com/)        | <img src="https://github.com/oskar-gmerek.png" className="rounded-full w-6 h-6 border opacity-70 m-0 inline mr-1" /> <a href="https://oskargmerek.com" alt="Web Developer UK">Oskar Gmerek</a>   |
13 | | [surrealdb-better-auth](https://github.com/Necmttn/surrealdb-better-auth)                               | [Surreal Database](https://surrealdb.com/) | <img src="https://github.com/Necmttn.png" className="rounded-full w-6 h-6 border opacity-70 m-0 inline mr-1" /> [Necmttn](https://github.com/Necmttn)             |
14 | | [better-auth-surrealdb](https://github.com/msanchezdev/better-auth-surrealdb)                           | [Surreal Database](https://surrealdb.com/) | <img src="https://github.com/msanchezdev.png" className="rounded-full w-6 h-6 border opacity-70 m-0 inline mr-1" /> [msanchezdev](https://github.com/msanchezdev)             |
15 | | [payload-better-auth](https://github.com/ForrestDevs/payload-better-auth/tree/main/packages/db-adapter) | [Payload CMS](https://payloadcms.com/)     | <img src="https://github.com/forrestdevs.png" className="rounded-full w-6 h-6 border opacity-70 m-0 inline mr-1" /> [forrestdevs](https://github.com/forrestdevs) |
16 | | [@ronin/better-auth](https://github.com/ronin-co/better-auth)                                           | [RONIN](https://ronin.co)                  | <img src="https://github.com/ronin-co.png" className="rounded-full w-6 h-6 border opacity-70 m-0 inline mr-1" /> [ronin-co](https://github.com/ronin-co)          |
17 | | [better-auth-instantdb](https://github.com/daveyplate/better-auth-instantdb)                            | [InstantDB](https://www.instantdb.com/)    | <img src="https://github.com/daveycodez.png" className="rounded-full w-6 h-6 border opacity-70 m-0 inline mr-1" /> [daveycodez](https://github.com/daveycodez)    |
18 | | [@nerdfolio/remult-better-auth](https://github.com/nerdfolio/remult-better-auth)                        | [Remult](https://remult.dev/)              | <img src="https://github.com/taivo.png" className="rounded-full w-6 h-6 border opacity-70 m-0 inline mr-1" /> [Tai Vo](https://github.com/taivo)                  |
19 | | [pocketbase-better-auth](https://github.com/LightInn/pocketbase-better-auth)                             | [PocketBase](https://pocketbase.io/)       | <img src="https://github.com/LightInn.png" className="rounded-full w-6 h-6 border opacity-70 m-0 inline mr-1" /> [LightInn](https://github.com/LightInn)          |
20 | 
```

--------------------------------------------------------------------------------
/packages/core/src/oauth2/validate-authorization-code.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { betterFetch } from "@better-fetch/fetch";
  2 | import { jwtVerify } from "jose";
  3 | import type { ProviderOptions } from "./index";
  4 | import { getOAuth2Tokens } from "./index";
  5 | import { base64 } from "@better-auth/utils/base64";
  6 | 
  7 | export function createAuthorizationCodeRequest({
  8 | 	code,
  9 | 	codeVerifier,
 10 | 	redirectURI,
 11 | 	options,
 12 | 	authentication,
 13 | 	deviceId,
 14 | 	headers,
 15 | 	additionalParams = {},
 16 | 	resource,
 17 | }: {
 18 | 	code: string;
 19 | 	redirectURI: string;
 20 | 	options: Partial<ProviderOptions>;
 21 | 	codeVerifier?: string;
 22 | 	deviceId?: string;
 23 | 	authentication?: "basic" | "post";
 24 | 	headers?: Record<string, string>;
 25 | 	additionalParams?: Record<string, string>;
 26 | 	resource?: string | string[];
 27 | }) {
 28 | 	const body = new URLSearchParams();
 29 | 	const requestHeaders: Record<string, any> = {
 30 | 		"content-type": "application/x-www-form-urlencoded",
 31 | 		accept: "application/json",
 32 | 		"user-agent": "better-auth",
 33 | 		...headers,
 34 | 	};
 35 | 	body.set("grant_type", "authorization_code");
 36 | 	body.set("code", code);
 37 | 	codeVerifier && body.set("code_verifier", codeVerifier);
 38 | 	options.clientKey && body.set("client_key", options.clientKey);
 39 | 	deviceId && body.set("device_id", deviceId);
 40 | 	body.set("redirect_uri", options.redirectURI || redirectURI);
 41 | 	if (resource) {
 42 | 		if (typeof resource === "string") {
 43 | 			body.append("resource", resource);
 44 | 		} else {
 45 | 			for (const _resource of resource) {
 46 | 				body.append("resource", _resource);
 47 | 			}
 48 | 		}
 49 | 	}
 50 | 	// Use standard Base64 encoding for HTTP Basic Auth (OAuth2 spec, RFC 7617)
 51 | 	// Fixes compatibility with providers like Notion, Twitter, etc.
 52 | 	if (authentication === "basic") {
 53 | 		const primaryClientId = Array.isArray(options.clientId)
 54 | 			? options.clientId[0]
 55 | 			: options.clientId;
 56 | 		const encodedCredentials = base64.encode(
 57 | 			`${primaryClientId}:${options.clientSecret ?? ""}`,
 58 | 		);
 59 | 		requestHeaders["authorization"] = `Basic ${encodedCredentials}`;
 60 | 	} else {
 61 | 		const primaryClientId = Array.isArray(options.clientId)
 62 | 			? options.clientId[0]
 63 | 			: options.clientId;
 64 | 		body.set("client_id", primaryClientId);
 65 | 		if (options.clientSecret) {
 66 | 			body.set("client_secret", options.clientSecret);
 67 | 		}
 68 | 	}
 69 | 
 70 | 	for (const [key, value] of Object.entries(additionalParams)) {
 71 | 		if (!body.has(key)) body.append(key, value);
 72 | 	}
 73 | 
 74 | 	return {
 75 | 		body,
 76 | 		headers: requestHeaders,
 77 | 	};
 78 | }
 79 | 
 80 | export async function validateAuthorizationCode({
 81 | 	code,
 82 | 	codeVerifier,
 83 | 	redirectURI,
 84 | 	options,
 85 | 	tokenEndpoint,
 86 | 	authentication,
 87 | 	deviceId,
 88 | 	headers,
 89 | 	additionalParams = {},
 90 | 	resource,
 91 | }: {
 92 | 	code: string;
 93 | 	redirectURI: string;
 94 | 	options: Partial<ProviderOptions>;
 95 | 	codeVerifier?: string;
 96 | 	deviceId?: string;
 97 | 	tokenEndpoint: string;
 98 | 	authentication?: "basic" | "post";
 99 | 	headers?: Record<string, string>;
100 | 	additionalParams?: Record<string, string>;
101 | 	resource?: string | string[];
102 | }) {
103 | 	const { body, headers: requestHeaders } = createAuthorizationCodeRequest({
104 | 		code,
105 | 		codeVerifier,
106 | 		redirectURI,
107 | 		options,
108 | 		authentication,
109 | 		deviceId,
110 | 		headers,
111 | 		additionalParams,
112 | 		resource,
113 | 	});
114 | 
115 | 	const { data, error } = await betterFetch<object>(tokenEndpoint, {
116 | 		method: "POST",
117 | 		body: body,
118 | 		headers: requestHeaders,
119 | 	});
120 | 
121 | 	if (error) {
122 | 		throw error;
123 | 	}
124 | 	const tokens = getOAuth2Tokens(data);
125 | 	return tokens;
126 | }
127 | 
128 | export async function validateToken(token: string, jwksEndpoint: string) {
129 | 	const { data, error } = await betterFetch<{
130 | 		keys: {
131 | 			kid: string;
132 | 			kty: string;
133 | 			use: string;
134 | 			n: string;
135 | 			e: string;
136 | 			x5c: string[];
137 | 		}[];
138 | 	}>(jwksEndpoint, {
139 | 		method: "GET",
140 | 		headers: {
141 | 			accept: "application/json",
142 | 			"user-agent": "better-auth",
143 | 		},
144 | 	});
145 | 	if (error) {
146 | 		throw error;
147 | 	}
148 | 	const keys = data["keys"];
149 | 	const header = JSON.parse(atob(token.split(".")[0]!));
150 | 	const key = keys.find((key) => key.kid === header.kid);
151 | 	if (!key) {
152 | 		throw new Error("Key not found");
153 | 	}
154 | 	const verified = await jwtVerify(token, key);
155 | 	return verified;
156 | }
157 | 
```

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

```typescript
  1 | "use client";
  2 | 
  3 | import * as React from "react";
  4 | import * as SheetPrimitive from "@radix-ui/react-dialog";
  5 | import { XIcon } from "lucide-react";
  6 | 
  7 | import { cn } from "@/lib/utils";
  8 | 
  9 | function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
 10 | 	return <SheetPrimitive.Root data-slot="sheet" {...props} />;
 11 | }
 12 | 
 13 | function SheetTrigger({
 14 | 	...props
 15 | }: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
 16 | 	return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />;
 17 | }
 18 | 
 19 | function SheetClose({
 20 | 	...props
 21 | }: React.ComponentProps<typeof SheetPrimitive.Close>) {
 22 | 	return <SheetPrimitive.Close data-slot="sheet-close" {...props} />;
 23 | }
 24 | 
 25 | function SheetPortal({
 26 | 	...props
 27 | }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
 28 | 	return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />;
 29 | }
 30 | 
 31 | function SheetOverlay({
 32 | 	className,
 33 | 	...props
 34 | }: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
 35 | 	return (
 36 | 		<SheetPrimitive.Overlay
 37 | 			data-slot="sheet-overlay"
 38 | 			className={cn(
 39 | 				"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80",
 40 | 				className,
 41 | 			)}
 42 | 			{...props}
 43 | 		/>
 44 | 	);
 45 | }
 46 | 
 47 | function SheetContent({
 48 | 	className,
 49 | 	children,
 50 | 	side = "right",
 51 | 	...props
 52 | }: React.ComponentProps<typeof SheetPrimitive.Content> & {
 53 | 	side?: "top" | "right" | "bottom" | "left";
 54 | }) {
 55 | 	return (
 56 | 		<SheetPortal>
 57 | 			<SheetOverlay />
 58 | 			<SheetPrimitive.Content
 59 | 				data-slot="sheet-content"
 60 | 				className={cn(
 61 | 					"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
 62 | 					side === "right" &&
 63 | 						"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
 64 | 					side === "left" &&
 65 | 						"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
 66 | 					side === "top" &&
 67 | 						"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
 68 | 					side === "bottom" &&
 69 | 						"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
 70 | 					className,
 71 | 				)}
 72 | 				{...props}
 73 | 			>
 74 | 				{children}
 75 | 				<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
 76 | 					<XIcon className="size-4" />
 77 | 					<span className="sr-only">Close</span>
 78 | 				</SheetPrimitive.Close>
 79 | 			</SheetPrimitive.Content>
 80 | 		</SheetPortal>
 81 | 	);
 82 | }
 83 | 
 84 | function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
 85 | 	return (
 86 | 		<div
 87 | 			data-slot="sheet-header"
 88 | 			className={cn("flex flex-col gap-1.5 p-4", className)}
 89 | 			{...props}
 90 | 		/>
 91 | 	);
 92 | }
 93 | 
 94 | function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
 95 | 	return (
 96 | 		<div
 97 | 			data-slot="sheet-footer"
 98 | 			className={cn("mt-auto flex flex-col gap-2 p-4", className)}
 99 | 			{...props}
100 | 		/>
101 | 	);
102 | }
103 | 
104 | function SheetTitle({
105 | 	className,
106 | 	...props
107 | }: React.ComponentProps<typeof SheetPrimitive.Title>) {
108 | 	return (
109 | 		<SheetPrimitive.Title
110 | 			data-slot="sheet-title"
111 | 			className={cn("text-foreground font-semibold", className)}
112 | 			{...props}
113 | 		/>
114 | 	);
115 | }
116 | 
117 | function SheetDescription({
118 | 	className,
119 | 	...props
120 | }: React.ComponentProps<typeof SheetPrimitive.Description>) {
121 | 	return (
122 | 		<SheetPrimitive.Description
123 | 			data-slot="sheet-description"
124 | 			className={cn("text-muted-foreground text-sm", className)}
125 | 			{...props}
126 | 		/>
127 | 	);
128 | }
129 | 
130 | export {
131 | 	Sheet,
132 | 	SheetTrigger,
133 | 	SheetClose,
134 | 	SheetContent,
135 | 	SheetHeader,
136 | 	SheetFooter,
137 | 	SheetTitle,
138 | 	SheetDescription,
139 | };
140 | 
```

--------------------------------------------------------------------------------
/docs/content/docs/authentication/other-social-providers.mdx:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: Other Social Providers
  3 | description: Other social providers setup and usage.
  4 | ---
  5 | 
  6 | Better Auth provides out of the box support for a [Generic OAuth Plugin](/docs/plugins/generic-oauth) which allows you to use any social provider that implements the OAuth2 protocol or OpenID Connect (OIDC) flows.
  7 | 
  8 | To use a provider that is not supported out of the box, you can use the [Generic OAuth Plugin](/docs/plugins/generic-oauth).
  9 | 
 10 | ## Installation
 11 | 
 12 | <Steps>
 13 |   <Step>
 14 |     ### Add the plugin to your auth config
 15 | 
 16 |     To use the Generic OAuth plugin, add it to your auth config.
 17 | 
 18 |     ```ts title="auth.ts"
 19 |     import { betterAuth } from "better-auth"
 20 |     import { genericOAuth } from "better-auth/plugins" // [!code highlight]
 21 | 
 22 |     export const auth = betterAuth({
 23 |         // ... other config options
 24 |         plugins: [
 25 |             genericOAuth({ // [!code highlight]
 26 |                 config: [ // [!code highlight]
 27 |                     { // [!code highlight]
 28 |                         providerId: "provider-id", // [!code highlight]
 29 |                         clientId: "test-client-id", // [!code highlight]
 30 |                         clientSecret: "test-client-secret", // [!code highlight]
 31 |                         discoveryUrl: "https://auth.example.com/.well-known/openid-configuration", // [!code highlight]
 32 |                         // ... other config options // [!code highlight]
 33 |                     }, // [!code highlight]
 34 |                     // Add more providers as needed // [!code highlight]
 35 |                 ] // [!code highlight]
 36 |             }) // [!code highlight]
 37 |         ]
 38 |     })
 39 |     ```
 40 | 
 41 |   </Step>
 42 | 
 43 |   <Step>
 44 |     ### Add the client plugin
 45 | 
 46 |     Include the Generic OAuth client plugin in your authentication client instance.
 47 | 
 48 |     ```ts title="auth-client.ts"
 49 |     import { createAuthClient } from "better-auth/client"
 50 |     import { genericOAuthClient } from "better-auth/client/plugins"
 51 | 
 52 |     const authClient = createAuthClient({
 53 |         plugins: [
 54 |             genericOAuthClient()
 55 |         ]
 56 |     })
 57 |     ```
 58 | 
 59 |   </Step>
 60 | </Steps>
 61 | 
 62 | <Callout>
 63 |   Read more about installation and usage of the Generic Oauth plugin
 64 |   [here](/docs/plugins/generic-oauth#usage).
 65 | </Callout>
 66 | 
 67 | ## Example usage
 68 | 
 69 | ### Instagram Example
 70 | 
 71 | ```ts title="auth.ts"
 72 | import { betterAuth } from "better-auth";
 73 | import { genericOAuth } from "better-auth/plugins";
 74 | 
 75 | export const auth = betterAuth({
 76 |   // ... other config options
 77 |   plugins: [
 78 |     genericOAuth({
 79 |       config: [
 80 |         {
 81 |           providerId: "instagram",
 82 |           clientId: process.env.INSTAGRAM_CLIENT_ID as string,
 83 |           clientSecret: process.env.INSTAGRAM_CLIENT_SECRET as string,
 84 |           authorizationUrl: "https://api.instagram.com/oauth/authorize",
 85 |           tokenUrl: "https://api.instagram.com/oauth/access_token",
 86 |           scopes: ["user_profile", "user_media"],
 87 |         },
 88 |       ],
 89 |     }),
 90 |   ],
 91 | });
 92 | ```
 93 | 
 94 | ```ts title="sign-in.ts"
 95 | const response = await authClient.signIn.oauth2({
 96 |   providerId: "instagram",
 97 |   callbackURL: "/dashboard", // the path to redirect to after the user is authenticated
 98 | });
 99 | ```
100 | 
101 | ### Coinbase Example
102 | 
103 | ```ts title="auth.ts"
104 | import { betterAuth } from "better-auth";
105 | import { genericOAuth } from "better-auth/plugins";
106 | 
107 | export const auth = betterAuth({
108 |   // ... other config options
109 |   plugins: [
110 |     genericOAuth({
111 |       config: [
112 |         {
113 |           providerId: "coinbase",
114 |           clientId: process.env.COINBASE_CLIENT_ID as string,
115 |           clientSecret: process.env.COINBASE_CLIENT_SECRET as string,
116 |           authorizationUrl: "https://www.coinbase.com/oauth/authorize",
117 |           tokenUrl: "https://api.coinbase.com/oauth/token",
118 |           scopes: ["wallet:user:read"], // and more...
119 |         },
120 |       ],
121 |     }),
122 |   ],
123 | });
124 | ```
125 | 
126 | ```ts title="sign-in.ts"
127 | const response = await authClient.signIn.oauth2({
128 |   providerId: "coinbase",
129 |   callbackURL: "/dashboard", // the path to redirect to after the user is authenticated
130 | });
131 | ```
132 | 
```

--------------------------------------------------------------------------------
/docs/components/landing/grid-pattern.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | "use client";
  2 | 
  3 | import { motion } from "framer-motion";
  4 | import { useEffect, useId, useRef, useState } from "react";
  5 | 
  6 | const Block = ({
  7 | 	x,
  8 | 	y,
  9 | 	...props
 10 | }: Omit<React.ComponentPropsWithoutRef<typeof motion.path>, "x" | "y"> & {
 11 | 	x: number;
 12 | 	y: number;
 13 | }) => {
 14 | 	return (
 15 | 		<motion.path
 16 | 			transform={`translate(${-32 * y + 96 * x} ${160 * y})`}
 17 | 			d="M45.119 4.5a11.5 11.5 0 0 0-11.277 9.245l-25.6 128C6.82 148.861 12.262 155.5 19.52 155.5h63.366a11.5 11.5 0 0 0 11.277-9.245l25.6-128c1.423-7.116-4.02-13.755-11.277-13.755H45.119Z"
 18 | 			{...props}
 19 | 		/>
 20 | 	);
 21 | };
 22 | 
 23 | export const GridPattern = ({ yOffset = 0, interactive = false, ...props }) => {
 24 | 	const id = useId();
 25 | 	const ref = useRef<React.ElementRef<"svg">>(null);
 26 | 	const currentBlock = useRef<[x: number, y: number]>();
 27 | 	const counter = useRef(0);
 28 | 	const [hoveredBlocks, setHoveredBlocks] = useState<
 29 | 		Array<[x: number, y: number, key: number]>
 30 | 	>([]);
 31 | 	const staticBlocks = [
 32 | 		[1, 1],
 33 | 		[2, 2],
 34 | 		[4, 3],
 35 | 		[6, 2],
 36 | 		[7, 4],
 37 | 		[5, 5],
 38 | 	];
 39 | 
 40 | 	useEffect(() => {
 41 | 		if (!interactive) {
 42 | 			return;
 43 | 		}
 44 | 
 45 | 		function onMouseMove(event: MouseEvent) {
 46 | 			if (!ref.current) {
 47 | 				return;
 48 | 			}
 49 | 
 50 | 			const rect = ref.current.getBoundingClientRect();
 51 | 			let x = event.clientX - rect.left;
 52 | 			let y = event.clientY - rect.top;
 53 | 			if (x < 0 || y < 0 || x > rect.width || y > rect.height) {
 54 | 				return;
 55 | 			}
 56 | 
 57 | 			x = x - rect.width / 2 - 32;
 58 | 			y = y - yOffset;
 59 | 			x += Math.tan(32 / 160) * y;
 60 | 			x = Math.floor(x / 96);
 61 | 			y = Math.floor(y / 160);
 62 | 
 63 | 			if (currentBlock.current?.[0] === x && currentBlock.current?.[1] === y) {
 64 | 				return;
 65 | 			}
 66 | 
 67 | 			currentBlock.current = [x, y];
 68 | 
 69 | 			setHoveredBlocks((blocks) => {
 70 | 				const key = counter.current++;
 71 | 				const block = [x, y, key] as (typeof hoveredBlocks)[number];
 72 | 				return [...blocks, block].filter(
 73 | 					(block) => !(block[0] === x && block[1] === y && block[2] !== key),
 74 | 				);
 75 | 			});
 76 | 		}
 77 | 
 78 | 		window.addEventListener("mousemove", onMouseMove);
 79 | 
 80 | 		return () => {
 81 | 			window.removeEventListener("mousemove", onMouseMove);
 82 | 		};
 83 | 	}, [yOffset, interactive]);
 84 | 
 85 | 	return (
 86 | 		<motion.svg
 87 | 			ref={ref}
 88 | 			aria-hidden="true"
 89 | 			{...props}
 90 | 			exit={{ opacity: 0 }}
 91 | 			animate={{ opacity: 1 }}
 92 | 			initial={{ opacity: 0 }}
 93 | 		>
 94 | 			<rect width="100%" height="100%" fill={`url(#${id})`} strokeWidth="0" />
 95 | 			<svg x="50%" y={yOffset} strokeWidth="0" className="overflow-visible">
 96 | 				{staticBlocks.map((block) => (
 97 | 					<Block key={`${block}`} x={block[0]} y={block[1]} />
 98 | 				))}
 99 | 				{hoveredBlocks.map((block) => (
100 | 					<Block
101 | 						key={block[2]}
102 | 						x={block[0]}
103 | 						y={block[1]}
104 | 						animate={{ opacity: [0, 1, 0] }}
105 | 						transition={{ duration: 1, times: [0, 0, 1] }}
106 | 						onAnimationComplete={() => {
107 | 							setHoveredBlocks((blocks) =>
108 | 								blocks.filter((b) => b[2] !== block[2]),
109 | 							);
110 | 						}}
111 | 					/>
112 | 				))}
113 | 			</svg>
114 | 			<defs>
115 | 				<pattern
116 | 					id={id}
117 | 					width="96"
118 | 					height="480"
119 | 					x="50%"
120 | 					patternUnits="userSpaceOnUse"
121 | 					patternTransform={`translate(0 ${yOffset})`}
122 | 					fill="none"
123 | 				>
124 | 					<path d="M128 0 98.572 147.138A16 16 0 0 1 82.883 160H13.117a16 16 0 0 0-15.69 12.862l-26.855 134.276A16 16 0 0 1-45.117 320H-116M64-160 34.572-12.862A16 16 0 0 1 18.883 0h-69.766a16 16 0 0 0-15.69 12.862l-26.855 134.276A16 16 0 0 1-109.117 160H-180M192 160l-29.428 147.138A15.999 15.999 0 0 1 146.883 320H77.117a16 16 0 0 0-15.69 12.862L34.573 467.138A16 16 0 0 1 18.883 480H-52M-136 480h58.883a16 16 0 0 0 15.69-12.862l26.855-134.276A16 16 0 0 1-18.883 320h69.766a16 16 0 0 0 15.69-12.862l26.855-134.276A16 16 0 0 1 109.117 160H192M-72 640h58.883a16 16 0 0 0 15.69-12.862l26.855-134.276A16 16 0 0 1 45.117 480h69.766a15.999 15.999 0 0 0 15.689-12.862l26.856-134.276A15.999 15.999 0 0 1 173.117 320H256M-200 320h58.883a15.999 15.999 0 0 0 15.689-12.862l26.856-134.276A16 16 0 0 1-82.883 160h69.766a16 16 0 0 0 15.69-12.862L29.427 12.862A16 16 0 0 1 45.117 0H128" />
125 | 				</pattern>
126 | 			</defs>
127 | 		</motion.svg>
128 | 	);
129 | };
130 | 
```

--------------------------------------------------------------------------------
/packages/better-auth/src/plugins/two-factor/verify-two-factor.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { APIError } from "better-call";
  2 | import { TRUST_DEVICE_COOKIE_NAME, TWO_FACTOR_COOKIE_NAME } from "./constant";
  3 | import { setSessionCookie } from "../../cookies";
  4 | import { getSessionFromCtx } from "../../api";
  5 | import type { UserWithTwoFactor } from "./types";
  6 | import { createHMAC } from "@better-auth/utils/hmac";
  7 | import { TWO_FACTOR_ERROR_CODES } from "./error-code";
  8 | import type { GenericEndpointContext } from "@better-auth/core";
  9 | 
 10 | export async function verifyTwoFactor(ctx: GenericEndpointContext) {
 11 | 	const session = await getSessionFromCtx(ctx);
 12 | 	if (!session) {
 13 | 		const cookieName = ctx.context.createAuthCookie(TWO_FACTOR_COOKIE_NAME);
 14 | 		const twoFactorCookie = await ctx.getSignedCookie(
 15 | 			cookieName.name,
 16 | 			ctx.context.secret,
 17 | 		);
 18 | 		if (!twoFactorCookie) {
 19 | 			throw new APIError("UNAUTHORIZED", {
 20 | 				message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE,
 21 | 			});
 22 | 		}
 23 | 		const verificationToken =
 24 | 			await ctx.context.internalAdapter.findVerificationValue(twoFactorCookie);
 25 | 		if (!verificationToken) {
 26 | 			throw new APIError("UNAUTHORIZED", {
 27 | 				message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE,
 28 | 			});
 29 | 		}
 30 | 		const user = (await ctx.context.internalAdapter.findUserById(
 31 | 			verificationToken.value,
 32 | 		)) as UserWithTwoFactor;
 33 | 		if (!user) {
 34 | 			throw new APIError("UNAUTHORIZED", {
 35 | 				message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE,
 36 | 			});
 37 | 		}
 38 | 		const dontRememberMe = await ctx.getSignedCookie(
 39 | 			ctx.context.authCookies.dontRememberToken.name,
 40 | 			ctx.context.secret,
 41 | 		);
 42 | 		return {
 43 | 			valid: async (ctx: GenericEndpointContext) => {
 44 | 				const session = await ctx.context.internalAdapter.createSession(
 45 | 					verificationToken.value,
 46 | 					!!dontRememberMe,
 47 | 				);
 48 | 				if (!session) {
 49 | 					throw new APIError("INTERNAL_SERVER_ERROR", {
 50 | 						message: "failed to create session",
 51 | 					});
 52 | 				}
 53 | 				await setSessionCookie(ctx, {
 54 | 					session,
 55 | 					user,
 56 | 				});
 57 | 				if (ctx.body.trustDevice) {
 58 | 					const trustDeviceCookie = ctx.context.createAuthCookie(
 59 | 						TRUST_DEVICE_COOKIE_NAME,
 60 | 						{
 61 | 							maxAge: 30 * 24 * 60 * 60, // 30 days, it'll be refreshed on sign in requests
 62 | 						},
 63 | 					);
 64 | 					/**
 65 | 					 * create a token that will be used to
 66 | 					 * verify the device
 67 | 					 */
 68 | 					const token = await createHMAC("SHA-256", "base64urlnopad").sign(
 69 | 						ctx.context.secret,
 70 | 						`${user.id}!${session.token}`,
 71 | 					);
 72 | 					await ctx.setSignedCookie(
 73 | 						trustDeviceCookie.name,
 74 | 						`${token}!${session.token}`,
 75 | 						ctx.context.secret,
 76 | 						trustDeviceCookie.attributes,
 77 | 					);
 78 | 					// delete the dont remember me cookie
 79 | 					ctx.setCookie(ctx.context.authCookies.dontRememberToken.name, "", {
 80 | 						maxAge: 0,
 81 | 					});
 82 | 					// delete the two factor cookie
 83 | 					ctx.setCookie(cookieName.name, "", {
 84 | 						maxAge: 0,
 85 | 					});
 86 | 				}
 87 | 				return ctx.json({
 88 | 					token: session.token,
 89 | 					user: {
 90 | 						id: user.id,
 91 | 						email: user.email,
 92 | 						emailVerified: user.emailVerified,
 93 | 						name: user.name,
 94 | 						image: user.image,
 95 | 						createdAt: user.createdAt,
 96 | 						updatedAt: user.updatedAt,
 97 | 					},
 98 | 				});
 99 | 			},
100 | 			invalid: async (errorKey: keyof typeof TWO_FACTOR_ERROR_CODES) => {
101 | 				throw new APIError("UNAUTHORIZED", {
102 | 					message: TWO_FACTOR_ERROR_CODES[errorKey],
103 | 				});
104 | 			},
105 | 			session: {
106 | 				session: null,
107 | 				user,
108 | 			},
109 | 			key: twoFactorCookie,
110 | 		};
111 | 	}
112 | 	return {
113 | 		valid: async (ctx: GenericEndpointContext) => {
114 | 			return ctx.json({
115 | 				token: session.session.token,
116 | 				user: {
117 | 					id: session.user.id,
118 | 					email: session.user.email,
119 | 					emailVerified: session.user.emailVerified,
120 | 					name: session.user.name,
121 | 					image: session.user.image,
122 | 					createdAt: session.user.createdAt,
123 | 					updatedAt: session.user.updatedAt,
124 | 				},
125 | 			});
126 | 		},
127 | 		invalid: async () => {
128 | 			throw new APIError("UNAUTHORIZED", {
129 | 				message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE,
130 | 			});
131 | 		},
132 | 		session,
133 | 		key: `${session.user.id}!${session.session.id}`,
134 | 	};
135 | }
136 | 
```

--------------------------------------------------------------------------------
/packages/core/src/env/color-depth.ts:
--------------------------------------------------------------------------------

```typescript
  1 | // Source code copied & modified from node internals: https://github.com/nodejs/node/blob/5b32bb1573dace2dd058c05ac4fab1e4e446c775/lib/internal/tty.js#L123
  2 | import { env, getEnvVar } from "./env-impl";
  3 | 
  4 | const COLORS_2 = 1;
  5 | const COLORS_16 = 4;
  6 | const COLORS_256 = 8;
  7 | const COLORS_16m = 24;
  8 | 
  9 | const TERM_ENVS: Record<string, number> = {
 10 | 	eterm: COLORS_16,
 11 | 	cons25: COLORS_16,
 12 | 	console: COLORS_16,
 13 | 	cygwin: COLORS_16,
 14 | 	dtterm: COLORS_16,
 15 | 	gnome: COLORS_16,
 16 | 	hurd: COLORS_16,
 17 | 	jfbterm: COLORS_16,
 18 | 	konsole: COLORS_16,
 19 | 	kterm: COLORS_16,
 20 | 	mlterm: COLORS_16,
 21 | 	mosh: COLORS_16m,
 22 | 	putty: COLORS_16,
 23 | 	st: COLORS_16,
 24 | 	// http://lists.schmorp.de/pipermail/rxvt-unicode/2016q2/002261.html
 25 | 	"rxvt-unicode-24bit": COLORS_16m,
 26 | 	// https://bugs.launchpad.net/terminator/+bug/1030562
 27 | 	terminator: COLORS_16m,
 28 | 	"xterm-kitty": COLORS_16m,
 29 | };
 30 | 
 31 | const CI_ENVS_MAP = new Map(
 32 | 	Object.entries({
 33 | 		APPVEYOR: COLORS_256,
 34 | 		BUILDKITE: COLORS_256,
 35 | 		CIRCLECI: COLORS_16m,
 36 | 		DRONE: COLORS_256,
 37 | 		GITEA_ACTIONS: COLORS_16m,
 38 | 		GITHUB_ACTIONS: COLORS_16m,
 39 | 		GITLAB_CI: COLORS_256,
 40 | 		TRAVIS: COLORS_256,
 41 | 	}),
 42 | );
 43 | 
 44 | const TERM_ENVS_REG_EXP = [
 45 | 	/ansi/,
 46 | 	/color/,
 47 | 	/linux/,
 48 | 	/direct/,
 49 | 	/^con[0-9]*x[0-9]/,
 50 | 	/^rxvt/,
 51 | 	/^screen/,
 52 | 	/^xterm/,
 53 | 	/^vt100/,
 54 | 	/^vt220/,
 55 | ];
 56 | 
 57 | // The `getColorDepth` API got inspired by multiple sources such as
 58 | // https://github.com/chalk/supports-color,
 59 | // https://github.com/isaacs/color-support.
 60 | export function getColorDepth(): number {
 61 | 	// Use level 0-3 to support the same levels as `chalk` does. This is done for
 62 | 	// consistency throughout the ecosystem.
 63 | 	if (getEnvVar("FORCE_COLOR") !== undefined) {
 64 | 		switch (getEnvVar("FORCE_COLOR")) {
 65 | 			case "":
 66 | 			case "1":
 67 | 			case "true":
 68 | 				return COLORS_16;
 69 | 			case "2":
 70 | 				return COLORS_256;
 71 | 			case "3":
 72 | 				return COLORS_16m;
 73 | 			default:
 74 | 				return COLORS_2;
 75 | 		}
 76 | 	}
 77 | 
 78 | 	if (
 79 | 		(getEnvVar("NODE_DISABLE_COLORS") !== undefined &&
 80 | 			getEnvVar("NODE_DISABLE_COLORS") !== "") ||
 81 | 		// See https://no-color.org/
 82 | 		(getEnvVar("NO_COLOR") !== undefined && getEnvVar("NO_COLOR") !== "") ||
 83 | 		// The "dumb" special terminal, as defined by terminfo, doesn't support
 84 | 		// ANSI color control codes.
 85 | 		// See https://invisible-island.net/ncurses/terminfo.ti.html#toc-_Specials
 86 | 		getEnvVar("TERM") === "dumb"
 87 | 	) {
 88 | 		return COLORS_2;
 89 | 	}
 90 | 
 91 | 	// Edge runtime doesn't support `process?.platform` syntax
 92 | 	// if (typeof process !== "undefined" && process?.platform === "win32") {
 93 | 	// 	// Windows 10 build 14931 (from 2016) has true color support
 94 | 	// 	return COLORS_16m;
 95 | 	// }
 96 | 
 97 | 	if (getEnvVar("TMUX")) {
 98 | 		return COLORS_16m;
 99 | 	}
100 | 
101 | 	// Azure DevOps
102 | 	if ("TF_BUILD" in env && "AGENT_NAME" in env) {
103 | 		return COLORS_16;
104 | 	}
105 | 
106 | 	if ("CI" in env) {
107 | 		for (const { 0: envName, 1: colors } of CI_ENVS_MAP) {
108 | 			if (envName in env) {
109 | 				return colors;
110 | 			}
111 | 		}
112 | 		if (getEnvVar("CI_NAME") === "codeship") {
113 | 			return COLORS_256;
114 | 		}
115 | 		return COLORS_2;
116 | 	}
117 | 
118 | 	if ("TEAMCITY_VERSION" in env) {
119 | 		return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.exec(
120 | 			getEnvVar("TEAMCITY_VERSION"),
121 | 		) !== null
122 | 			? COLORS_16
123 | 			: COLORS_2;
124 | 	}
125 | 
126 | 	switch (getEnvVar("TERM_PROGRAM")) {
127 | 		case "iTerm.app":
128 | 			if (
129 | 				!getEnvVar("TERM_PROGRAM_VERSION") ||
130 | 				/^[0-2]\./.exec(getEnvVar("TERM_PROGRAM_VERSION")) !== null
131 | 			) {
132 | 				return COLORS_256;
133 | 			}
134 | 			return COLORS_16m;
135 | 		case "HyperTerm":
136 | 		case "MacTerm":
137 | 			return COLORS_16m;
138 | 		case "Apple_Terminal":
139 | 			return COLORS_256;
140 | 	}
141 | 
142 | 	if (
143 | 		getEnvVar("COLORTERM") === "truecolor" ||
144 | 		getEnvVar("COLORTERM") === "24bit"
145 | 	) {
146 | 		return COLORS_16m;
147 | 	}
148 | 
149 | 	if (getEnvVar("TERM")) {
150 | 		if (/truecolor/.exec(getEnvVar("TERM")) !== null) {
151 | 			return COLORS_16m;
152 | 		}
153 | 
154 | 		if (/^xterm-256/.exec(getEnvVar("TERM")) !== null) {
155 | 			return COLORS_256;
156 | 		}
157 | 
158 | 		const termEnv = getEnvVar("TERM").toLowerCase();
159 | 
160 | 		if (TERM_ENVS[termEnv]) {
161 | 			return TERM_ENVS[termEnv];
162 | 		}
163 | 		if (TERM_ENVS_REG_EXP.some((term) => term.exec(termEnv) !== null)) {
164 | 			return COLORS_16;
165 | 		}
166 | 	}
167 | 	// Move 16 color COLORTERM below 16m and 256
168 | 	if (getEnvVar("COLORTERM")) {
169 | 		return COLORS_16;
170 | 	}
171 | 	return COLORS_2;
172 | }
173 | 
```

--------------------------------------------------------------------------------
/docs/content/blogs/authjs-joins-better-auth.mdx:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: Auth.js is now part of Better Auth
 3 | description: "Auth.js, formerly known as NextAuth.js, is now being maintained and overseen by Better Auth team"
 4 | date: 2025-09-22
 5 | author:
 6 |   name: "Bereket Engida"
 7 |   avatar: "/avatars/beka.jpg"
 8 |   twitter: "imbereket"
 9 | image: "/blogs/authjs-joins.png"
10 | tags: ["seed round", "authentication", "funding"]
11 | ---
12 | 
13 | We’re excited to announce that [Auth.js](https://authjs.dev), formerly known as NextAuth.js, is now being maintained and overseen by Better Auth team. If you haven't heard of Auth.js, it has long been one of the most widely used open source authentication libraries in the JavaScript ecosystem. Chances are, if you’ve used [ChatGPT](https://chatgpt.com), [Google Labs](https://labs.google), [Cal.com](https://cal.com) or a million other websites, you’ve already interacted with Auth.js.  
14 | 
15 | ## Back Story about Better Auth and Auth.js
16 | 
17 | Before Better Auth, Auth.js gave developers like us the ability to own our auth without spending months wrestling with OAuth integrations or session management. But as applications became more complex and authentication needs evolved, some of its limitations became harder to ignore. We found ourselves rebuilding the same primitives over and over.
18 | 
19 | The Auth.js team recognized these challenges and had big ideas for the future, but for various reasons couldn’t execute them as fully as they hoped.
20 | 
21 | That shared frustration and the vision of empowering everyone to truly own their auth started the creation of Better Auth. Since our goals aligned with the Auth.js team, we were excited to help maintain Auth.js and make auth better across the web. As we talked more, we realized that Better Auth was the best home for Auth.js.
22 | 
23 | ## What does this mean for existing users?
24 | 
25 | We recognize how important this project is for countless applications, companies, and developers. If you’re using Auth.js/NextAuth.js today, you can continue doing so without disruption—we’ll keep addressing security patches and urgent issues as they come up.
26 | 
27 | But we strongly recommend new projects to start with Better Auth unless there are some very specific feature gaps (most notably stateless session management without a database). Our roadmap includes bringing those capabilities into Better Auth, so the ecosystem can converge rather than fragment.
28 | 
29 | <Callout>
30 | For teams considering migration, we’ve prepared a [guide](/docs/guides/next-auth-migration-guide) and we’ll be adding more guides and documentation soon.
31 | </Callout>
32 | 
33 | ## Final Thoughts
34 | 
35 | We are deeply grateful to the Auth.js community who have carried the project to this point. In particular, the core maintainers-[Balázs](https://x.com/balazsorban44), who served as lead maintainer, [Thang Vu](https://x.com/thanghvu),[Nico Domino](https://ndo.dev), Lluis Agusti and [Falco Winkler](https://github.com/falcowinkler)-pushed through difficult phases, brought in new primitives, and kept the project alive long enough for this transition to even be possible.
36 | 
37 | Better Auth beginning was inspired by Auth.js, and now, together, the two projects can carry the ecosystem further. The end goal remains unchanged: you should own your auth!
38 | 
39 | 
40 | <Callout type="none">
41 | For the Auth.js team's announcement, see [GitHub discussion](https://github.com/nextauthjs/next-auth/discussions/13252).
42 | </Callout>
43 | 
44 | 
45 | ### Learn More
46 | 
47 | <Cards>
48 |   <Card
49 |     href="https://www.better-auth.com/docs/introduction"
50 |     title="Better Auth Setup"
51 |   >
52 |     Get started with installing Better Auth
53 |   </Card>
54 |     <Card
55 |     href="https://www.better-auth.com/docs/comparison"
56 |     title="Comparison"
57 |   >
58 |     Comparison between Better Auth and other options
59 |   </Card>
60 |   <Card
61 |     href="https://www.better-auth.com/docs/guides/next-auth-migration-guide"
62 |     title="NextAuth Migration Guide"
63 |   >
64 |     Migrate from NextAuth to Better Auth
65 |   </Card>
66 |   <Card
67 |     href="https://www.better-auth.com/docs/guides/clerk-migration-guide"
68 |     title="Clerk Migration Guide"
69 |   >
70 |     Migrate from Clerk to Better Auth
71 |   </Card>
72 | </Cards>
73 | 
```

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

```typescript
  1 | "use client";
  2 | 
  3 | import * as React from "react";
  4 | import * as LabelPrimitive from "@radix-ui/react-label";
  5 | import { Slot } from "@radix-ui/react-slot";
  6 | import {
  7 | 	Controller,
  8 | 	ControllerProps,
  9 | 	FieldPath,
 10 | 	FieldValues,
 11 | 	FormProvider,
 12 | 	useFormContext,
 13 | } from "react-hook-form";
 14 | 
 15 | import { cn } from "@/lib/utils";
 16 | import { Label } from "@/components/ui/label";
 17 | 
 18 | const Form = FormProvider;
 19 | 
 20 | type FormFieldContextValue<
 21 | 	TFieldValues extends FieldValues = FieldValues,
 22 | 	TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
 23 | > = {
 24 | 	name: TName;
 25 | };
 26 | 
 27 | const FormFieldContext = React.createContext<FormFieldContextValue>(
 28 | 	{} as FormFieldContextValue,
 29 | );
 30 | 
 31 | const FormField = <
 32 | 	TFieldValues extends FieldValues = FieldValues,
 33 | 	TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
 34 | >({
 35 | 	...props
 36 | }: ControllerProps<TFieldValues, TName>) => {
 37 | 	return (
 38 | 		<FormFieldContext.Provider value={{ name: props.name }}>
 39 | 			<Controller {...props} />
 40 | 		</FormFieldContext.Provider>
 41 | 	);
 42 | };
 43 | 
 44 | const useFormField = () => {
 45 | 	const fieldContext = React.useContext(FormFieldContext);
 46 | 	const itemContext = React.useContext(FormItemContext);
 47 | 	const { getFieldState, formState } = useFormContext();
 48 | 
 49 | 	const fieldState = getFieldState(fieldContext.name, formState);
 50 | 
 51 | 	if (!fieldContext) {
 52 | 		throw new Error("useFormField should be used within <FormField>");
 53 | 	}
 54 | 
 55 | 	const { id } = itemContext;
 56 | 
 57 | 	return {
 58 | 		id,
 59 | 		name: fieldContext.name,
 60 | 		formItemId: `${id}-form-item`,
 61 | 		formDescriptionId: `${id}-form-item-description`,
 62 | 		formMessageId: `${id}-form-item-message`,
 63 | 		...fieldState,
 64 | 	};
 65 | };
 66 | 
 67 | type FormItemContextValue = {
 68 | 	id: string;
 69 | };
 70 | 
 71 | const FormItemContext = React.createContext<FormItemContextValue>(
 72 | 	{} as FormItemContextValue,
 73 | );
 74 | 
 75 | const FormItem = ({
 76 | 	ref,
 77 | 	className,
 78 | 	...props
 79 | }: React.HTMLAttributes<HTMLDivElement> & {
 80 | 	ref: React.RefObject<HTMLDivElement>;
 81 | }) => {
 82 | 	const id = React.useId();
 83 | 
 84 | 	return (
 85 | 		<FormItemContext.Provider value={{ id }}>
 86 | 			<div ref={ref} className={cn("space-y-2", className)} {...props} />
 87 | 		</FormItemContext.Provider>
 88 | 	);
 89 | };
 90 | FormItem.displayName = "FormItem";
 91 | 
 92 | const FormLabel = ({
 93 | 	ref,
 94 | 	className,
 95 | 	...props
 96 | }: React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & {
 97 | 	ref: React.RefObject<React.ElementRef<typeof LabelPrimitive.Root>>;
 98 | }) => {
 99 | 	const { error, formItemId } = useFormField();
100 | 
101 | 	return (
102 | 		<Label
103 | 			ref={ref}
104 | 			className={cn(error && "text-destructive", className)}
105 | 			htmlFor={formItemId}
106 | 			{...props}
107 | 		/>
108 | 	);
109 | };
110 | FormLabel.displayName = "FormLabel";
111 | 
112 | const FormControl = ({
113 | 	ref,
114 | 	...props
115 | }: React.ComponentPropsWithoutRef<typeof Slot> & {
116 | 	ref: React.RefObject<React.ElementRef<typeof Slot>>;
117 | }) => {
118 | 	const { error, formItemId, formDescriptionId, formMessageId } =
119 | 		useFormField();
120 | 
121 | 	return (
122 | 		<Slot
123 | 			ref={ref}
124 | 			id={formItemId}
125 | 			aria-describedby={
126 | 				!error
127 | 					? `${formDescriptionId}`
128 | 					: `${formDescriptionId} ${formMessageId}`
129 | 			}
130 | 			aria-invalid={!!error}
131 | 			{...props}
132 | 		/>
133 | 	);
134 | };
135 | FormControl.displayName = "FormControl";
136 | 
137 | const FormDescription = ({
138 | 	ref,
139 | 	className,
140 | 	...props
141 | }: React.HTMLAttributes<HTMLParagraphElement> & {
142 | 	ref: React.RefObject<HTMLParagraphElement>;
143 | }) => {
144 | 	const { formDescriptionId } = useFormField();
145 | 
146 | 	return (
147 | 		<p
148 | 			ref={ref}
149 | 			id={formDescriptionId}
150 | 			className={cn("text-[0.8rem] text-muted-foreground", className)}
151 | 			{...props}
152 | 		/>
153 | 	);
154 | };
155 | FormDescription.displayName = "FormDescription";
156 | 
157 | const FormMessage = ({
158 | 	ref,
159 | 	className,
160 | 	children,
161 | 	...props
162 | }: React.HTMLAttributes<HTMLParagraphElement> & {
163 | 	ref: React.RefObject<HTMLParagraphElement>;
164 | }) => {
165 | 	const { error, formMessageId } = useFormField();
166 | 	const body = error ? String(error?.message) : children;
167 | 
168 | 	if (!body) {
169 | 		return null;
170 | 	}
171 | 
172 | 	return (
173 | 		<p
174 | 			ref={ref}
175 | 			id={formMessageId}
176 | 			className={cn("text-[0.8rem] font-medium text-destructive", className)}
177 | 			{...props}
178 | 		>
179 | 			{body}
180 | 		</p>
181 | 	);
182 | };
183 | FormMessage.displayName = "FormMessage";
184 | 
185 | export {
186 | 	useFormField,
187 | 	Form,
188 | 	FormItem,
189 | 	FormLabel,
190 | 	FormControl,
191 | 	FormDescription,
192 | 	FormMessage,
193 | 	FormField,
194 | };
195 | 
```

--------------------------------------------------------------------------------
/docs/app/blog/_components/blog-list.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import { formatBlogDate } from "@/lib/blog";
  2 | import Link from "next/link";
  3 | import { blogs } from "@/lib/source";
  4 | import { IconLink } from "./changelog-layout";
  5 | import { GitHubIcon, BookIcon, XIcon } from "./icons";
  6 | import { Glow } from "./default-changelog";
  7 | import { StarField } from "./stat-field";
  8 | import { DiscordLogoIcon } from "@radix-ui/react-icons";
  9 | import Image from "next/image";
 10 | 
 11 | export async function BlogPage() {
 12 | 	const posts = blogs.getPages().sort((a, b) => {
 13 | 		return new Date(b.data.date).getTime() - new Date(a.data.date).getTime();
 14 | 	});
 15 | 	return (
 16 | 		<div className="md:grid md:grid-cols-2 items-start">
 17 | 			<div className="bg-gradient-to-tr hidden md:block overflow-hidden px-12 py-24 md:py-0 -mt-[100px] md:h-dvh relative md:sticky top-0 from-transparent dark:via-stone-950/5 via-stone-100/30 to-stone-200/20 dark:to-transparent/10">
 18 | 				<StarField className="top-1/2 -translate-y-1/2 left-1/2 -translate-x-1/2" />
 19 | 				<Glow />
 20 | 
 21 | 				<div className="flex flex-col md:justify-center max-w-xl mx-auto h-full">
 22 | 					<h1 className="mt-14 font-sans font-semibold tracking-tighter text-5xl">
 23 | 						Blogs
 24 | 					</h1>
 25 | 
 26 | 					<p className="text-sm text-gray-600 dark:text-gray-300">
 27 | 						Latest updates, articles, and insights about Better Auth
 28 | 					</p>
 29 | 					<hr className="h-px bg-gray-300 mt-5" />
 30 | 					<div className="mt-8 flex flex-wrap text-gray-600 dark:text-gray-300 gap-x-1 gap-y-3 sm:gap-x-2">
 31 | 						<IconLink
 32 | 							href="/docs"
 33 | 							icon={BookIcon}
 34 | 							className="flex-none text-gray-600 dark:text-gray-300"
 35 | 						>
 36 | 							Documentation
 37 | 						</IconLink>
 38 | 						<IconLink
 39 | 							href="https://github.com/better-auth/better-auth"
 40 | 							icon={GitHubIcon}
 41 | 							className="flex-none text-gray-600 dark:text-gray-300"
 42 | 						>
 43 | 							GitHub
 44 | 						</IconLink>
 45 | 						<IconLink
 46 | 							href="https://discord.gg/better-auth"
 47 | 							icon={DiscordLogoIcon}
 48 | 							className="flex-none text-gray-600 dark:text-gray-300"
 49 | 						>
 50 | 							Community
 51 | 						</IconLink>
 52 | 					</div>
 53 | 					<p className="flex items-baseline absolute bottom-4 max-md:left-1/2 max-md:-translate-x-1/2 gap-x-2 text-[0.8125rem]/6 text-gray-500">
 54 | 						<IconLink href="https://x.com/better_auth" icon={XIcon} compact>
 55 | 							BETTER-AUTH.
 56 | 						</IconLink>
 57 | 					</p>
 58 | 				</div>
 59 | 			</div>
 60 | 			<div className="py-6 lg:py-10 px-3">
 61 | 				<div className="flex flex-col gap-2">
 62 | 					{posts.map((post) => (
 63 | 						<div
 64 | 							className="group/blog flex flex-col gap-3 transition-colors p-4"
 65 | 							key={post.slugs.join("/")}
 66 | 						>
 67 | 							<article className="group relative flex flex-col space-y-2 flex-3/4 py-1">
 68 | 								<div className="flex gap-2">
 69 | 									<div className="flex flex-col gap-2  border-b border-dashed pb-2">
 70 | 										<p className="text-xs opacity-50">
 71 | 											{formatBlogDate(post.data.date)}
 72 | 										</p>
 73 | 										<h2 className="text-2xl font-bold">{post.data?.title}</h2>
 74 | 									</div>
 75 | 								</div>
 76 | 								{post.data?.image && (
 77 | 									<Image
 78 | 										src={post.data.image}
 79 | 										alt={post.data.title}
 80 | 										width={1206}
 81 | 										height={756}
 82 | 										className="rounded-md w-full bg-muted transition-colors"
 83 | 									/>
 84 | 								)}
 85 | 								<div className="flex gap-2">
 86 | 									<div className="flex flex-col gap-2  border-b border-dashed pb-2">
 87 | 										<p className="text-muted-foreground">
 88 | 											{post.data?.description.substring(0, 100)}...
 89 | 										</p>
 90 | 									</div>
 91 | 								</div>
 92 | 								<p className="text-xs opacity-50">
 93 | 									{post.data.structuredData.contents[0].content.substring(
 94 | 										0,
 95 | 										250,
 96 | 									)}
 97 | 									...
 98 | 								</p>
 99 | 								<Link href={`/blog/${post.slugs.join("/")}`}>
100 | 									<p className="text-xs group-hover/blog:underline underline-offset-4 transition-all">
101 | 										Read More
102 | 									</p>
103 | 								</Link>
104 | 								<Link
105 | 									href={`/blog/${post.slugs.join("/")}`}
106 | 									className="absolute inset-0"
107 | 								>
108 | 									<span className="sr-only">View Article</span>
109 | 								</Link>
110 | 							</article>
111 | 						</div>
112 | 					))}
113 | 				</div>
114 | 			</div>
115 | 		</div>
116 | 	);
117 | }
118 | 
```

--------------------------------------------------------------------------------
/docs/content/docs/plugins/anonymous.mdx:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: Anonymous  
  3 | description: Anonymous plugin for Better Auth.
  4 | ---
  5 | 
  6 | The Anonymous plugin allows users to have an authenticated experience without requiring them to provide an email address, password, OAuth provider, or any other Personally Identifiable Information (PII). Users can later link an authentication method to their account when ready.
  7 | 
  8 | ## Installation
  9 | 
 10 | <Steps>
 11 |   <Step>
 12 |     ### Add the plugin to your auth config
 13 | 
 14 |     To enable anonymous authentication, add the anonymous plugin to your authentication configuration.
 15 | 
 16 |     ```ts title="auth.ts"
 17 |     import { betterAuth } from "better-auth"
 18 |     import { anonymous } from "better-auth/plugins" // [!code highlight]
 19 |     
 20 |     export const auth = betterAuth({
 21 |         // ... other config options
 22 |         plugins: [
 23 |             anonymous() // [!code highlight]
 24 |         ]
 25 |     })
 26 |     ```
 27 |   </Step>
 28 | 
 29 |     <Step>
 30 |         ### Migrate the database
 31 | 
 32 |         Run the migration or generate the schema to add the necessary fields and tables to the database.
 33 | 
 34 |         <Tabs items={["migrate", "generate"]}>
 35 |             <Tab value="migrate">
 36 |             ```bash
 37 |             npx @better-auth/cli migrate
 38 |             ```
 39 |             </Tab>
 40 |             <Tab value="generate">
 41 |             ```bash
 42 |             npx @better-auth/cli generate
 43 |             ```
 44 |             </Tab>
 45 |         </Tabs>
 46 |         See the [Schema](#schema) section to add the fields manually.
 47 |     </Step>
 48 | 
 49 |   <Step>
 50 |     ### Add the client plugin
 51 | 
 52 |     Next, include the anonymous client plugin in your authentication client instance.
 53 | 
 54 |     ```ts title="auth-client.ts"
 55 |     import { createAuthClient } from "better-auth/client"
 56 |     import { anonymousClient } from "better-auth/client/plugins"
 57 |     
 58 |     export const authClient = createAuthClient({
 59 |         plugins: [
 60 |             anonymousClient()
 61 |         ]
 62 |     })
 63 |     ```
 64 |   </Step>
 65 | </Steps>
 66 | 
 67 | ## Usage
 68 | 
 69 | ### Sign In
 70 | 
 71 | To sign in a user anonymously, use the `signIn.anonymous()` method.
 72 | 
 73 | ```ts title="example.ts"
 74 | const user = await authClient.signIn.anonymous()
 75 | ```
 76 | 
 77 | ### Link Account
 78 | 
 79 | If a user is already signed in anonymously and tries to `signIn` or `signUp` with another method, their anonymous activities can be linked to the new account.
 80 | 
 81 | To do that you first need to provide `onLinkAccount` callback to the plugin.
 82 | 
 83 | ```ts title="auth.ts"
 84 | import { betterAuth } from "better-auth"
 85 | 
 86 | export const auth = betterAuth({
 87 |     plugins: [
 88 |         anonymous({
 89 |             onLinkAccount: async ({ anonymousUser, newUser }) => {
 90 |                // perform actions like moving the cart items from anonymous user to the new user
 91 |             }
 92 |         })
 93 |     ]
 94 | ```
 95 | 
 96 | Then when you call `signIn` or `signUp` with another method, the `onLinkAccount` callback will be called. And the `anonymousUser` will be deleted by default.
 97 | 
 98 | ```ts title="example.ts"
 99 | const user = await authClient.signIn.email({
100 |     email,
101 | })
102 | ```
103 | 
104 | ## Options
105 | 
106 | - `emailDomainName`: The domain name to use when generating an email address for anonymous users. Defaults to the domain name of the current site.
107 | 
108 | ```ts title="auth.ts"
109 | import { betterAuth } from "better-auth"
110 | 
111 | export const auth = betterAuth({
112 |     plugins: [
113 |         anonymous({
114 |             emailDomainName: "example.com"
115 |         })
116 |     ]
117 | })
118 | ```
119 | 
120 | - `onLinkAccount`: A callback function that is called when an anonymous user links their account to a new authentication method. The callback receives an object with the `anonymousUser` and the `newUser`.
121 | 
122 | - `disableDeleteAnonymousUser`: By default, the anonymous user is deleted when the account is linked to a new authentication method. Set this option to `true` to disable this behavior.
123 | 
124 | - `generateName`: A callback function that is called to generate a name for the anonymous user. Useful if you want to have random names for anonymous users, or if `name` is unique in your database.
125 | 
126 | ## Schema
127 | 
128 | The anonymous plugin requires an additional field in the user table:
129 | 
130 | <DatabaseTable
131 |     fields={[
132 |         { name: "isAnonymous", type: "boolean", description: "Indicates whether the user is anonymous.", isOptional: true },
133 |     ]}
134 | />
135 | 
```

--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------

```yaml
  1 | name: CI
  2 | 
  3 | on:
  4 |   pull_request:
  5 |     types:
  6 |       - opened
  7 |       - synchronize
  8 |   push:
  9 |     branches:
 10 |       - main
 11 |       - canary
 12 |   merge_group:
 13 | 
 14 | jobs:
 15 |   lint:
 16 |     runs-on: ubuntu-latest
 17 |     steps:
 18 |       - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
 19 |         with:
 20 |           fetch-depth: 0
 21 | 
 22 |       - name: Cache turbo build setup
 23 |         uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
 24 |         with:
 25 |           path: .turbo
 26 |           key: ${{ runner.os }}-turbo-${{ github.sha }}
 27 |           restore-keys: |
 28 |             ${{ runner.os }}-turbo-
 29 | 
 30 |       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
 31 | 
 32 |       - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
 33 |         with:
 34 |           node-version: 22.x
 35 |           registry-url: 'https://registry.npmjs.org'
 36 |           cache: pnpm
 37 | 
 38 |       - name: Install
 39 |         run: pnpm install
 40 | 
 41 |       - name: Build
 42 |         env:
 43 |           TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
 44 |           TURBO_TEAM: ${{ vars.TURBO_TEAM || github.repository_owner }}
 45 |           TURBO_CACHE: remote:rw
 46 |         run: pnpm build
 47 | 
 48 |       - name: Lint
 49 |         env:
 50 |           TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
 51 |           TURBO_TEAM: ${{ vars.TURBO_TEAM || github.repository_owner }}
 52 |           TURBO_CACHE: remote:rw
 53 |         run: pnpm lint
 54 | 
 55 |   typecheck:
 56 |     runs-on: ubuntu-latest
 57 |     steps:
 58 |       - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
 59 |         with:
 60 |           fetch-depth: 0
 61 | 
 62 |       - name: Cache turbo build setup
 63 |         uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
 64 |         with:
 65 |           path: .turbo
 66 |           key: ${{ runner.os }}-turbo-${{ github.sha }}
 67 |           restore-keys: |
 68 |             ${{ runner.os }}-turbo-
 69 | 
 70 |       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
 71 | 
 72 |       - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
 73 |         with:
 74 |           node-version: 22.x
 75 |           registry-url: 'https://registry.npmjs.org'
 76 |           cache: pnpm
 77 | 
 78 |       - name: Install
 79 |         run: pnpm install
 80 | 
 81 |       - name: Build
 82 |         env:
 83 |           TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
 84 |           TURBO_TEAM: ${{ vars.TURBO_TEAM || github.repository_owner }}
 85 |           TURBO_CACHE: remote:rw
 86 |         run: pnpm build
 87 | 
 88 |       - name: Typecheck
 89 |         env:
 90 |           TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
 91 |           TURBO_TEAM: ${{ vars.TURBO_TEAM || github.repository_owner }}
 92 |           TURBO_CACHE: remote:rw
 93 |         run: pnpm typecheck
 94 | 
 95 |   test:
 96 |     runs-on: ubuntu-latest
 97 |     strategy:
 98 |       fail-fast: false
 99 |       matrix:
100 |         node-version: [ 22.x, 24.x, 25.x ]
101 |     steps:
102 |       - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
103 |         with:
104 |           fetch-depth: 0
105 | 
106 |       - name: Cache turbo build setup
107 |         uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
108 |         with:
109 |           path: .turbo
110 |           key: ${{ runner.os }}-turbo-${{ github.sha }}
111 |           restore-keys: |
112 |             ${{ runner.os }}-turbo-
113 | 
114 |       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
115 | 
116 |       - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
117 |         with:
118 |           node-version: ${{ matrix.node-version }}
119 |           registry-url: 'https://registry.npmjs.org'
120 |           cache: pnpm
121 | 
122 |       - name: Install
123 |         run: pnpm install
124 | 
125 |       - name: Build
126 |         env:
127 |           TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
128 |           TURBO_TEAM: ${{ vars.TURBO_TEAM || github.repository_owner }}
129 |           TURBO_CACHE: remote:rw
130 |         run: pnpm build
131 | 
132 |       - name: Start Docker Containers
133 |         run: |
134 |           docker compose up -d
135 |           # Wait for services to be ready (optional)
136 |           sleep 10
137 | 
138 |       - name: Test
139 |         env:
140 |           TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
141 |           TURBO_TEAM: ${{ vars.TURBO_TEAM || github.repository_owner }}
142 |         run: pnpm test
143 | 
144 |       - name: Stop Docker Containers
145 |         run: docker compose down
146 |             
```
Page 13/69FirstPrevNextLast