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¶
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¶
Get Active Organization¶
Set Active Organization¶
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¶
Cancel Invitation¶
Accept Invitation¶
Get Member Role¶
const { data: roleData } = await authClient.organization.getActiveMemberRole();
const role = roleData?.role; // "owner" | "admin" | "member"
Team Management¶
List Teams¶
Create Team¶
Set Active Team¶
List Team Members¶
Two-Factor Authentication¶
Enable 2FA¶
const { data: totpData } = await authClient.twoFactor.enable({
type: "totp"
});
// Returns QR code / secret for authenticator app
Verify 2FA¶
Disable 2FA¶
Passkeys (WebAuthn)¶
Register Passkey¶
List Passkeys¶
Delete Passkey¶
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¶
Get User¶
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: