#
tokens: 48646/50000 7/1120 files (page 29/52)
lines: off (toggle) GitHub
raw markdown copy
This is page 29 of 52. Use http://codebase.md/better-auth/better-auth?lines=false&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
│   └── stateless
│       ├── .env.example
│       ├── .gitignore
│       ├── next.config.ts
│       ├── package.json
│       ├── postcss.config.mjs
│       ├── src
│       │   ├── app
│       │   │   ├── api
│       │   │   │   ├── auth
│       │   │   │   │   └── [...all]
│       │   │   │   │       └── route.ts
│       │   │   │   └── user
│       │   │   │       └── route.ts
│       │   │   ├── dashboard
│       │   │   │   └── page.tsx
│       │   │   ├── globals.css
│       │   │   ├── layout.tsx
│       │   │   └── page.tsx
│       │   └── lib
│       │       ├── auth-client.ts
│       │       └── auth.ts
│       ├── tailwind.config.ts
│       └── tsconfig.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
│   │       │   ├── polar.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-declaration
│       │   │   │   ├── package.json
│       │   │   │   ├── src
│       │   │   │   │   ├── demo.ts
│       │   │   │   │   ├── index.ts
│       │   │   │   │   └── username.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-custom-schema.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-schema.test.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
│   │   └── 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
│   │   │   │   ├── polar.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
│   │   │   └── index.ts
│   │   ├── test
│   │   │   └── expo.test.ts
│   │   ├── tsconfig.json
│   │   ├── tsconfig.test.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.base.json
├── tsconfig.json
└── turbo.json
```

# Files

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

```typescript
import { describe, expect, it } from "vitest";
import { getTestInstance } from "../../test-utils/test-instance";
import { createAuthClient } from "../../client";
import { jwtClient } from "./client";
import { jwt } from ".";
import { createLocalJWKSet, jwtVerify, type JSONWebKeySet } from "jose";
import type { JwtOptions, JWKOptions } from "./types";
import { generateExportedKeyPair } from "./utils";

describe("jwt", async () => {
	// Testing the default behaviour
	const { auth, signInWithTestUser } = await getTestInstance({
		plugins: [jwt()],
		logger: {
			level: "error",
		},
	});

	const { headers } = await signInWithTestUser();
	const client = createAuthClient({
		plugins: [jwtClient()],
		baseURL: "http://localhost:3000/api/auth",
		fetchOptions: {
			customFetchImpl: async (url, init) => {
				return auth.handler(new Request(url, init));
			},
		},
	});

	it("Client gets a token from session", async () => {
		let token = "";
		await client.getSession({
			fetchOptions: {
				headers,
				onSuccess(context) {
					token = context.response.headers.get("set-auth-jwt") || "";
				},
			},
		});

		expect(token.length).toBeGreaterThan(10);
	});

	it("Client gets a token", async () => {
		const token = await client.token({
			fetchOptions: {
				headers,
			},
		});

		expect(token.data?.token).toBeDefined();
	});

	it("Get JWKS", async () => {
		// If no JWK exists, this makes sure it gets added.
		// TODO: Replace this with a generate JWKS endpoint once it exists.
		const token = await client.token({
			fetchOptions: {
				headers,
			},
		});

		expect(token.data?.token).toBeDefined();

		const jwks = await client.jwks();

		expect(jwks.data?.keys).length.above(0);
		expect(jwks.data?.keys[0]!.alg).toBe("EdDSA");
	});

	it("Signed tokens can be validated with the JWKS", async () => {
		const token = await client.token({
			fetchOptions: {
				headers,
			},
		});

		const jwks = await client.jwks();

		const localJwks = createLocalJWKSet(jwks.data!);
		const decoded = await jwtVerify(token.data?.token!, localJwks);

		expect(decoded).toBeDefined();
	});

	it("should set subject to user id by default", async () => {
		const token = await client.token({
			fetchOptions: {
				headers,
			},
		});

		const jwks = await client.jwks();

		const localJwks = createLocalJWKSet(jwks.data!);
		const decoded = await jwtVerify(token.data?.token!, localJwks);
		expect(decoded.payload.sub).toBeDefined();
		expect(decoded.payload.sub).toBe(decoded.payload.id);
	});

	const algorithmsToTest: {
		keyPairConfig: JWKOptions;
		expectedOutcome: { ec: string; length: number; crv?: string; alg: string };
	}[] = [
		{
			keyPairConfig: {
				alg: "EdDSA",
				crv: "Ed25519",
			},
			expectedOutcome: {
				ec: "OKP",
				length: 43,
				crv: "Ed25519",
				alg: "EdDSA",
			},
		},
		{
			keyPairConfig: {
				alg: "ES256",
			},
			expectedOutcome: {
				ec: "EC",
				length: 43,
				crv: "P-256",
				alg: "ES256",
			},
		},
		{
			keyPairConfig: {
				alg: "ES512",
			},
			expectedOutcome: {
				ec: "EC",
				length: 88,
				crv: "P-521",
				alg: "ES512",
			},
		},
		{
			keyPairConfig: {
				alg: "PS256",
			},
			expectedOutcome: {
				ec: "RSA",
				length: 342,
				alg: "PS256",
			},
		},
		{
			keyPairConfig: {
				alg: "RS256",
			},
			expectedOutcome: {
				ec: "RSA",
				length: 342,
				alg: "RS256",
			},
		},
	];

	for (const algorithm of algorithmsToTest) {
		const expectedOutcome = algorithm.expectedOutcome;
		for (let disablePrivateKeyEncryption of [false, true]) {
			const jwtOptions: JwtOptions = {
				jwks: {
					keyPairConfig: {
						...algorithm.keyPairConfig,
					},
					disablePrivateKeyEncryption: disablePrivateKeyEncryption,
				},
			};
			try {
				const { auth, signInWithTestUser } = await getTestInstance({
					plugins: [jwt(jwtOptions)],
					logger: {
						level: "error",
					},
				});

				const alg: string =
					algorithm.keyPairConfig.alg +
					("crv" in algorithm.keyPairConfig
						? `(${algorithm.keyPairConfig.crv})`
						: "");
				const enc: string = disablePrivateKeyEncryption
					? " without private key encryption"
					: "";

				it(`${alg} algorithm${enc} can be used to generate JWKS`, async () => {
					// Unit test (JWS Supported key)
					const { publicWebKey, privateWebKey } =
						await generateExportedKeyPair(jwtOptions);
					for (const key of [publicWebKey, privateWebKey]) {
						expect(key.kty).toBe(expectedOutcome.ec);
						if (key.x) expect(key.x).toHaveLength(expectedOutcome.length);
						if (key.y) expect(key.y).toHaveLength(expectedOutcome.length);
						if (key.n) expect(key.n).toHaveLength(expectedOutcome.length);
					}

					// Functional test (JWKS)
					const jwks = await auth.api.getJwks();
					expect(jwks.keys.at(0)?.kty).toBe(expectedOutcome.ec);
					if (jwks.keys.at(0)?.crv)
						expect(jwks.keys.at(0)?.crv).toBe(expectedOutcome.crv);
					expect(jwks.keys.at(0)?.alg).toBe(expectedOutcome.alg);
					if (jwks.keys.at(0)?.x)
						expect(jwks.keys.at(0)?.x).toHaveLength(expectedOutcome.length);
					if (jwks.keys.at(0)?.y)
						expect(jwks.keys.at(0)?.y).toHaveLength(expectedOutcome.length);
					if (jwks.keys.at(0)?.n)
						expect(jwks?.keys.at(0)?.n).toHaveLength(expectedOutcome.length);
				});

				const client = createAuthClient({
					plugins: [jwtClient()],
					baseURL: "http://localhost:3000/api/auth",
					fetchOptions: {
						customFetchImpl: async (url, init) => {
							return auth.handler(new Request(url, init));
						},
					},
				});
				let headers: Headers | undefined = undefined;

				it(`${alg} algorithm${enc}: Client can sign in`, async () => {
					try {
						const { headers: heads } = await signInWithTestUser();
						headers = heads;
						expect(headers).toBeDefined();
					} catch (err) {
						console.error(err);
						expect.unreachable();
					}
				});

				it(`${alg} algorithm${enc}: Client gets a token`, async () => {
					const token = await client.token({
						fetchOptions: {
							headers,
						},
					});

					expect(token.data?.token).toBeDefined();
				});

				it(`${alg} algorithm${enc}: Client gets a token from session`, async () => {
					let token = "";
					await client.getSession({
						fetchOptions: {
							headers,
							onSuccess(context) {
								token = context.response.headers.get("set-auth-jwt") || "";
							},
						},
					});

					expect(token.length).toBeGreaterThan(10);
				});

				it(`${alg} algorithm${enc}: Signed tokens can be validated with the JWKS`, async () => {
					const token = await client.token({
						fetchOptions: {
							headers,
						},
					});

					const jwks = await client.jwks();

					const localJwks = createLocalJWKSet(jwks.data!);
					const decoded = await jwtVerify(token.data?.token!, localJwks);

					expect(decoded).toBeDefined();
				});

				it(`${alg} algorithm${enc}: Should set subject to user id by default`, async () => {
					const token = await client.token({
						fetchOptions: {
							headers,
						},
					});

					const jwks = await client.jwks();

					const localJwks = createLocalJWKSet(jwks.data!);
					const decoded = await jwtVerify(token.data?.token!, localJwks);
					expect(decoded.payload.sub).toBeDefined();
					expect(decoded.payload.sub).toBe(decoded.payload.id);
				});
			} catch (err) {
				console.error(err);
				expect.unreachable();
			}
		}
	}
});

describe.each([
	{
		alg: "EdDSA",
		crv: "Ed25519",
	},
	{
		alg: "ES256",
	},
	{
		alg: "ES512",
	},
	{
		alg: "PS256",
	},
	{
		alg: "RS256",
	},
] as JWKOptions[])("signJWT - alg: $alg", async (keyPairConfig) => {
	const { auth } = await getTestInstance({
		plugins: [
			jwt({
				jwks: {
					keyPairConfig,
				},
			}),
		],
		logger: {
			level: "error",
		},
	});

	it("should sign a JWT", async () => {
		const jwt = await auth.api.signJWT({
			body: {
				payload: {
					sub: "123",
					exp: 1000,
					iat: 1000,
					iss: "https://example.com",
					aud: "https://example.com",
					custom: "custom",
				},
			},
		});
		expect(jwt?.token).toBeDefined();
	});

	it("should be a valid JWT", async () => {
		const jwt = await auth.api.signJWT({
			body: {
				payload: {
					sub: "123",
					exp: Math.floor(Date.now() / 1000) + 600,
					iat: Math.floor(Date.now() / 1000),
					iss: "https://example.com",
					aud: "https://example.com",
					custom: "custom",
				},
			},
		});
		const jwks = await auth.api.getJwks();
		const localJwks = createLocalJWKSet(jwks);
		const decoded = await jwtVerify(jwt?.token!, localJwks);
		expect(decoded).toMatchObject({
			payload: {
				iss: "https://example.com",
				aud: "https://example.com",
				sub: "123",
				exp: expect.any(Number),
				iat: expect.any(Number),
				custom: "custom",
			},
			protectedHeader: {
				alg: keyPairConfig.alg,
				kid: jwks.keys[0]!.kid,
			},
		});
	});

	it("shouldn't let you sign from a client", async () => {
		const client = createAuthClient({
			plugins: [jwtClient()],
			baseURL: "http://localhost:3000/api/auth",
			fetchOptions: {
				customFetchImpl: async (url, init) => {
					return auth.handler(new Request(url, init));
				},
			},
		});
		const jwt = await client.$fetch("/sign-jwt", {
			method: "POST",
			body: {
				payload: { sub: "123" },
			},
		});
		expect(jwt.error?.status).toBe(404);
	});
});

describe("jwt - remote signing", async () => {
	it("should fail if sign is defined and remoteUrl is not", async () => {
		expect(() =>
			getTestInstance({
				plugins: [
					jwt({
						jwt: {
							sign: () => {
								return "123";
							},
						},
					}),
				],
			}),
		).toThrowError("jwks_config");
	});
});

describe("jwt - remote url", async () => {
	it("should require specifying the alg when using remoteUrl", async () => {
		expect(() =>
			getTestInstance({
				plugins: [
					jwt({
						jwks: {
							remoteUrl: "https://example.com/.well-known/jwks.json",
						},
					}),
				],
			}),
		).toThrowError("jwks_config");
	});

	it("should accept remoteUrl with alg specified", async () => {
		const { auth } = await getTestInstance({
			plugins: [
				jwt({
					jwks: {
						remoteUrl: "https://example.com/.well-known/jwks.json",
						keyPairConfig: {
							alg: "ES256",
						},
					},
				}),
			],
		});
		expect(auth).toBeDefined();
	});

	it("should disable /jwks endpoint when remoteUrl is configured", async () => {
		const { auth } = await getTestInstance({
			plugins: [
				jwt({
					jwks: {
						remoteUrl: "https://example.com/.well-known/jwks.json",
						keyPairConfig: {
							alg: "ES256",
						},
					},
				}),
			],
		});

		const client = createAuthClient({
			plugins: [jwtClient()],
			baseURL: "http://localhost:3000/api/auth",
			fetchOptions: {
				customFetchImpl: async (url, init) => {
					return auth.handler(new Request(url, init));
				},
			},
		});

		const response = await client.$fetch<JSONWebKeySet>("/jwks");
		expect(response.error?.status).toBe(404);
	});

	it("should work with different algorithms when remoteUrl is set", async () => {
		const algorithms = ["ES256", "ES512", "RS256", "PS256", "EdDSA"];

		for (const alg of algorithms) {
			const { auth } = await getTestInstance({
				plugins: [
					jwt({
						jwks: {
							remoteUrl: "https://example.com/.well-known/jwks.json",
							keyPairConfig: {
								alg: alg as any,
							},
						},
					}),
				],
			});
			expect(auth).toBeDefined();
		}
	});

	it("should still allow token generation when remoteUrl is set", async () => {
		const { auth, signInWithTestUser } = await getTestInstance({
			plugins: [
				jwt({
					jwks: {
						remoteUrl: "https://example.com/.well-known/jwks.json",
						keyPairConfig: {
							alg: "ES256",
						},
					},
				}),
			],
		});

		const { headers } = await signInWithTestUser();

		const client = createAuthClient({
			plugins: [jwtClient()],
			baseURL: "http://localhost:3000/api/auth",
			fetchOptions: {
				customFetchImpl: async (url, init) => {
					return auth.handler(new Request(url, init));
				},
			},
		});

		const token = await client.token({
			fetchOptions: {
				headers,
			},
		});

		expect(token.data?.token).toBeDefined();
		expect(token.data?.token).toMatch(/^[\w-]+\.[\w-]+\.[\w-]+$/); // JWT format
	});

	it("should work with custom sign function and remoteUrl", async () => {
		const mockSignFunction = (payload: any) => {
			// Mock JWT with test signature
			const header = Buffer.from(
				JSON.stringify({ alg: "ES256", typ: "JWT" }),
			).toString("base64url");
			const body = Buffer.from(JSON.stringify(payload)).toString("base64url");
			const signature = "mock-signature";
			return `${header}.${body}.${signature}`;
		};

		const { auth, signInWithTestUser } = await getTestInstance({
			plugins: [
				jwt({
					jwks: {
						remoteUrl: "https://example.com/.well-known/jwks.json",
						keyPairConfig: {
							alg: "ES256",
						},
					},
					jwt: {
						sign: mockSignFunction,
					},
				}),
			],
		});

		const { headers } = await signInWithTestUser();

		const client = createAuthClient({
			plugins: [jwtClient()],
			baseURL: "http://localhost:3000/api/auth",
			fetchOptions: {
				customFetchImpl: async (url, init) => {
					return auth.handler(new Request(url, init));
				},
			},
		});

		const token = await client.token({
			fetchOptions: {
				headers,
			},
		});

		expect(token.data?.token).toBeDefined();
		// Verify it's using our mock sign function
		expect(token.data?.token).toContain("mock-signature");
	});

	it("should validate that remoteUrl is a valid URL format", async () => {
		const invalidUrls = [
			"not-a-url",
			"http://",
			"//example.com",
			"example.com/jwks",
		];

		for (const url of invalidUrls) {
			// While the current implementation doesn't validate URL format,
			// this test documents expected behavior
			const { auth } = await getTestInstance({
				plugins: [
					jwt({
						jwks: {
							remoteUrl: url,
							keyPairConfig: {
								alg: "ES256",
							},
						},
					}),
				],
			});
			// Currently passes, but documents that URL validation might be needed
			expect(auth).toBeDefined();
		}
	});

	it("should work with remoteUrl pointing to different paths", async () => {
		const validPaths = [
			"https://example.com/.well-known/jwks.json",
			"https://auth.example.com/jwks",
			"https://api.example.com/v1/keys",
			"https://example.com:8080/jwks.json",
		];

		for (const url of validPaths) {
			const { auth } = await getTestInstance({
				plugins: [
					jwt({
						jwks: {
							remoteUrl: url,
							keyPairConfig: {
								alg: "ES256",
							},
						},
					}),
				],
			});
			expect(auth).toBeDefined();
		}
	});

	it("should handle remoteUrl with query parameters", async () => {
		const { auth } = await getTestInstance({
			plugins: [
				jwt({
					jwks: {
						remoteUrl: "https://example.com/jwks?version=1&format=json",
						keyPairConfig: {
							alg: "RS256",
						},
					},
				}),
			],
		});
		expect(auth).toBeDefined();
	});

	it("should not interfere with other JWT endpoints when remoteUrl is set", async () => {
		const { auth, signInWithTestUser } = await getTestInstance({
			plugins: [
				jwt({
					jwks: {
						remoteUrl: "https://example.com/.well-known/jwks.json",
						keyPairConfig: {
							alg: "ES256",
						},
					},
				}),
			],
		});

		const { headers } = await signInWithTestUser();

		const client = createAuthClient({
			plugins: [jwtClient()],
			baseURL: "http://localhost:3000/api/auth",
			fetchOptions: {
				customFetchImpl: async (url, init) => {
					return auth.handler(new Request(url, init));
				},
			},
		});

		// Test that /token endpoint still works
		const tokenResponse = await client.token({
			fetchOptions: {
				headers,
			},
		});
		expect(tokenResponse.data?.token).toBeDefined();

		// Test that /jwks endpoint returns 404
		const jwksResponse = await client.$fetch("/jwks");
		expect(jwksResponse.error?.status).toBe(404);

		// Test that session endpoint still returns JWT header
		let jwtHeader = "";
		await client.getSession({
			fetchOptions: {
				headers,
				onSuccess(context) {
					jwtHeader = context.response.headers.get("set-auth-jwt") || "";
				},
			},
		});
		expect(jwtHeader).toBeTruthy();
	});
});

```

--------------------------------------------------------------------------------
/docs/content/docs/plugins/2fa.mdx:
--------------------------------------------------------------------------------

```markdown
---
title: Two-Factor Authentication (2FA)
description: Enhance your app's security with two-factor authentication.
---

`OTP` `TOTP` `Backup Codes` `Trusted Devices`

Two-Factor Authentication (2FA) adds an extra security step when users log in. Instead of just using a password, they'll need to provide a second form of verification. This makes it much harder for unauthorized people to access accounts, even if they've somehow gotten the password.

This plugin offers two main methods to do a second factor verification:

1. **OTP (One-Time Password)**: A temporary code sent to the user's email or phone.
2. **TOTP (Time-based One-Time Password)**: A code generated by an app on the user's device.

**Additional features include:**
- Generating backup codes for account recovery
- Enabling/disabling 2FA
- Managing trusted devices

## Installation

<Steps>
    <Step>
        ### Add the plugin to your auth config

        Add the two-factor plugin to your auth configuration and specify your app name as the issuer.

        ```ts title="auth.ts"
        import { betterAuth } from "better-auth"
        import { twoFactor } from "better-auth/plugins" // [!code highlight]

        export const auth = betterAuth({
            // ... other config options
            appName: "My App", // provide your app name. It'll be used as an issuer. // [!code highlight]
            plugins: [
                twoFactor() // [!code highlight]
            ]
        })
        ```
    </Step>
      <Step>
        ### Migrate the database

        Run the migration or generate the schema to add the necessary fields and tables to the database.

        <Tabs items={["migrate", "generate"]}>
            <Tab value="migrate">
            ```bash
            npx @better-auth/cli migrate
            ```
            </Tab>
            <Tab value="generate">
            ```bash
            npx @better-auth/cli generate
            ```
            </Tab>
        </Tabs>
        See the [Schema](#schema) section to add the fields manually.
    </Step>

        <Step>
        ### Add the client plugin

        Add the client plugin and Specify where the user should be redirected if they need to verify 2nd factor

        ```ts title="auth-client.ts"
        import { createAuthClient } from "better-auth/client"
        import { twoFactorClient } from "better-auth/client/plugins"

        export const authClient = createAuthClient({
            plugins: [
                twoFactorClient()
            ]
        })
        ```
        </Step>
</Steps>

## Usage

### Enabling 2FA

To enable two-factor authentication, call `twoFactor.enable` with the user's password and issuer (optional):

<APIMethod
  path="/two-factor/enable"
  method="POST"
  requireSession
>
```ts
type enableTwoFactor = {
    /**
     * The user's password
     */
    password: string = "secure-password"
    /**
     * An optional custom issuer for the TOTP URI. Defaults to app-name defined in your auth config.
     */
    issuer?: string = "my-app-name"
}
```
</APIMethod>

When 2FA is enabled:
- An encrypted `secret` and `backupCodes` are generated.
- `enable` returns `totpURI` and `backupCodes`.

Note: `twoFactorEnabled` won’t be set to `true` until the user verifies their TOTP code. Learn more about veryifying TOTP [here](#totp). You can skip verification by setting `skipVerificationOnEnable` to true in your plugin config.

<Callout type="warn">
Two Factor can only be enabled for credential accounts at the moment. For social accounts, it's assumed the provider already handles 2FA.
</Callout>

### Sign In with 2FA

When a user with 2FA enabled tries to sign in via email, the response object will contain `twoFactorRedirect` set to `true`. This indicates that the user needs to verify their 2FA code.

You can handle this in the `onSuccess` callback or by providing a `onTwoFactorRedirect` callback in the plugin config.

```ts title="sign-in.tsx"
await authClient.signIn.email({
        email: "[email protected]",
        password: "password123",
    },
    {
        async onSuccess(context) {
            if (context.data.twoFactorRedirect) {
                // Handle the 2FA verification in place
            }
        },
    }
)
```

Using the `onTwoFactorRedirect` config:

```ts title="sign-in.ts"
import { createAuthClient } from "better-auth/client";
import { twoFactorClient } from "better-auth/client/plugins";

const authClient = createAuthClient({
    plugins: [
        twoFactorClient({
            onTwoFactorRedirect(){
                // Handle the 2FA verification globally
            },
        }),
    ],
});
```




<Callout type="warn">
**With `auth.api`**

When you call `auth.api.signInEmail` on the server, and the user has 2FA enabled, it will return an object where `twoFactorRedirect` is set to `true`. This behavior isn’t inferred in TypeScript, which can be misleading. You can check using `in` instead to check if `twoFactorRedirect` is set to `true`.

```ts
const response = await auth.api.signInEmail({
	body: {
		email: "[email protected]",
		password: "test",
	},
});

if ("twoFactorRedirect" in response) {
	// Handle the 2FA verification in place
}
```
</Callout>

### Disabling 2FA

To disable two-factor authentication, call `twoFactor.disable` with the user's password:

<APIMethod
  path="/two-factor/disable"
  method="POST"
  requireSession
>
```ts
type disableTwoFactor = {
    /**
     * The user's password
     */
    password: string
}
```
</APIMethod>

### TOTP

TOTP (Time-Based One-Time Password) is an algorithm that generates a unique password for each login attempt using time as a counter. Every fixed interval (Better Auth defaults to 30 seconds), a new password is generated. This addresses several issues with traditional passwords: they can be forgotten, stolen, or guessed. OTPs solve some of these problems, but their delivery via SMS or email can be unreliable (or even risky, considering it opens new attack vectors).

TOTP, however, generates codes offline, making it both secure and convenient. You just need an authenticator app on your phone.

#### Getting TOTP URI

After enabling 2FA, you can get the TOTP URI to display to the user. This URI is generated by the server using the `secret` and `issuer` and can be used to generate a QR code for the user to scan with their authenticator app.

<APIMethod
  path="/two-factor/get-totp-uri"
  method="POST"
  requireSession
>
```ts
type getTOTPURI = {
    /**
     * The user's password
     */
    password: string
}
```
</APIMethod>


**Example: Using React**

Once you have the TOTP URI, you can use it to generate a QR code for the user to scan with their authenticator app.

```tsx title="user-card.tsx"
import QRCode from "react-qr-code";

export default function UserCard({ password }: { password: string }){
    const { data: session } = client.useSession();
	const { data: qr } = useQuery({
		queryKey: ["two-factor-qr"],
		queryFn: async () => {
			const res = await authClient.twoFactor.getTotpUri({ password });
			return res.data;
		},
		enabled: !!session?.user.twoFactorEnabled,
	});
    return (
        <QRCode value={qr?.totpURI || ""} />
   )
}
```

<Callout>
By default the issuer for TOTP is set to the app name provided in the auth config or if not provided it will be set to `Better Auth`. You can override this by passing `issuer` to the plugin config.
</Callout>

#### Verifying TOTP

After the user has entered their 2FA code, you can verify it using `twoFactor.verifyTotp` method. `Better Auth` follows standard practice by accepting TOTP codes from one period before and one after the current code, ensuring users can authenticate even with minor time delays on their end.

<APIMethod path="/two-factor/verify-totp" method="POST">
```ts
type verifyTOTP = {
    /**
     * The otp code to verify. 
     */
    code: string = "012345"
    /**
     * If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. 
     */
    trustDevice?: boolean = true
}
```
</APIMethod>

### OTP

OTP (One-Time Password) is similar to TOTP but a random code is generated and sent to the user's email or phone.

Before using OTP to verify the second factor, you need to configure `sendOTP` in your Better Auth instance. This function is responsible for sending the OTP to the user's email, phone, or any other method supported by your application.

```ts title="auth.ts"
import { betterAuth } from "better-auth"
import { twoFactor } from "better-auth/plugins"

export const auth = betterAuth({
    plugins: [
        twoFactor({
          	otpOptions: {
				async sendOTP({ user, otp }, request) {
                    // send otp to user
				},
			},
        })
    ]
})
```

#### Sending OTP

Sending an OTP is done by calling the `twoFactor.sendOtp` function. This function will trigger your sendOTP implementation that you provided in the Better Auth configuration.

<APIMethod path="/two-factor/send-otp" method="POST">
```ts
type send2FaOTP = {
    /**
     * If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. 
     */
    trustDevice?: boolean = true
}

if (data) {
    // redirect or show the user to enter the code
}
```
</APIMethod>

#### Verifying OTP

After the user has entered their OTP code, you can verify it

<APIMethod path="/two-factor/verify-otp" method="POST">
```ts
type verifyOTP = {
    /**
     * The otp code to verify. 
     */
    code: string = "012345"
    /**
     * If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. 
     */
    trustDevice?: boolean = true
}
```
</APIMethod>

### Backup Codes

Backup codes are generated and stored in the database. This can be used to recover access to the account if the user loses access to their phone or email.

#### Generating Backup Codes
Generate backup codes for account recovery:

<APIMethod
  path="/two-factor/generate-backup-codes"
  method="POST"
  requireSession
>
```ts
type generateBackupCodes = {
    /**
     * The users password. 
     */
    password: string
}

if (data) {
    // Show the backup codes to the user
}
```
</APIMethod>


<Callout type="warn">
When you generate backup codes, the old backup codes will be deleted and new ones will be generated.
</Callout>

#### Using Backup Codes

You can now allow users to provider backup code as account recover method.


<APIMethod path="/two-factor/verify-backup-code" method="POST">
```ts
type verifyBackupCode = {
    /**
     * A backup code to verify. 
     */
    code: string = "123456"
    /**
     * If true, the session cookie will not be set. 
     */
    disableSession?: boolean = false
    /**
     * If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. 
     */
    trustDevice?: boolean = true
}
```
</APIMethod>

<Callout>
Once a backup code is used, it will be removed from the database and can't be used again.
</Callout>

#### Viewing Backup Codes

To display the backup codes to the user, you can call `viewBackupCodes` on the server. This will return the backup codes in the response. You should only this if the user has a fresh session - a session that was just created.

<APIMethod
  path="/two-factor/view-backup-codes"
  method="GET"
  isServerOnly
  forceAsBody
>
```ts
type viewBackupCodes = {
    /**
     * The user ID to view all backup codes. 
     */
    userId?: string | null = "user-id"
}
```
</APIMethod>

### Trusted Devices

You can mark a device as trusted by passing `trustDevice` to `verifyTotp` or `verifyOtp`.

```ts
const verify2FA = async (code: string) => {
    const { data, error } = await authClient.twoFactor.verifyTotp({
        code,
        callbackURL: "/dashboard",
        trustDevice: true // Mark this device as trusted
    })
    if (data) {
        // 2FA verified and device trusted
    }
}
```

When `trustDevice` is set to `true`, the current device will be remembered for 60 days. During this period, the user won't be prompted for 2FA on subsequent sign-ins from this device. The trust period is refreshed each time the user signs in successfully.

### Issuer

By adding an `issuer` you can set your application name for the 2fa application.

For example, if your user uses Google Auth, the default appName will show up as `Better Auth`. However, by using the following code, it will show up as `my-app-name`.

```ts
twoFactor({
    issuer: "my-app-name" // [!code highlight]
})
```
---

## Schema

The plugin requires 1 additional fields in the `user` table and 1 additional table to store the two factor authentication data.

Table: `user`

<DatabaseTable
    fields={[
        { name: "twoFactorEnabled", type: "boolean", description: "Whether two factor authentication is enabled for the user.", isOptional: true },
    ]}
/>

Table: `twoFactor`

<DatabaseTable
    fields={[
        { name: "id", type: "string", description: "The ID of the two factor authentication.", isPrimaryKey: true },
        { name: "userId", type: "string", description: "The ID of the user", isForeignKey: true },
        { name: "secret", type: "string", description: "The secret used to generate the TOTP code.", isOptional: true },
        { name: "backupCodes", type: "string", description: "The backup codes used to recover access to the account if the user loses access to their phone or email.", isOptional: true },
    ]}
/>

## Options

### Server

**twoFactorTable**: The name of the table that stores the two factor authentication data. Default: `twoFactor`.

**skipVerificationOnEnable**: Skip the verification process before enabling two factor for a user.

**Issuer**: The issuer is the name of your application. It's used to generate TOTP codes. It'll be displayed in the authenticator apps.

**TOTP options**

these are options for TOTP.

<TypeTable
  type={{
    digits:{
        description: "The number of digits the otp to be",
        type: "number",
        default: 6,
    },
    period: {
        description: "The period for totp in seconds.",
        type: "number",
        default: 30,
    },
  }}
/>

**OTP options**

these are options for OTP.

<TypeTable
  type={{
    sendOTP: {
        description: "a function that sends the otp to the user's email or phone number. It takes two parameters: user and otp",
        type: "function",
    },
    period: {
        description: "The period for otp in minutes.", 
        type: "number",
        default: 3,
    },
    storeOTP: {
        description: "How to store the otp in the database. Whether to store it as plain text, encrypted or hashed. You can also provide a custom encryptor or hasher.",
        type: "string",
        default: "plain",
    },
  }}
  />

**Backup Code Options**

backup codes are generated and stored in the database when the user enabled two factor authentication. This can be used to recover access to the account if the user loses access to their phone or email.

<TypeTable
    type={{
        amount: {
            description: "The amount of backup codes to generate",
            type: "number",
            default: 10,
        },
        length: {
            description: "The length of the backup codes",
            type: "number",
            default: 10,
        },
        customBackupCodesGenerate: {
            description: "A function that generates custom backup codes. It takes no parameters and returns an array of strings.",
            type: "function",
        },
        storeBackupCodes: {
            description: "How to store the backup codes in the database. Whether to store it as plain text or encrypted. You can also provide a custom encryptor.",
            type: "string",
            default: "plain",
        },
    }}
/>

### Client

To use the two factor plugin in the client, you need to add it on your plugins list.

```ts title="auth-client.ts"
import { createAuthClient } from "better-auth/client"
import { twoFactorClient } from "better-auth/client/plugins"

const authClient =  createAuthClient({
    plugins: [
        twoFactorClient({ // [!code highlight]
            onTwoFactorRedirect(){ // [!code highlight]
                window.location.href = "/2fa" // Handle the 2FA verification redirect // [!code highlight]
            } // [!code highlight]
        }) // [!code highlight]
    ]
})
```


**Options**

`onTwoFactorRedirect`: A callback that will be called when the user needs to verify their 2FA code. This can be used to redirect the user to the 2FA page.

```

--------------------------------------------------------------------------------
/docs/content/docs/guides/clerk-migration-guide.mdx:
--------------------------------------------------------------------------------

```markdown
---
title: Migrating from Clerk to Better Auth
description: A step-by-step guide to transitioning from Clerk to Better Auth.
---

In this guide, we'll walk through the steps to migrate a project from Clerk to Better Auth — including email/password with proper hashing, social/external accounts, phone number, two-factor data, and more.

<Callout type="warn">
This migration will invalidate all active sessions. This guide doesn't currently show you how to migrate Organization but it should be possible with additional steps and the [Organization](/docs/plugins/organization) Plugin.
</Callout>

## Before You Begin

Before starting the migration process, set up Better Auth in your project. Follow the [installation guide](/docs/installation) to get started. And go to 

<Steps>
<Step>
### Connect to your database

You'll need to connect to your database to migrate the users and accounts. You can use any database you want, but for this example, we'll use PostgreSQL.

```package-install
npm install pg
```

And then you can use the following code to connect to your database.

```ts title="auth.ts"
import { Pool } from "pg";

export const auth = betterAuth({
    database: new Pool({ 
        connectionString: process.env.DATABASE_URL 
    }),
})
```
</Step>
<Step>
### Enable Email and Password (Optional)

Enable the email and password in your auth config and implement your own logic for sending verification emails, reset password emails, etc.

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    database: new Pool({ 
        connectionString: process.env.DATABASE_URL 
    }),
    emailAndPassword: { // [!code highlight]
        enabled: true, // [!code highlight]
    }, // [!code highlight]
    emailVerification: {
      sendVerificationEmail: async({ user, url })=>{
        // implement your logic here to send email verification
      }
	},
})
```

See [Email and Password](/docs/authentication/email-password) for more configuration options.
</Step>
<Step>
### Setup Social Providers (Optional)

Add social providers you have enabled in your Clerk project in your auth config.

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    database: new Pool({ 
        connectionString: process.env.DATABASE_URL 
    }),
    emailAndPassword: { 
        enabled: true,
    },
    socialProviders: { // [!code highlight]
        github: { // [!code highlight]
            clientId: process.env.GITHUB_CLIENT_ID, // [!code highlight]
            clientSecret: process.env.GITHUB_CLIENT_SECRET, // [!code highlight]
        } // [!code highlight]
    } // [!code highlight]
})
```
</Step>
<Step>
### Add Plugins (Optional)

You can add the following plugins to your auth config based on your needs.

[Admin](/docs/plugins/admin) Plugin will allow you to manage users, user impersonations and app level roles and permissions.

[Two Factor](/docs/plugins/2fa) Plugin will allow you to add two-factor authentication to your application.

[Phone Number](/docs/plugins/phone-number) Plugin will allow you to add phone number authentication to your application.

[Username](/docs/plugins/username) Plugin will allow you to add username authentication to your application.

```ts title="auth.ts"
import { Pool } from "pg";
import { betterAuth } from "better-auth";
import { admin, twoFactor, phoneNumber, username } from "better-auth/plugins";

export const auth = betterAuth({
    database: new Pool({ 
        connectionString: process.env.DATABASE_URL 
    }),
    emailAndPassword: { 
        enabled: true,
    },
    socialProviders: {
        github: {
            clientId: process.env.GITHUB_CLIENT_ID!,
            clientSecret: process.env.GITHUB_CLIENT_SECRET!,
        }
    },
    plugins: [admin(), twoFactor(), phoneNumber(), username()], // [!code highlight]
})
```
</Step>
  <Step>
  ### Generate Schema

  If you're using a custom database adapter, generate the schema:

  ```sh
  npx @better-auth/cli generate
  ```

  or if you're using the default adapter, you can use the following command:

  ```sh
  npx @better-auth/cli migrate
  ```
  </Step>
  <Step>
  ### Export Clerk Users
  Go to the Clerk dashboard and export the users. Check how to do it [here](https://clerk.com/docs/deployments/exporting-users#export-your-users-data-from-the-clerk-dashboard). It will download a CSV file with the users data. You need to save it as `exported_users.csv` and put it in the root of your project.
  </Step>
  <Step>
  ### Create the migration script

  Create a new file called `migrate-clerk.ts` in the `scripts` folder and add the following code:

  ```ts title="scripts/migrate-clerk.ts"
import { generateRandomString, symmetricEncrypt } from "better-auth/crypto";

import { auth } from "@/lib/auth"; // import your auth instance

function getCSVData(csv: string) {
    const lines = csv.split('\n').filter(line => line.trim());
    const headers = lines[0]?.split(',').map(header => header.trim()) || [];
    const jsonData = lines.slice(1).map(line => {
        const values = line.split(',').map(value => value.trim());
        return headers.reduce((obj, header, index) => {
            obj[header] = values[index] || '';
            return obj;
        }, {} as Record<string, string>);
    });

    return jsonData as Array<{
        id: string;
        first_name: string;
        last_name: string;
        username: string;
        primary_email_address: string;
        primary_phone_number: string;
        verified_email_addresses: string;
        unverified_email_addresses: string;
        verified_phone_numbers: string;
        unverified_phone_numbers: string;
        totp_secret: string;
        password_digest: string;
        password_hasher: string;
    }>;
}

const exportedUserCSV = await Bun.file("exported_users.csv").text(); // this is the file you downloaded from Clerk

async function getClerkUsers(totalUsers: number) {
    const clerkUsers: {
        id: string;
        first_name: string;
        last_name: string;
        username: string;
        image_url: string;
        password_enabled: boolean;
        two_factor_enabled: boolean;
        totp_enabled: boolean;
        backup_code_enabled: boolean;
        banned: boolean;
        locked: boolean;
        lockout_expires_in_seconds: number;
        created_at: number;
        updated_at: number;
        external_accounts: {
            id: string;
            provider: string;
            identification_id: string;
            provider_user_id: string;
            approved_scopes: string;
            email_address: string;
            first_name: string;
            last_name: string;
            image_url: string;
            created_at: number;
            updated_at: number;
        }[]
    }[] = [];
    for (let i = 0; i < totalUsers; i += 500) {
        const response = await fetch(`https://api.clerk.com/v1/users?offset=${i}&limit=${500}`, {
            headers: {
                'Authorization': `Bearer ${process.env.CLERK_SECRET_KEY}`
            }
        });
        if (!response.ok) {
            throw new Error(`Failed to fetch users: ${response.statusText}`);
        }
        const clerkUsersData = await response.json();
        // biome-ignore lint/suspicious/noExplicitAny: <explanation>
        clerkUsers.push(...clerkUsersData as any);
    }
    return clerkUsers;
}


export async function generateBackupCodes(
    secret: string,
) {
    const key = secret;
    const backupCodes = Array.from({ length: 10 })
        .fill(null)
        .map(() => generateRandomString(10, "a-z", "0-9", "A-Z"))
        .map((code) => `${code.slice(0, 5)}-${code.slice(5)}`);
    const encCodes = await symmetricEncrypt({
        data: JSON.stringify(backupCodes),
        key: key,
    });
    return encCodes
}

// Helper function to safely convert timestamp to Date
function safeDateConversion(timestamp?: number): Date {
    if (!timestamp) return new Date();

    // Convert seconds to milliseconds
    const date = new Date(timestamp * 1000);

    // Check if the date is valid
    if (isNaN(date.getTime())) {
        console.warn(`Invalid timestamp: ${timestamp}, falling back to current date`);
        return new Date();
    }

    // Check for unreasonable dates (before 2000 or after 2100)
    const year = date.getFullYear();
    if (year < 2000 || year > 2100) {
        console.warn(`Suspicious date year: ${year}, falling back to current date`);
        return new Date();
    }

    return date;
}

async function migrateFromClerk() {
    const jsonData = getCSVData(exportedUserCSV);
    const clerkUsers = await getClerkUsers(jsonData.length);
    const ctx = await auth.$context
    const isAdminEnabled = ctx.options?.plugins?.find(plugin => plugin.id === "admin");
    const isTwoFactorEnabled = ctx.options?.plugins?.find(plugin => plugin.id === "two-factor");
    const isUsernameEnabled = ctx.options?.plugins?.find(plugin => plugin.id === "username");
    const isPhoneNumberEnabled = ctx.options?.plugins?.find(plugin => plugin.id === "phone-number");
    for (const user of jsonData) {
        const { id, first_name, last_name, username, primary_email_address, primary_phone_number, verified_email_addresses, unverified_email_addresses, verified_phone_numbers, unverified_phone_numbers, totp_secret, password_digest, password_hasher } = user;
        const clerkUser = clerkUsers.find(clerkUser => clerkUser?.id === id);

        // create user
        const createdUser = await ctx.adapter.create<{
            id: string;
        }>({
            model: "user",
            data: {
                id,
                email: primary_email_address,
                emailVerified: verified_email_addresses.length > 0,
                name: `${first_name} ${last_name}`,
                image: clerkUser?.image_url,
                createdAt: safeDateConversion(clerkUser?.created_at),
                updatedAt: safeDateConversion(clerkUser?.updated_at),
                // # Two Factor (if you enabled two factor plugin)
                ...(isTwoFactorEnabled ? {
                    twoFactorEnabled: clerkUser?.two_factor_enabled
                } : {}),
                // # Admin (if you enabled admin plugin)
                ...(isAdminEnabled ? {
                    banned: clerkUser?.banned,
                    banExpiresAt: clerkUser?.lockout_expires_in_seconds,
                    role: "user"
                } : {}),
                // # Username (if you enabled username plugin)
                ...(isUsernameEnabled ? {
                    username: username,
                } : {}),
                // # Phone Number (if you enabled phone number plugin)  
                ...(isPhoneNumberEnabled ? {
                    phoneNumber: primary_phone_number,
                    phoneNumberVerified: verified_phone_numbers.length > 0,
                } : {}),
            },
            forceAllowId: true
        }).catch(async e => {
            return await ctx.adapter.findOne<{
                id: string;
            }>({
                model: "user",
                where: [{
                    field: "id",
                    value: id
                }]
            })
        })
        // create external account
        const externalAccounts = clerkUser?.external_accounts;
        if (externalAccounts) {
            for (const externalAccount of externalAccounts) {
                const { id, provider, identification_id, provider_user_id, approved_scopes, email_address, first_name, last_name, image_url, created_at, updated_at } = externalAccount;
                if (externalAccount.provider === "credential") {
                    await ctx.adapter.create({
                        model: "account",
                        data: {
                            id,
                            providerId: provider,
                            accountId: externalAccount.provider_user_id,
                            scope: approved_scopes,
                            userId: createdUser?.id,
                            createdAt: safeDateConversion(created_at),
                            updatedAt: safeDateConversion(updated_at),
                            password: password_digest,
                        }
                    })
                } else {
                    await ctx.adapter.create({
                        model: "account",
                        data: {
                            id,
                            providerId: provider.replace("oauth_", ""),
                            accountId: externalAccount.provider_user_id,
                            scope: approved_scopes,
                            userId: createdUser?.id,
                            createdAt: safeDateConversion(created_at),
                            updatedAt: safeDateConversion(updated_at),
                        },
                        forceAllowId: true
                    })
                }
            }
        }

        //two factor
        if (isTwoFactorEnabled) {
            await ctx.adapter.create({
                model: "twoFactor",
                data: {
                    userId: createdUser?.id,
                    secret: totp_secret,
                    backupCodes: await generateBackupCodes(totp_secret)
                }
            })
        }
    }
}

migrateFromClerk()
    .then(() => {
        console.log('Migration completed');
        process.exit(0);
    })
    .catch((error) => {
        console.error('Migration failed:', error);
        process.exit(1);
    });
  ```
  Make sure to replace the `process.env.CLERK_SECRET_KEY` with your own Clerk secret key. Feel free to customize the script to your needs.
 </Step>
  
<Step>
  ### Run the migration

  Run the migration:

  ```sh
  bun run script/migrate-clerk.ts # you can use any thing you like to run the script
  ```

  <Callout type="warning">
  Make sure to:
  1. Test the migration in a development environment first
  2. Monitor the migration process for any errors
  3. Verify the migrated data in Better Auth before proceeding
  4. Keep Clerk installed and configured until the migration is complete
  </Callout>

  </Step>
  <Step>
  ### Verify the migration

  After running the migration, verify that all users have been properly migrated by checking the database.
  </Step>
  <Step>
  ### Update your components

  Now that the data is migrated, you can start updating your components to use Better Auth. Here's an example for the sign-in component:

  ```tsx title="components/auth/sign-in.tsx"
  import { authClient } from "better-auth/client";

  export const SignIn = () => {
    const handleSignIn = async () => {
      const { data, error } = await authClient.signIn.email({
        email: "[email protected]",
        password: "password",
      });
      
      if (error) {
        console.error(error);
        return;
      }
      // Handle successful sign in
    };

    return (
      <form onSubmit={handleSignIn}>
        <button type="submit">Sign in</button>
      </form>
    );
  };
  ```
  </Step>
<Step>
  ### Update the middleware

  Replace your Clerk middleware with Better Auth's middleware:

  ```ts title="middleware.ts"

  import { NextRequest, NextResponse } from "next/server";
  import { getSessionCookie } from "better-auth/cookies";
  export async function middleware(request: NextRequest) {
    const sessionCookie = getSessionCookie(request);
    const { pathname } = request.nextUrl;
    if (sessionCookie && ["/login", "/signup"].includes(pathname)) {
      return NextResponse.redirect(new URL("/dashboard", request.url));
    }
    if (!sessionCookie && pathname.startsWith("/dashboard")) {
      return NextResponse.redirect(new URL("/login", request.url));
    }
    return NextResponse.next();
  }

  export const config = {
    matcher: ["/dashboard", "/login", "/signup"],
  };
  ```
  </Step>
  <Step>
  ### Remove Clerk Dependencies

  Once you've verified that everything is working correctly with Better Auth, you can remove Clerk:

  ```bash title="Remove Clerk"
  pnpm remove @clerk/nextjs @clerk/themes @clerk/types
  ```
    </Step>
</Steps>

## Additional Resources

[Goodbye Clerk, Hello Better Auth – Full Migration Guide!](https://www.youtube.com/watch?v=Za_QihbDSuk)

## Wrapping Up

Congratulations! You've successfully migrated from Clerk to Better Auth.

Better Auth offers greater flexibility and more features—be sure to explore the [documentation](/docs) to unlock its full potential.
```

--------------------------------------------------------------------------------
/docs/content/docs/integrations/expo.mdx:
--------------------------------------------------------------------------------

```markdown
---
title: Expo Integration
description: Integrate Better Auth with Expo.
---

Expo is a popular framework for building cross-platform apps with React Native. Better Auth supports both Expo native and web apps.

## Installation

<Steps>
    <Step>
        ## Configure A Better Auth Backend
        Before using Better Auth with Expo, make sure you have a Better Auth backend set up. You can either use a separate server or leverage Expo's new [API Routes](https://docs.expo.dev/router/reference/api-routes) feature to host your Better Auth instance.

        To get started, check out our [installation](/docs/installation) guide for setting up Better Auth on your server. If you prefer to check out the full example, you can find it [here](https://github.com/better-auth/examples/tree/main/expo-example).

        To use the new API routes feature in Expo to host your Better Auth instance you can create a new API route in your Expo app and mount the Better Auth handler.

        ```ts title="app/api/auth/[...auth]+api.ts"
        import { auth } from "@/lib/auth"; // import Better Auth handler
        
        const handler = auth.handler;
        export { handler as GET, handler as POST }; // export handler for both GET and POST requests
        ```
    </Step>
    <Step>
        ## Install Server Dependencies

        Install both the Better Auth package and Expo plugin into your server application.

        ```package-install
        better-auth @better-auth/expo
        ```

    </Step>

    <Step>
        ## Install Client Dependencies

        You also need to install both the Better Auth package and Expo plugin into your Expo application.

        ```package-install
        better-auth @better-auth/expo 
        ```

        If you plan on using our social integrations (Google, Apple etc.) then there are a few more dependencies that are required in your Expo app. In the default Expo template these are already installed so you may be able to skip this step if you have these dependencies already.

        ```package-install
        expo-linking expo-web-browser expo-constants
        
        ```
    </Step>
    
    <Step>
        ## Add the Expo Plugin on Your Server

        Add the Expo plugin to your Better Auth server.

        ```ts title="lib/auth.ts"
        import { betterAuth } from "better-auth";
        import { expo } from "@better-auth/expo";

        export const auth = betterAuth({
            plugins: [expo()],
            emailAndPassword: { 
                enabled: true, // Enable authentication using email and password.
              }, 
        });
        ```
    </Step>

    <Step>
        ## Initialize Better Auth Client
        
        To initialize Better Auth in your Expo app, you need to call `createAuthClient` with the base URL of your Better Auth backend. Make sure to import the client from `/react`.

        Make sure you install the `expo-secure-store` package into your Expo app. This is used to store the session data and cookies securely.

        ```package-install
        expo-secure-store
        ```

        You need to also import client plugin from `@better-auth/expo/client` and pass it to the `plugins` array when initializing the auth client.

        This is important because:

        - **Social Authentication Support:** enables social auth flows by handling authorization URLs and callbacks within the Expo web browser.
        - **Secure Cookie Management:** stores cookies securely and automatically adds them to the headers of your auth requests.

        ```ts title="lib/auth-client.ts"
        import { createAuthClient } from "better-auth/react";
        import { expoClient } from "@better-auth/expo/client";
        import * as SecureStore from "expo-secure-store";

        export const authClient = createAuthClient({
            baseURL: "http://localhost:8081", // Base URL of your Better Auth backend.
            plugins: [
                expoClient({
                    scheme: "myapp",
                    storagePrefix: "myapp",
                    storage: SecureStore,
                })
            ]
        });
        ```
        <Callout>
         Be sure to include the full URL, including the path, if you've changed the default path from `/api/auth`.
        </Callout>
    </Step>

     <Step>
        ## Scheme and Trusted Origins

        Better Auth uses deep links to redirect users back to your app after authentication. To enable this, you need to add your app's scheme to the `trustedOrigins` list in your Better Auth config. 

        First, make sure you have a scheme defined in your `app.json` file.

        ```json title="app.json"
        {
            "expo": {
                "scheme": "myapp"
            }
        }
        ```

        Then, update your Better Auth config to include the scheme in the `trustedOrigins` list.

        ```ts title="auth.ts"
        export const auth = betterAuth({
            trustedOrigins: ["myapp://"]
        })
        ```

        If you have multiple schemes or need to support deep linking with various paths, you can use specific patterns or wildcards:

        ```ts title="auth.ts"
        export const auth = betterAuth({
            trustedOrigins: [
                // Basic scheme
                "myapp://", 
                
                // Production & staging schemes
                "myapp-prod://",
                "myapp-staging://",
                
                // Wildcard support for all paths following the scheme
                "myapp://*"
            ]
        })
        ```

        ### Development Mode

        During development, Expo uses the `exp://` scheme with your device's local IP address. To support this, you can use wildcards to match common local IP ranges:

        ```ts title="auth.ts"
        export const auth = betterAuth({
            trustedOrigins: [
                "myapp://",
                
                // Development mode - Expo's exp:// scheme with local IP ranges
                ...(process.env.NODE_ENV === "development" ? [
                    "exp://*/*",                 // Trust all Expo development URLs
                    "exp://10.0.0.*:*/*",        // Trust 10.0.0.x IP range
                    "exp://192.168.*.*:*/*",     // Trust 192.168.x.x IP range
                    "exp://172.*.*.*:*/*",       // Trust 172.x.x.x IP range
                    "exp://localhost:*/*"        // Trust localhost
                ] : [])
            ]
        })
        ```

        <Callout type="warn">
          The wildcard patterns for `exp://` should only be used in development. In production, use your app's specific scheme (e.g., `myapp://`).
        </Callout>
    </Step>
 
    <Step>
        ## Configure Metro Bundler

        To resolve Better Auth exports you'll need to enable `unstable_enablePackageExports` in your metro config. 

        ```js title="metro.config.js"
        const { getDefaultConfig } = require("expo/metro-config");

        const config = getDefaultConfig(__dirname)

        config.resolver.unstable_enablePackageExports = true; // [!code highlight]

        module.exports = config;
        ```

        <Callout>In case you don't have a `metro.config.js` file in your project run `npx expo customize metro.config.js`.</Callout>
        
        If you can't enable `unstable_enablePackageExports` option, you can use [babel-plugin-module-resolver](https://github.com/tleunen/babel-plugin-module-resolver) to manually resolve the paths.

        ```ts title="babel.config.js"
        module.exports = function (api) {
            api.cache(true);
            return {
                presets: ["babel-preset-expo"],
                plugins: [
                    [
                        "module-resolver",
                        {
                            alias: {
                                "better-auth/react": "./node_modules/better-auth/dist/client/react/index.cjs",
                                "better-auth/client/plugins": "./node_modules/better-auth/dist/client/plugins/index.cjs",
                                "@better-auth/expo/client": "./node_modules/@better-auth/expo/dist/client.cjs",
                            },
                        },
                    ],
                ],
            }
        }
        ```

        <Callout>In case you don't have a `babel.config.js` file in your project run `npx expo customize babel.config.js`.</Callout>

        Don't forget to clear the cache after making changes.

        ```bash
        npx expo start --clear
        ```

    </Step>
</Steps>


## Usage

### Authenticating Users

With Better Auth initialized, you can now use the `authClient` to authenticate users in your Expo app.

<Tabs items={["sign-in", "sign-up"]}>
    <Tab value="sign-in">
        ```tsx title="app/sign-in.tsx"
        import { useState } from "react"; 
        import { View, TextInput, Button } from "react-native";
        import { authClient } from "@/lib/auth-client";

        export default function SignIn() {
            const [email, setEmail] = useState("");
            const [password, setPassword] = useState("");

            const handleLogin = async () => {
                await authClient.signIn.email({
                    email,
                    password,
                })
            };

            return (
                <View>
                    <TextInput
                        placeholder="Email"
                        value={email}
                        onChangeText={setEmail}
                    />
                    <TextInput
                        placeholder="Password"
                        value={password}
                        onChangeText={setPassword}
                    />
                    <Button title="Login" onPress={handleLogin} />
                </View>
            );
        }
        ```
    </Tab>
    <Tab value="sign-up">
        ```tsx title="app/sign-up.tsx"
        import { useState } from "react";
        import { View, TextInput, Button } from "react-native";
        import { authClient } from "@/lib/auth-client";

        export default function SignUp() {
            const [email, setEmail] = useState("");
            const [name, setName] = useState("");
            const [password, setPassword] = useState("");

            const handleLogin = async () => {
                await authClient.signUp.email({
                        email,
                        password,
                        name
                })
            };

            return (
                <View>
                    <TextInput
                        placeholder="Name"
                        value={name}
                        onChangeText={setName}
                    />
                    <TextInput
                        placeholder="Email"
                        value={email}
                        onChangeText={setEmail}
                    />
                    <TextInput
                        placeholder="Password"
                        value={password}
                        onChangeText={setPassword}
                    />
                    <Button title="Login" onPress={handleLogin} />
                </View>
            );
        }
        ```
    </Tab>
</Tabs>

#### Social Sign-In

For social sign-in, you can use the `authClient.signIn.social` method with the provider name and a callback URL.

```tsx title="app/social-sign-in.tsx"
import { Button } from "react-native";

export default function SocialSignIn() {
    const handleLogin = async () => {
        await authClient.signIn.social({
            provider: "google",
            callbackURL: "/dashboard" // this will be converted to a deep link (eg. `myapp://dashboard`) on native
        })
    };
    return <Button title="Login with Google" onPress={handleLogin} />;
}
```

#### IdToken Sign-In

If you want to make provider request on the mobile device and then verify the ID token on the server, you can use the `authClient.signIn.social` method with the `idToken` option.

```tsx title="app/social-sign-in.tsx"
import { Button } from "react-native";

export default function SocialSignIn() {
    const handleLogin = async () => {
        await authClient.signIn.social({
            provider: "google", // only google, apple and facebook are supported for idToken signIn
            idToken: {
                token: "...", // ID token from provider
                nonce: "...", // nonce from provider (optional)
            }
            callbackURL: "/dashboard" // this will be converted to a deep link (eg. `myapp://dashboard`) on native
        })
    };
    return <Button title="Login with Google" onPress={handleLogin} />;
}
```

### Session

Better Auth provides a `useSession` hook to access the current user's session in your app.

```tsx title="app/index.tsx"
import { Text } from "react-native";
import { authClient } from "@/lib/auth-client";

export default function Index() {
    const { data: session } = authClient.useSession();

    return <Text>Welcome, {session?.user.name}</Text>;
}
```

On native, the session data will be cached in SecureStore. This will allow you to remove the need for a loading spinner when the app is reloaded. You can disable this behavior by passing the `disableCache` option to the client.


### Making Authenticated Requests to Your Server

To make authenticated requests to your server that require the user's session, you have to retrieve the session cookie from `SecureStore` and manually add it to your request headers.

```tsx
import { authClient } from "@/lib/auth-client";

const makeAuthenticatedRequest = async () => {
  const cookies = authClient.getCookie(); // [!code highlight]
  const headers = {
    "Cookie": cookies, // [!code highlight]
  };
  const response = await fetch("http://localhost:8081/api/secure-endpoint", { 
    headers,
    // 'include' can interfere with the cookies we just set manually in the headers
    credentials: "omit" // [!code highlight]
  });
  const data = await response.json();
  return data;
};
```

**Example: Usage With TRPC**
    
```tsx title="lib/trpc-provider.tsx"
//...other imports
import { authClient } from "@/lib/auth-client"; // [!code highlight]

export const api = createTRPCReact<AppRouter>();

export function TRPCProvider(props: { children: React.ReactNode }) {
  const [queryClient] = useState(() => new QueryClient());
  const [trpcClient] = useState(() =>
    api.createClient({
      links: [
        httpBatchLink({
          //...your other options
          headers() {
            const headers = new Map<string, string>(); // [!code highlight]
            const cookies = authClient.getCookie(); // [!code highlight]
            if (cookies) { // [!code highlight]
              headers.set("Cookie", cookies); // [!code highlight]
            } // [!code highlight]
            return Object.fromEntries(headers); // [!code highlight]
          },
        }),
      ],
    }),
  );

  return (
    <api.Provider client={trpcClient} queryClient={queryClient}>
      <QueryClientProvider client={queryClient}>
        {props.children}
      </QueryClientProvider>
    </api.Provider>
  );
}
```


## Options

### Expo Client

**storage**: the storage mechanism used to cache the session data and cookies.

```ts title="lib/auth-client.ts"
import { createAuthClient } from "better-auth/react";
import SecureStorage from "expo-secure-store";

const authClient = createAuthClient({
    baseURL: "http://localhost:8081",
    storage: SecureStorage
});
```

**scheme**: scheme is used to deep link back to your app after a user has authenticated using oAuth providers. By default, Better Auth tries to read the scheme from the `app.json` file. If you need to override this, you can pass the scheme option to the client.

```ts title="lib/auth-client.ts"
import { createAuthClient } from "better-auth/react";

const authClient = createAuthClient({
    baseURL: "http://localhost:8081",
    scheme: "myapp"
});
```

**disableCache**: By default, the client will cache the session data in SecureStore. You can disable this behavior by passing the `disableCache` option to the client.

```ts title="lib/auth-client.ts"
import { createAuthClient } from "better-auth/react";

const authClient = createAuthClient({
    baseURL: "http://localhost:8081",
    disableCache: true
});
```


### Expo Servers

Server plugin options:

**overrideOrigin**: Override the origin for Expo API routes (default: false). Enable this if you're facing cors origin issues with Expo API routes.

```

--------------------------------------------------------------------------------
/docs/content/docs/installation.mdx:
--------------------------------------------------------------------------------

```markdown
---
title: Installation
description: Learn how to configure Better Auth in your project.
---

<Steps>

<Step>
### Install the Package

Let's start by adding Better Auth to your project:

```package-install
better-auth
```

<Callout type="info">
If you're using a separate client and server setup, make sure to install Better Auth in both parts of your project.
</Callout>
</Step>

<Step>
### Set Environment Variables

Create a `.env` file in the root of your project and add the following environment variables:

1. **Secret Key**

Random value used by the library for encryption and generating hashes. **You can generate one using the button below** or you can use something like openssl.

```txt title=".env"
BETTER_AUTH_SECRET=
```

<GenerateSecret />

2. **Set Base URL**

```txt title=".env"
BETTER_AUTH_URL=http://localhost:3000 # Base URL of your app
```

</Step>

<Step>
### Create A Better Auth Instance

Create a file named `auth.ts` in one of these locations:

- Project root
- `lib/` folder
- `utils/` folder

You can also nest any of these folders under `src/`, `app/` or `server/` folder. (e.g. `src/lib/auth.ts`, `app/lib/auth.ts`).

And in this file, import Better Auth and create your auth instance. Make sure to export the auth instance with the variable name `auth` or as a `default` export.

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  //...
});
```

</Step>

<Step>
### Configure Database

Better Auth requires a database to store user data.
You can easily configure Better Auth to use SQLite, PostgreSQL, or MySQL, and more!

<Tabs items={["sqlite", "postgres", "mysql"]}>
    <Tab value="sqlite">
    ```ts title="auth.ts"
    import { betterAuth } from "better-auth";
    import Database from "better-sqlite3";

    export const auth = betterAuth({
        database: new Database("./sqlite.db"),
    })
    ```
    </Tab>
    <Tab value="postgres">
    ```ts title="auth.ts"
    import { betterAuth } from "better-auth";
    import { Pool } from "pg";

    export const auth = betterAuth({
        database: new Pool({
            // connection options
        }),
    })
    ```
    </Tab>
    <Tab value="mysql">
    ```ts title="auth.ts"
    import { betterAuth } from "better-auth";
    import { createPool } from "mysql2/promise";

    export const auth = betterAuth({
        database: createPool({
            // connection options
        }),
    })
    ```
    </Tab>

</Tabs>

Alternatively, if you prefer to use an ORM, you can use one of the built-in adapters.

<Tabs items={["drizzle", "prisma", "mongodb"]}>

    <Tab value="drizzle">
    ```ts title="auth.ts"
    import { betterAuth } from "better-auth";
    import { drizzleAdapter } from "better-auth/adapters/drizzle";
    import { db } from "@/db"; // your drizzle instance

    export const auth = betterAuth({
        database: drizzleAdapter(db, {
            provider: "pg", // or "mysql", "sqlite"
        }),
    });
    ```
    </Tab>
    <Tab value="prisma">
        ```ts title="auth.ts"
        import { betterAuth } from "better-auth";
        import { prismaAdapter } from "better-auth/adapters/prisma";
        // If your Prisma file is located elsewhere, you can change the path
        import { PrismaClient } from "@/generated/prisma";

        const prisma = new PrismaClient();
        export const auth = betterAuth({
            database: prismaAdapter(prisma, {
                provider: "sqlite", // or "mysql", "postgresql", ...etc
            }),
        });
    ```
    </Tab>

    <Tab value="mongodb">
    ```ts title="auth.ts"
    import { betterAuth } from "better-auth";
    import { mongodbAdapter } from "better-auth/adapters/mongodb";
    import { client } from "@/db"; // your mongodb client

    export const auth = betterAuth({
        database: mongodbAdapter(client),
    });
    ```
    </Tab>

</Tabs>

<Callout>
  If your database is not listed above, check out our other supported
  [databases](/docs/adapters/other-relational-databases) for more information,
  or use one of the supported ORMs.
</Callout>

</Step>

<Step>
    ### Create Database Tables
    Better Auth includes a CLI tool to help manage the schema required by the library.

    - **Generate**: This command generates an ORM schema or SQL migration file.

    <Callout>
    If you're using Kysely, you can apply the migration directly with `migrate` command below. Use `generate` only if you plan to apply the migration manually.
    </Callout>

    ```bash title="Terminal"
    npx @better-auth/cli generate
    ```

    - **Migrate**: This command creates the required tables directly in the database. (Available only for the built-in Kysely adapter)

    ```bash title="Terminal"
    npx @better-auth/cli migrate
    ```

    see the [CLI documentation](/docs/concepts/cli) for more information.

    <Callout>
     If you instead want to create the schema manually, you can find the core schema required in the [database section](/docs/concepts/database#core-schema).
    </Callout>

</Step>

<Step>

### Authentication Methods

Configure the authentication methods you want to use. Better Auth comes with built-in support for email/password, and social sign-on providers.

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  //...other options // [!code highlight]
  emailAndPassword: { // [!code highlight]
    enabled: true, // [!code highlight]
  }, // [!code highlight]
  socialProviders: { // [!code highlight]
    github: { // [!code highlight]
      clientId: process.env.GITHUB_CLIENT_ID as string, // [!code highlight]
      clientSecret: process.env.GITHUB_CLIENT_SECRET as string, // [!code highlight]
    }, // [!code highlight]
  }, // [!code highlight]
});
```

<Callout type="info">
You can use even more authentication methods like [passkey](/docs/plugins/passkey), [username](/docs/plugins/username), [magic link](/docs/plugins/magic-link) and more through plugins.
</Callout>
</Step>

<Step>
### Mount Handler
To handle API requests, you need to set up a route handler on your server.

Create a new file or route in your framework's designated catch-all route handler. This route should handle requests for the path `/api/auth/*` (unless you've configured a different base path).

<Callout>
  Better Auth supports any backend framework with standard Request and Response
  objects and offers helper functions for popular frameworks.
</Callout>

<Tabs items={["next-js", "nuxt", "svelte-kit", "remix", "solid-start", "hono", "cloudflare-workers", "express", "elysia", "tanstack-start", "expo"]} defaultValue="next-js">
    <Tab value="next-js">
        ```ts title="/app/api/auth/[...all]/route.ts"
        import { auth } from "@/lib/auth"; // path to your auth file
        import { toNextJsHandler } from "better-auth/next-js";

        export const { POST, GET } = toNextJsHandler(auth);
        ```
    </Tab>
    <Tab value="nuxt">
    ```ts title="/server/api/auth/[...all].ts"
    import { auth } from "~/utils/auth"; // path to your auth file

    export default defineEventHandler((event) => {
        return auth.handler(toWebRequest(event));
    });
    ```
    </Tab>
    <Tab value="svelte-kit">
    ```ts title="hooks.server.ts"
    import { auth } from "$lib/auth"; // path to your auth file
    import { svelteKitHandler } from "better-auth/svelte-kit";
    import { building } from '$app/environment'

    export async function handle({ event, resolve }) {
        return svelteKitHandler({ event, resolve, auth, building });
    }
    ```
    </Tab>
    <Tab value="remix">
    ```ts title="/app/routes/api.auth.$.ts"
    import { auth } from '~/lib/auth.server' // Adjust the path as necessary
    import type { LoaderFunctionArgs, ActionFunctionArgs } from "@remix-run/node"

    export async function loader({ request }: LoaderFunctionArgs) {
        return auth.handler(request)
    }

    export async function action({ request }: ActionFunctionArgs) {
        return auth.handler(request)
    }
    ```
    </Tab>
    <Tab value="solid-start">
    ```ts title="/routes/api/auth/*all.ts"
    import { auth } from "~/lib/auth"; // path to your auth file
    import { toSolidStartHandler } from "better-auth/solid-start";

    export const { GET, POST } = toSolidStartHandler(auth);
    ```
    </Tab>
    <Tab value="hono">
    ```ts title="src/index.ts"
    import { Hono } from "hono";
    import { auth } from "./auth"; // path to your auth file
    import { serve } from "@hono/node-server";
    import { cors } from "hono/cors";

    const app = new Hono();

    app.on(["POST", "GET"], "/api/auth/*", (c) => auth.handler(c.req.raw));

    serve(app);
    ```
    </Tab>
    <Tab value="cloudflare-workers">
    ```ts title="src/index.ts"
    import { auth } from "./auth"; // path to your auth file

    export default {
        async fetch(request: Request) {
            const url = new URL(request.url);

            // Handle auth routes
            if (url.pathname.startsWith("/api/auth")) {
                return auth.handler(request);
            }

            // Handle other routes
            return new Response("Not found", { status: 404 });
        },
    };
    ```

    <Callout type="info">
    **Node.js AsyncLocalStorage Support**: Better Auth uses AsyncLocalStorage for async context tracking. To enable this in Cloudflare Workers, add the `nodejs_compat` flag to your `wrangler.toml`:

    ```toml title="wrangler.toml"
    compatibility_flags = ["nodejs_compat"]
    compatibility_date = "2024-09-23"
    ```

    Alternatively, if you only need AsyncLocalStorage support:
    ```toml title="wrangler.toml"
    compatibility_flags = ["nodejs_als"]
    ```

    In the next major release, we will assume AsyncLocalStorage support by default, so this configuration will be necessary.
    </Callout>
    </Tab>

    <Tab value="express">
    <Callout type="warn">
        ExpressJS v5 introduced breaking changes to route path matching by switching to `path-to-regexp@6`. Wildcard routes like `*` should now be written using the new named syntax, e.g. `/{*any}`, to properly capture catch-all patterns. This ensures compatibility and predictable behavior across routing scenarios.
        See the [Express v5 migration guide](https://expressjs.com/en/guide/migrating-5.html) for details.

        As a result, the implementation in ExpressJS v5 should look like this:

        ```ts
        app.all('/api/auth/{*any}', toNodeHandler(auth));
        ```
        *The name any is arbitrary and can be replaced with any identifier you prefer.*
    </Callout>

    ```ts title="server.ts"
    import express from "express";
    import { toNodeHandler } from "better-auth/node";
    import { auth } from "./auth";

    const app = express();
    const port = 8000;

    app.all("/api/auth/*", toNodeHandler(auth));

    // Mount express json middleware after Better Auth handler
    // or only apply it to routes that don't interact with Better Auth
    app.use(express.json());

    app.listen(port, () => {
        console.log(`Better Auth app listening on port ${port}`);
    });
    ```
    This will also work for any other node server framework like express, fastify, hapi, etc., but may require some modifications. See [fastify guide](/docs/integrations/fastify). Note that CommonJS (cjs) isn't supported.
    </Tab>
    <Tab value="astro">
    ```ts title="/pages/api/auth/[...all].ts"
    import type { APIRoute } from "astro";
    import { auth } from "@/auth"; // path to your auth file

    export const GET: APIRoute = async (ctx) => {
        return auth.handler(ctx.request);
    };

    export const POST: APIRoute = async (ctx) => {
        return auth.handler(ctx.request);
    };
    ```
    </Tab>
    <Tab value="elysia">
    ```ts
    import { Elysia, Context } from "elysia";
    import { auth } from "./auth";

    const betterAuthView = (context: Context) => {
        const BETTER_AUTH_ACCEPT_METHODS = ["POST", "GET"]
        // validate request method
        if(BETTER_AUTH_ACCEPT_METHODS.includes(context.request.method)) {
            return auth.handler(context.request);
        } else {
            context.error(405)
        }
    }

    const app = new Elysia().all("/api/auth/*", betterAuthView).listen(3000);

    console.log(
    `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
    );
    ```
    </Tab>
    <Tab value="tanstack-start">
    ```ts title="src/routes/api/auth/$.ts"
    import { createFileRoute } from '@tanstack/react-router'
    import { auth } from '@/lib/auth/auth'

    export const Route = createFileRoute('/api/auth/$')({
    server: {
        handlers: {
            GET: async ({ request }:{ request: Request }) => {
                return await auth.handler(request)
            },
            POST: async ({ request }:{ request: Request }) => {
                return await auth.handler(request)
            },
        },
    },
    })
    ```
    </Tab>
    <Tab value="expo">
    ```ts title="app/api/auth/[...all]+api.ts"
    import { auth } from '@/lib/server/auth'; // path to your auth file

    const handler = auth.handler;
    export { handler as GET, handler as POST };
    ```
    </Tab>

</Tabs>
</Step>

<Step>
### Create Client Instance

The client-side library helps you interact with the auth server. Better Auth comes with a client for all the popular web frameworks, including vanilla JavaScript.

1. Import `createAuthClient` from the package for your framework (e.g., "better-auth/react" for React).
2. Call the function to create your client.
3. Pass the base URL of your auth server. (If the auth server is running on the same domain as your client, you can skip this step.)

<Callout type="info">
  If you're using a different base path other than `/api/auth` make sure to pass
  the whole URL including the path. (e.g.
  `http://localhost:3000/custom-path/auth`)
</Callout>

<Tabs items={["react", "vue", "svelte", "solid",
  "vanilla"]} defaultValue="react">
    <Tab value="vanilla">
            ```ts  title="lib/auth-client.ts"
            import { createAuthClient } from "better-auth/client"
            export const authClient = createAuthClient({
                /** The base URL of the server (optional if you're using the same domain) */ // [!code highlight]
                baseURL: "http://localhost:3000" // [!code highlight]
            })
            ```
    </Tab>
    <Tab value="react" title="lib/auth-client.ts">
            ```ts  title="lib/auth-client.ts"
            import { createAuthClient } from "better-auth/react"
            export const authClient = createAuthClient({
                /** The base URL of the server (optional if you're using the same domain) */ // [!code highlight]
                baseURL: "http://localhost:3000" // [!code highlight]
            })
            ```
    </Tab>
    <Tab value="vue" title="lib/auth-client.ts">
            ```ts  title="lib/auth-client.ts"
            import { createAuthClient } from "better-auth/vue"
            export const authClient = createAuthClient({
                /** The base URL of the server (optional if you're using the same domain) */ // [!code highlight]
                baseURL: "http://localhost:3000" // [!code highlight]
            })
            ```
    </Tab>
    <Tab value="svelte" title="lib/auth-client.ts">
            ```ts  title="lib/auth-client.ts"
            import { createAuthClient } from "better-auth/svelte"
            export const authClient = createAuthClient({
                /** The base URL of the server (optional if you're using the same domain) */ // [!code highlight]
                baseURL: "http://localhost:3000" // [!code highlight]
            })
            ```
    </Tab>
    <Tab value="solid" title="lib/auth-client.ts">
            ```ts title="lib/auth-client.ts"
            import { createAuthClient } from "better-auth/solid"
            export const authClient = createAuthClient({
                /** The base URL of the server (optional if you're using the same domain) */ // [!code highlight]
                baseURL: "http://localhost:3000" // [!code highlight]
            })
            ```
    </Tab>
</Tabs>

<Callout type="info">
Tip: You can also export specific methods if you prefer:
</Callout>
```ts
export const { signIn, signUp, useSession } = createAuthClient()
```
</Step>

<Step>
### 🎉 That's it!
That's it! You're now ready to use better-auth in your application. Continue to [basic usage](/docs/basic-usage) to learn how to use the auth instance to sign in users.
</Step>
</Steps>

```

--------------------------------------------------------------------------------
/packages/better-auth/src/plugins/organization/schema.ts:
--------------------------------------------------------------------------------

```typescript
import * as z from "zod";
import { generateId } from "../../utils";
import type { OrganizationOptions } from "./types";
import type { InferAdditionalFieldsFromPluginOptions } from "../../db";
import type { Prettify } from "better-call";

// TODO: we need a better way to define plugin schema and endpoints
export type OrganizationSchema<O extends OrganizationOptions> =
	O["dynamicAccessControl"] extends { enabled: true }
		? {
				organizationRole: {
					modelName: O["schema"] extends {
						organizationRole: { modelName: infer M };
					}
						? M extends string
							? M
							: string
						: string;
					fields: {
						organizationId: {
							type: "string";
							required: true;
							references: {
								model: "organization";
								field: "id";
							};
							fieldName?: O["schema"] extends {
								organizationRole: {
									fields: { organizationId: infer F };
								};
							}
								? F extends string
									? F
									: string
								: string;
						};
						role: {
							type: "string";
							required: true;
							fieldName?: O["schema"] extends {
								organizationRole: {
									fields: { role: infer F };
								};
							}
								? F extends string
									? F
									: string
								: string;
						};
						permission: {
							type: "string";
							required: true;
							fieldName?: O["schema"] extends {
								organizationRole: {
									fields: { permission: infer F };
								};
							}
								? F extends string
									? F
									: string
								: string;
						};
						createdAt: {
							type: "date";
							required: true;
							defaultValue: Date;
							fieldName?: O["schema"] extends {
								organizationRole: {
									fields: { createdAt: infer F };
								};
							}
								? F extends string
									? F
									: string
								: string;
						};
						updatedAt: {
							type: "date";
							required: false;
							fieldName?: O["schema"] extends {
								organizationRole: {
									fields: { updatedAt: infer F };
								};
							}
								? F extends string
									? F
									: string
								: string;
						};
					} & (O["schema"] extends {
						organizationRole: { additionalFields: infer F };
					}
						? F
						: {});
				};
			}
		: {} & (O["teams"] extends { enabled: true }
				? {
						team: {
							modelName: O["schema"] extends { team: { modelName: infer M } }
								? M extends string
									? M
									: string
								: string;
							fields: {
								name: {
									type: "string";
									required: true;
									fieldName?: O["schema"] extends {
										team: { fields: { name: infer F } };
									}
										? F extends string
											? F
											: string
										: string;
								};
								organizationId: {
									type: "string";
									required: true;
									references: {
										model: "organization";
										field: "id";
									};
									fieldName?: O["schema"] extends {
										team: { fields: { organizationId: infer F } };
									}
										? F extends string
											? F
											: string
										: string;
								};
								createdAt: {
									type: "date";
									required: true;
									fieldName?: O["schema"] extends {
										team: { fields: { createdAt: infer F } };
									}
										? F extends string
											? F
											: string
										: string;
								};
								updatedAt: {
									type: "date";
									required: false;
									fieldName?: O["schema"] extends {
										team: { fields: { updatedAt: infer F } };
									}
										? F extends string
											? F
											: string
										: string;
								};
							} & (O["schema"] extends { team: { additionalFields: infer F } }
								? F
								: {});
						};
						teamMember: {
							modelName: O["schema"] extends {
								teamMember: { modelName: infer M };
							}
								? M extends string
									? M
									: string
								: string;
							fields: {
								teamId: {
									type: "string";
									required: true;
									references: {
										model: "team";
										field: "id";
									};
									fieldName?: O["schema"] extends {
										teamMember: { fields: { teamId: infer F } };
									}
										? F extends string
											? F
											: string
										: string;
								};
								userId: {
									type: "string";
									required: true;
									references: {
										model: "user";
										field: "id";
									};
									fieldName?: O["schema"] extends {
										teamMember: { fields: { userId: infer F } };
									}
										? F extends string
											? F
											: string
										: string;
								};
								createdAt: {
									type: "date";
									required: false;
									fieldName?: O["schema"] extends {
										teamMember: { fields: { createdAt: infer F } };
									}
										? F extends string
											? F
											: string
										: string;
								};
							};
						};
					}
				: {}) & {
					organization: {
						modelName: O["schema"] extends {
							organization: { modelName: infer M };
						}
							? M extends string
								? M
								: string
							: string;
						fields: {
							name: {
								type: "string";
								required: true;
								sortable: true;
								fieldName?: O["schema"] extends {
									organization: { fields: { name: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
							};
							slug: {
								type: "string";
								required: true;
								unique: true;
								sortable: true;
								fieldName?: O["schema"] extends {
									organization: { fields: { slug: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
							};
							logo: {
								type: "string";
								required: false;
								fieldName?: O["schema"] extends {
									organization: { fields: { logo: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
							};
							createdAt: {
								type: "date";
								required: true;
								fieldName?: O["schema"] extends {
									organization: { fields: { createdAt: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
							};
							updatedAt: {
								type: "date";
								required: false;
								fieldName?: O["schema"] extends {
									organization: { fields: { updatedAt: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
							};
						};
					};
					member: {
						modelName: O["schema"] extends { member: { modelName: infer M } }
							? M extends string
								? M
								: string
							: string;
						fields: {
							organizationId: {
								type: "string";
								required: true;
								references: {
									model: "organization";
									field: "id";
								};
								fieldName?: O["schema"] extends {
									member: { fields: { organizationId: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
							};
							userId: {
								type: "string";
								required: true;
								references: {
									model: "user";
									field: "id";
								};
								fieldName?: O["schema"] extends {
									member: { fields: { userId: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
							};
							role: {
								type: "string";
								required: true;
								fieldName?: O["schema"] extends {
									member: { fields: { role: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
								defaultValue: "member";
							};
							createdAt: {
								type: "date";
								required: true;
								fieldName?: O["schema"] extends {
									member: { fields: { createdAt: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
							};
						} & (O["schema"] extends { member: { additionalFields: infer F } }
							? F
							: {});
					};
					invitation: {
						modelName: O["schema"] extends {
							invitation: { modelName: infer M };
						}
							? M
							: string;
						fields: {
							organizationId: {
								type: "string";
								required: true;
								references: {
									model: "organization";
									field: "id";
								};
								fieldName?: O["schema"] extends {
									invitation: { fields: { organizationId: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
							};
							email: {
								type: "string";
								required: true;
								sortable: true;
								fieldName?: O["schema"] extends {
									invitation: { fields: { email: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
							};
							role: {
								type: "string";
								required: true;
								sortable: true;
								fieldName?: O["schema"] extends {
									invitation: { fields: { role: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
							};
							status: {
								type: "string";
								required: true;
								sortable: true;
								fieldName?: O["schema"] extends {
									invitation: { fields: { status: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
								defaultValue: "pending";
							};
							expiresAt: {
								type: "date";
								required: false;
								fieldName?: O["schema"] extends {
									invitation: { fields: { expiresAt: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
							};
							createdAt: {
								type: "date";
								required: true;
								fieldName?: O["schema"] extends {
									invitation: { fields: { createdAt: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
								defaultValue: Date;
							};
							inviterId: {
								type: "string";
								required: true;
								references: {
									model: "user";
									field: "id";
								};
								fieldName?: O["schema"] extends {
									invitation: { fields: { inviterId: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
							};
						} & (O["schema"] extends {
							invitation: { additionalFields: infer F };
						}
							? F
							: {}) &
							O extends { teams: { enabled: true } }
							? {
									teamId: {
										type: "string";
										required: false;
										sortable: true;
										fieldName?: O["schema"] extends {
											invitation: { fields: { teamId: infer F } };
										}
											? F extends string
												? F
												: string
											: string;
									};
								}
							: {};
					};
					session: {
						fields: {
							activeOrganizationId: {
								type: "string";
								required: false;
								fieldName?: O["schema"] extends {
									session: { fields: { activeOrganizationId: infer F } };
								}
									? F extends string
										? F
										: string
									: string;
							};
						} & O["teams"] extends { enabled: true }
							? {
									activeTeamId: {
										type: "string";
										required: false;
									};
									activeOrganizationId: {
										type: "string";
										required: false;
										fieldName?: O["schema"] extends {
											session: { fields: { activeOrganizationId: infer F } };
										}
											? F extends string
												? F
												: string
											: string;
									};
								}
							: {
									activeOrganizationId: {
										type: "string";
										required: false;
										fieldName?: O["schema"] extends {
											session: { fields: { activeOrganizationId: infer F } };
										}
											? F extends string
												? F
												: string
											: string;
									};
								} & (O["schema"] extends {
									session: { additionalFields: infer F };
								}
									? F extends string
										? F
										: string
									: {});
					};
				};

export const role = z.string();
export const invitationStatus = z
	.enum(["pending", "accepted", "rejected", "canceled"])
	.default("pending");

export const organizationSchema = z.object({
	id: z.string().default(generateId),
	name: z.string(),
	slug: z.string(),
	logo: z.string().nullish().optional(),
	metadata: z
		.record(z.string(), z.unknown())
		.or(z.string().transform((v) => JSON.parse(v)))
		.optional(),
	createdAt: z.date(),
});

export const memberSchema = z.object({
	id: z.string().default(generateId),
	organizationId: z.string(),
	userId: z.coerce.string(),
	role,
	createdAt: z.date().default(() => new Date()),
});

export const invitationSchema = z.object({
	id: z.string().default(generateId),
	organizationId: z.string(),
	email: z.string(),
	role,
	status: invitationStatus,
	teamId: z.string().nullish(),
	inviterId: z.string(),
	expiresAt: z.date(),
	createdAt: z.date().default(() => new Date()),
});

export const teamSchema = z.object({
	id: z.string().default(generateId),
	name: z.string().min(1),
	organizationId: z.string(),
	createdAt: z.date(),
	updatedAt: z.date().optional(),
});

export const teamMemberSchema = z.object({
	id: z.string().default(generateId),
	teamId: z.string(),
	userId: z.string(),
	createdAt: z.date().default(() => new Date()),
});

export const organizationRoleSchema = z.object({
	id: z.string().default(generateId),
	organizationId: z.string(),
	role: z.string(),
	permission: z.record(z.string(), z.array(z.string())),
	createdAt: z.date().default(() => new Date()),
	updatedAt: z.date().optional(),
});

export type Organization = z.infer<typeof organizationSchema>;
export type Member = z.infer<typeof memberSchema>;
export type TeamMember = z.infer<typeof teamMemberSchema>;
export type Team = z.infer<typeof teamSchema>;
export type Invitation = z.infer<typeof invitationSchema>;
export type InvitationInput = z.input<typeof invitationSchema>;
export type MemberInput = z.input<typeof memberSchema>;
export type TeamMemberInput = z.input<typeof teamMemberSchema>;
export type OrganizationInput = z.input<typeof organizationSchema>;
export type TeamInput = z.infer<typeof teamSchema>;
export type OrganizationRole = z.infer<typeof organizationRoleSchema>;

const defaultRoles = ["admin", "member", "owner"] as const;
export const defaultRolesSchema = z.union([
	z.enum(defaultRoles),
	z.array(z.enum(defaultRoles)),
]);

type CustomRolesSchema<O> = O extends { roles: { [key: string]: any } }
	? z.ZodType<keyof O["roles"] | Array<keyof O["roles"]>>
	: typeof defaultRolesSchema;

export type InferOrganizationZodRolesFromOption<
	O extends OrganizationOptions | undefined,
> = CustomRolesSchema<O>;

export type InferOrganizationRolesFromOption<
	O extends OrganizationOptions | undefined,
> = O extends { roles: any } ? keyof O["roles"] : "admin" | "member" | "owner";

export type InvitationStatus = "pending" | "accepted" | "rejected" | "canceled";

export type InferMember<O extends OrganizationOptions> = O["teams"] extends {
	enabled: true;
}
	? {
			id: string;
			organizationId: string;
			role: InferOrganizationRolesFromOption<O>;
			createdAt: Date;
			userId: string;
			teamId?: string;
			user: {
				email: string;
				name: string;
				image?: string;
			};
		}
	: {
			id: string;
			organizationId: string;
			role: InferOrganizationRolesFromOption<O>;
			createdAt: Date;
			userId: string;
			user: {
				email: string;
				name: string;
				image?: string;
			};
		};

export type InferOrganization<
	O extends OrganizationOptions,
	isClientSide extends boolean = true,
> = Prettify<
	Organization &
		InferAdditionalFieldsFromPluginOptions<"organization", O, isClientSide>
>;

export type InferTeam<O extends OrganizationOptions> = Prettify<
	Team & InferAdditionalFieldsFromPluginOptions<"team", O>
>;

export type InferInvitation<O extends OrganizationOptions> =
	(O["teams"] extends {
		enabled: true;
	}
		? {
				id: string;
				organizationId: string;
				email: string;
				role: InferOrganizationRolesFromOption<O>;
				status: InvitationStatus;
				inviterId: string;
				expiresAt: Date;
				createdAt: Date;
				teamId?: string;
			}
		: {
				id: string;
				organizationId: string;
				email: string;
				role: InferOrganizationRolesFromOption<O>;
				status: InvitationStatus;
				inviterId: string;
				expiresAt: Date;
				createdAt: Date;
			}) &
		InferAdditionalFieldsFromPluginOptions<"invitation", O, false>;

```

--------------------------------------------------------------------------------
/docs/app/sitemap.xml:
--------------------------------------------------------------------------------

```
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<url>
<loc>https://www.better-auth.com/</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>1.00</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/introduction</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://www.better-auth.com/changelogs</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://www.better-auth.com/blog</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://www.better-auth.com/community</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/examples/next-js</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/comparison</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.64</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/installation</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.64</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/basic-usage</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.64</priority>
</url>
<url>
<loc>https://www.better-auth.com/blog/1-3</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.64</priority>
</url>
<url>
<loc>https://www.better-auth.com/blog/seed-round</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.64</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/adapters/other-relational-databases</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/concepts/cli</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/concepts/database</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/passkey</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/username</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/magic-link</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/email-otp</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/integrations/next</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/concepts/session-management</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/2fa</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/concepts/api</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/sso</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/oidc-provider</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/mcp</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/stripe</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/siwe</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.51</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/adapters/mysql</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/adapters/sqlite</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/adapters/postgresql</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/adapters/mssql</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/adapters/drizzle</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/concepts/client</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/concepts/typescript</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/concepts/hooks</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/concepts/cookies</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/concepts/email</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/generic-oauth</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/anonymous</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/phone-number</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/integrations/remix</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/integrations/nuxt</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/concepts/rate-limit</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/integrations/expo</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/bearer</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/organization</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/api-key</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/jwt</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/polar</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/one-tap</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/admin</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.41</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/guides/optimizing-for-performance</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.33</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/other-social-providers</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.33</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/adapters/prisma</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.33</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/concepts/users-accounts</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.33</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/concepts/plugins</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.33</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/email-password</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.33</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/integrations/astro</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.33</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/integrations/svelte-kit</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.33</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/concepts/oauth</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.33</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/integrations/nestjs</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.33</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/captcha</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.33</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/open-api</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.33</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/autumn</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.33</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/guides/browser-extension-guide</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.26</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/reference/options</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.26</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/zoom</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.26</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/adapters/mongo</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.26</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/apple</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.26</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/adapters/community-adapters</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.26</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/integrations/solid-start</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.26</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/integrations/nitro</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.26</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/have-i-been-pwned</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.26</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/one-time-token</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.26</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/dodopayments</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.26</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/integrations/hono</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.21</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/guides/create-a-db-adapter</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.21</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/reference/contributing</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.21</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/vk</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.21</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/discord</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.21</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/integrations/tanstack</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.21</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/integrations/elysia</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.21</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/multi-session</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.21</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/oauth-proxy</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.21</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/dub</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.21</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/integrations/fastify</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.17</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/guides/your-first-plugin</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.17</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/reference/resources</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.17</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/spotify</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.17</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/facebook</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.17</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/integrations/express</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.17</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/plugins/community-plugins</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.17</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/guides/clerk-migration-guide</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.13</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/reference/security</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.13</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/roblox</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.13</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/github</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.13</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/guides/next-auth-migration-guide</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.13</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/guides/supabase-migration-guide</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.11</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/reference/faq</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.11</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/reddit</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.11</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/google</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.11</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/gitlab</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.09</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/huggingface</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.09</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/linkedin</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.07</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/kick</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.07</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/linear</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.05</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/microsoft</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.05</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/dropbox</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.04</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/slack</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.04</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/twitter</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.04</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/notion</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.04</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/twitch</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.03</priority>
</url>
<url>
<loc>https://www.better-auth.com/docs/authentication/tiktok</loc>
<lastmod>2025-07-31T12:33:38+00:00</lastmod>
<priority>0.03</priority>
</url>
</urlset>
```
Page 29/52FirstPrevNextLast