Skip to content

BetterAuth Integration

Authentication via BetterAuth with plugins for organizations, 2FA, passkeys, and Stripe.

Client Configuration

lib/auth-client.ts

import { createAuthClient } from "better-auth/react";
import { 
  inferAdditionalFields,
  adminClient,
  twoFactorClient,
  organizationClient,
  passkeyClient,
  jwtClient,
  stripeClient,
  phoneNumberClient,
  emailOTPClient
} from "better-auth/client/plugins";

export const authClient = createAuthClient({
  baseURL: process.env.NEXT_PUBLIC_AUTH_API_URL || "http://localhost:8787",
  fetchOptions: {
    credentials: "include" // Send cookies
  },
  plugins: [
    inferAdditionalFields({
      user: {
        position: { type: "string" },
        tutorialProgress: { type: "json" }
      }
    }),
    adminClient(),
    twoFactorClient(),
    organizationClient({ teams: { enabled: true } }),
    passkeyClient(),
    jwtClient(),
    stripeClient({ subscription: true }),
    phoneNumberClient(),
    emailOTPClient()
  ]
});

Session Management

Get Session

const { data: session, isLoading } = authClient.useSession();

// Session structure
session?.user?.id          // User ID
session?.user?.email       // Email
session?.user?.name        // Display name
session?.user?.image       // Avatar URL
session?.user?.position    // Custom field
session?.session?.id       // Session ID
session?.session?.activeTeamId // Active team

Sign In

await authClient.signIn.email({
  email: "[email protected]",
  password: "password"
});

Sign Up

await authClient.signUp.email({
  email: "[email protected]",
  password: "password",
  name: "User Name"
});

Sign Out

await authClient.signOut();

JWT Tokens

For API authentication:

const { data: tokenData } = await authClient.token();
const jwt = tokenData?.token;

// Use in API calls
fetch(url, {
  headers: {
    Authorization: `Bearer ${jwt}`
  }
});

Organization Management

List Organizations

const orgs = authClient.useListOrganizations();

Get Active Organization

const activeOrg = authClient.useActiveOrganization();

Set Active Organization

await authClient.organization.setActive({ 
  organizationSlug: "my-org" 
});

Create Organization

const { data: org } = await authClient.organization.create({
  name: "My Organization",
  slug: "my-org"
});

Get Full Organization

const { data: fullOrg } = await authClient.organization.getFullOrganization();
// Includes metadata like serverHostURL

Organization Members

List Members

const { data: members } = await authClient.organization.listMembers({
  organizationId: orgId,
  limit: 100,
  offset: 0,
  sortBy: "createdAt",
  sortDirection: "desc"
});

Invite Member

await authClient.organization.inviteMember({
  organizationId,
  email: "[email protected]",
  role: "member", // "owner" | "admin" | "member"
  teamIds: ["team-123"] // Optional
});

List Invitations

const { data: invites } = await authClient.organization.listInvitations({
  organizationId
});

Cancel Invitation

await authClient.organization.cancelInvitation({
  invitationId
});

Accept Invitation

await authClient.organization.acceptInvitation({
  invitationId
});

Get Member Role

const { data: roleData } = await authClient.organization.getActiveMemberRole();
const role = roleData?.role; // "owner" | "admin" | "member"

Team Management

List Teams

const { data: teams } = await authClient.organization.listTeams({
  organizationId
});

Create Team

await authClient.organization.createTeam({
  organizationId,
  name: "Varsity Football"
});

Set Active Team

await authClient.organization.setActiveTeam({
  teamId: "team-123"
});

List Team Members

const { data: teamMembers } = await authClient.organization.listTeamMembers({
  teamId
});

Two-Factor Authentication

Enable 2FA

const { data: totpData } = await authClient.twoFactor.enable({
  type: "totp"
});
// Returns QR code / secret for authenticator app

Verify 2FA

await authClient.twoFactor.verify({
  code: "123456"
});

Disable 2FA

await authClient.twoFactor.disable();

Passkeys (WebAuthn)

Register Passkey

await authClient.passkey.register();
// Browser prompts for biometric

List Passkeys

const { data: passkeys } = await authClient.passkey.list();

Delete Passkey

await authClient.passkey.delete({
  passkeyId
});

Email OTP

Send OTP

await authClient.emailOTP.sendOTP({
  email: "[email protected]",
  type: "sign-in" // or "email-verification"
});

Verify OTP

await authClient.emailOTP.verifyOTP({
  email: "[email protected]",
  code: "123456"
});

Admin Operations

List Users

const { data: users } = await authClient.admin.listUsers({
  limit: 100,
  offset: 0
});

Get User

const { data: user } = await authClient.admin.getUser({
  userId
});

Stripe Integration

BetterAuth Stripe plugin handles subscription management:

// Create checkout session
await authClient.stripe.createCheckoutSession({
  organizationId,
  priceId: "price_xxx"
});

// Get billing portal URL
const { data: portal } = await authClient.stripe.createBillingPortalSession({
  organizationId
});

Error Handling

try {
  await authClient.signIn.email({ email, password });
} catch (error) {
  if (error.code === "INVALID_CREDENTIALS") {
    // Handle invalid login
  }
  if (error.code === "EMAIL_NOT_VERIFIED") {
    // Handle unverified email
  }
}

Middleware Integration

proxy.ts checks session for route protection:

import { betterFetch } from "@better-fetch/fetch";

const session = await betterFetch<Session>(
  `${AUTH_URL}/api/auth/get-session`,
  {
    headers: {
      cookie: request.headers.get("cookie") || ""
    }
  }
);