Overview
@kubiks/otel-better-auth provides comprehensive OpenTelemetry instrumentation for Better Auth . Get complete authentication observability across all auth flows with a single line of code—OAuth, email/password, sessions, account management, and more.
Visualize your authentication flows with detailed span information including operation type, user IDs, session IDs, auth methods, and success/failure status.
Installation
npm install @kubiks/otel-better-auth
Peer Dependencies: @opentelemetry/api >= 1.9.0, better-auth >= 1.0.0
Quick Start
import { betterAuth } from "better-auth" ;
import { instrumentBetterAuth } from "@kubiks/otel-better-auth" ;
export const auth = instrumentBetterAuth (
betterAuth ({
database: db ,
// ... your Better Auth config
}),
);
Instrumenting Better Auth is just a single call—wrap the instance you already create and every API method invocation is traced automatically. Keep the rest of your configuration unchanged.
Traced Operations
Authentication
Session Management
Account Management
Password & Email
Sign In & Sign Up:
auth.http.oauth.callback.{provider} - OAuth callback with user ID ✅
auth.http.signin.email - Email signin with user ID
auth.http.signup.email - Email signup with user ID
auth.http.oauth.initiate.{provider} - OAuth initiation
auth.http.signout - User signout
auth.http.get_session - Get session
Session Operations:
auth.api.get_session - Get current session with user ID and session ID
auth.api.list_sessions - List all sessions
auth.api.revoke_session - Revoke a session
auth.api.revoke_sessions - Revoke multiple sessions
auth.api.revoke_other_sessions - Revoke all other sessions
Account Operations:
auth.api.link_social_account - Link social account
auth.api.unlink_account - Unlink account
auth.api.list_user_accounts - List user accounts
auth.api.update_user - Update user profile
auth.api.delete_user - Delete user account
Password Management:
auth.api.change_password - Change password
auth.api.set_password - Set password
auth.api.forget_password - Forgot password request
auth.api.reset_password - Reset password
Email Management:
auth.api.change_email - Change email
auth.api.verify_email - Verify email with user ID
auth.api.send_verification_email - Send verification email
Span Attributes
Each span includes rich context about the authentication operation:
Attribute Description Example auth.operationType of operation signin, signup, get_session, signoutauth.methodAuth method email, oauthauth.providerOAuth provider (when applicable) google, githubauth.successOperation success true, falseauth.errorError message (when failed) Invalid credentialsuser.idUser ID (when available) user_123456user.emailUser email (when available) [email protected] session.idSession ID (when available) session_abcdef
User IDs and session IDs are captured where applicable to help with debugging and monitoring authentication flows.
Configuration
You can optionally customize the instrumentation:
instrumentBetterAuth ( authClient , {
tracerName: "my-app" , // Custom tracer name
tracer: customTracer , // Custom tracer instance
});
Usage Examples
Basic Setup (Next.js App Router)
Configure Better Auth
import { betterAuth } from "better-auth" ;
import { drizzleAdapter } from "better-auth/adapters/drizzle" ;
import { instrumentBetterAuth } from "@kubiks/otel-better-auth" ;
import { db } from "./db" ;
export const auth = instrumentBetterAuth (
betterAuth ({
baseURL: process . env . BETTER_AUTH_URL ,
database: drizzleAdapter ( db , { provider: "pg" }),
socialProviders: {
github: {
clientId: process . env . GITHUB_CLIENT_ID ,
clientSecret: process . env . GITHUB_CLIENT_SECRET ,
},
google: {
clientId: process . env . GOOGLE_CLIENT_ID ,
clientSecret: process . env . GOOGLE_CLIENT_SECRET ,
},
},
}),
);
Create Auth Handler
app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth" ;
export const { GET , POST } = auth . handler ;
Use in Your App
All authentication operations are now automatically traced! // Sign in
await auth . api . signInEmail ({
email: "[email protected] " ,
password: "password" ,
});
// Get session
const session = await auth . api . getSession ();
OAuth Authentication
GitHub OAuth
Google OAuth
Multiple Providers
export const auth = instrumentBetterAuth (
betterAuth ({
database: db ,
socialProviders: {
github: {
clientId: process . env . GITHUB_CLIENT_ID ,
clientSecret: process . env . GITHUB_CLIENT_SECRET ,
},
},
}),
);
// OAuth callback is automatically traced with provider name
// Span: auth.http.oauth.callback.github
Email/Password Authentication
"use server" ;
import { auth } from "@/lib/auth" ;
export async function signUp ( email : string , password : string , name : string ) {
const result = await auth . api . signUpEmail ({
email ,
password ,
name ,
});
// Traced as: auth.http.signup.email
// Includes: user.id, user.email, auth.success
return result ;
}
Session Management
Get Session
List Sessions
Revoke Session
Revoke Other Sessions
"use server" ;
import { auth } from "@/lib/auth" ;
export async function getCurrentSession () {
const session = await auth . api . getSession ();
// Traced as: auth.api.get_session
// Includes: user.id, session.id, auth.success
return session ;
}
Account Management
Update Profile
Link Social Account
Delete Account
"use server" ;
import { auth } from "@/lib/auth" ;
export async function updateProfile ( name : string , image ?: string ) {
const result = await auth . api . updateUser ({
name ,
image ,
});
// Traced as: auth.api.update_user
// Includes: user.id, auth.success
return result ;
}
Password Management
Change Password
Reset Password
"use server" ;
import { auth } from "@/lib/auth" ;
export async function changePassword (
currentPassword : string ,
newPassword : string
) {
const result = await auth . api . changePassword ({
currentPassword ,
newPassword ,
});
// Traced as: auth.api.change_password
// Includes: user.id, auth.success
return result ;
}
Email Management
"use server" ;
import { auth } from "@/lib/auth" ;
export async function verifyEmail ( token : string ) {
const result = await auth . api . verifyEmail ({ token });
// Traced as: auth.api.verify_email
// Includes: user.id, user.email, auth.success
return result ;
}
Complete Integration Example
Here’s a full example of Better Auth with OpenTelemetry in a Next.js application:
import { betterAuth } from "better-auth" ;
import { drizzleAdapter } from "better-auth/adapters/drizzle" ;
import { instrumentBetterAuth } from "@kubiks/otel-better-auth" ;
import { db } from "./db" ;
export const auth = instrumentBetterAuth (
betterAuth ({
baseURL: process . env . BETTER_AUTH_URL ! ,
database: drizzleAdapter ( db , { provider: "pg" }),
emailAndPassword: {
enabled: true ,
requireEmailVerification: true ,
},
socialProviders: {
github: {
clientId: process . env . GITHUB_CLIENT_ID ! ,
clientSecret: process . env . GITHUB_CLIENT_SECRET ! ,
},
google: {
clientId: process . env . GOOGLE_CLIENT_ID ! ,
clientSecret: process . env . GOOGLE_CLIENT_SECRET ! ,
},
},
session: {
expiresIn: 60 * 60 * 24 * 7 , // 1 week
updateAge: 60 * 60 * 24 , // 1 day
},
}),
{
tracerName: "my-app-auth" ,
}
);
app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth" ;
export const { GET , POST } = auth . handler ;
"use server" ;
import { auth } from "@/lib/auth" ;
import { redirect } from "next/navigation" ;
export async function signInWithEmail ( email : string , password : string ) {
const result = await auth . api . signInEmail ({
email ,
password ,
});
if ( ! result . error ) {
redirect ( "/dashboard" );
}
return result ;
}
export async function signUpWithEmail (
email : string ,
password : string ,
name : string
) {
const result = await auth . api . signUpEmail ({
email ,
password ,
name ,
});
if ( ! result . error ) {
redirect ( "/verify-email" );
}
return result ;
}
export async function signOut () {
await auth . api . signOut ();
redirect ( "/" );
}
Best Practices
Always use Server Actions for authentication operations in Next.js: "use server" ;
import { auth } from "@/lib/auth" ;
export async function signIn ( email : string , password : string ) {
return await auth . api . signInEmail ({ email , password });
}
Check for errors and provide appropriate feedback: const result = await auth . api . signInEmail ({ email , password });
if ( result . error ) {
return { error: result . error . message };
}
return { success: true };
Always enable email verification for production: betterAuth ({
emailAndPassword: {
enabled: true ,
requireEmailVerification: true ,
},
})
Use descriptive tracer names for easier debugging: instrumentBetterAuth ( auth , {
tracerName: "my-app-auth" ,
});
Troubleshooting
Ensure OpenTelemetry is properly initialized before creating the auth instance: import { NodeSDK } from "@opentelemetry/sdk-node" ;
const sdk = new NodeSDK ({
// ... configuration
});
sdk . start ();
// Then create auth instance
export const auth = instrumentBetterAuth ( betterAuth ({ ... }));
User IDs are only captured for operations where the user is authenticated. For sign-in and sign-up, the user ID is captured after successful authentication.
OAuth Provider Not Traced
Make sure you’re using the correct provider name in your Better Auth configuration. The provider name must match exactly (e.g., github, not GitHub).
Resources
License
MIT