> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kubiks.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Better Auth

> OpenTelemetry instrumentation for Better Auth authentication flows

## Overview

`@kubiks/otel-better-auth` provides comprehensive OpenTelemetry instrumentation for [Better Auth](https://better-auth.com/). Get complete authentication observability across all auth flows with a single line of code—OAuth, email/password, sessions, account management, and more.

<Frame>
  <img src="https://mintcdn.com/kubiks-fb8cce26/aWqkpsvTx2hc17v8/images/otel/otel-better-auth-trace.png?fit=max&auto=format&n=aWqkpsvTx2hc17v8&q=85&s=f45f460411f0e3f534fde11715327318" alt="Better Auth Trace Visualization" width="3379" height="2386" data-path="images/otel/otel-better-auth-trace.png" />
</Frame>

<Note>
  Visualize your authentication flows with detailed span information including operation type, user IDs, session IDs, auth methods, and success/failure status.
</Note>

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @kubiks/otel-better-auth
  ```

  ```bash pnpm theme={null}
  pnpm add @kubiks/otel-better-auth
  ```

  ```bash yarn theme={null}
  yarn add @kubiks/otel-better-auth
  ```
</CodeGroup>

<Warning>
  **Peer Dependencies:** `@opentelemetry/api` >= 1.9.0, `better-auth` >= 1.0.0
</Warning>

## Quick Start

```typescript theme={null}
import { betterAuth } from "better-auth";
import { instrumentBetterAuth } from "@kubiks/otel-better-auth";

export const auth = instrumentBetterAuth(
  betterAuth({
    database: db,
    // ... your Better Auth config
  }),
);
```

<Tip>
  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.
</Tip>

## Traced Operations

<Tabs>
  <Tab title="Authentication">
    **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
  </Tab>

  <Tab title="Session Management">
    **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
  </Tab>

  <Tab title="Account Management">
    **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
  </Tab>

  <Tab title="Password & Email">
    **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
  </Tab>
</Tabs>

## Span Attributes

Each span includes rich context about the authentication operation:

| Attribute        | Description                      | Example                                      |
| ---------------- | -------------------------------- | -------------------------------------------- |
| `auth.operation` | Type of operation                | `signin`, `signup`, `get_session`, `signout` |
| `auth.method`    | Auth method                      | `email`, `oauth`                             |
| `auth.provider`  | OAuth provider (when applicable) | `google`, `github`                           |
| `auth.success`   | Operation success                | `true`, `false`                              |
| `auth.error`     | Error message (when failed)      | `Invalid credentials`                        |
| `user.id`        | User ID (when available)         | `user_123456`                                |
| `user.email`     | User email (when available)      | `user@example.com`                           |
| `session.id`     | Session ID (when available)      | `session_abcdef`                             |

<Info>
  User IDs and session IDs are captured where applicable to help with debugging and monitoring authentication flows.
</Info>

## Configuration

You can optionally customize the instrumentation:

```typescript theme={null}
instrumentBetterAuth(authClient, {
  tracerName: "my-app", // Custom tracer name
  tracer: customTracer, // Custom tracer instance
});
```

## Usage Examples

### Basic Setup (Next.js App Router)

<Steps>
  <Step title="Configure Better Auth">
    ```typescript lib/auth.ts theme={null}
    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,
          },
        },
      }),
    );
    ```
  </Step>

  <Step title="Create Auth Handler">
    ```typescript app/api/auth/[...all]/route.ts theme={null}
    import { auth } from "@/lib/auth";

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

  <Step title="Use in Your App">
    All authentication operations are now automatically traced!

    ```typescript theme={null}
    // Sign in
    await auth.api.signInEmail({
      email: "user@example.com",
      password: "password",
    });

    // Get session
    const session = await auth.api.getSession();
    ```
  </Step>
</Steps>

### OAuth Authentication

<CodeGroup>
  ```typescript GitHub OAuth theme={null}
  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
  ```

  ```typescript Google OAuth theme={null}
  export const auth = instrumentBetterAuth(
    betterAuth({
      database: db,
      socialProviders: {
        google: {
          clientId: process.env.GOOGLE_CLIENT_ID,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET,
          scopes: ["email", "profile"],
        },
      },
    }),
  );

  // OAuth initiation is traced
  // Span: auth.http.oauth.initiate.google
  ```

  ```typescript Multiple Providers theme={null}
  export const auth = instrumentBetterAuth(
    betterAuth({
      database: db,
      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,
        },
        discord: {
          clientId: process.env.DISCORD_CLIENT_ID,
          clientSecret: process.env.DISCORD_CLIENT_SECRET,
        },
      },
    }),
  );
  ```
</CodeGroup>

### Email/Password Authentication

<CodeGroup>
  ```typescript Sign Up theme={null}
  "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;
  }
  ```

  ```typescript Sign In theme={null}
  "use server";

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

  export async function signIn(email: string, password: string) {
    const result = await auth.api.signInEmail({
      email,
      password,
    });

    // Traced as: auth.http.signin.email
    // Includes: user.id, user.email, auth.success, session.id

    return result;
  }
  ```

  ```typescript Sign Out theme={null}
  "use server";

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

  export async function signOut() {
    await auth.api.signOut();
    
    // Traced as: auth.http.signout
    // Includes: session.id, auth.success
  }
  ```
</CodeGroup>

### Session Management

<CodeGroup>
  ```typescript Get Session theme={null}
  "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;
  }
  ```

  ```typescript List Sessions theme={null}
  "use server";

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

  export async function listUserSessions() {
    const sessions = await auth.api.listSessions();
    
    // Traced as: auth.api.list_sessions
    // Includes: user.id, auth.success
    
    return sessions;
  }
  ```

  ```typescript Revoke Session theme={null}
  "use server";

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

  export async function revokeSession(sessionId: string) {
    await auth.api.revokeSession({ sessionId });
    
    // Traced as: auth.api.revoke_session
    // Includes: session.id, auth.success
  }
  ```

  ```typescript Revoke Other Sessions theme={null}
  "use server";

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

  export async function revokeOtherSessions() {
    await auth.api.revokeOtherSessions();
    
    // Traced as: auth.api.revoke_other_sessions
    // Includes: user.id, session.id (current), auth.success
  }
  ```
</CodeGroup>

### Account Management

<CodeGroup>
  ```typescript Update Profile theme={null}
  "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;
  }
  ```

  ```typescript Link Social Account theme={null}
  "use server";

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

  export async function linkGithubAccount() {
    const result = await auth.api.linkSocialAccount({
      provider: "github",
    });

    // Traced as: auth.api.link_social_account
    // Includes: user.id, auth.provider (github), auth.success

    return result;
  }
  ```

  ```typescript Delete Account theme={null}
  "use server";

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

  export async function deleteAccount() {
    await auth.api.deleteUser();

    // Traced as: auth.api.delete_user
    // Includes: user.id, auth.success
  }
  ```
</CodeGroup>

### Password Management

<CodeGroup>
  ```typescript Change Password theme={null}
  "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;
  }
  ```

  ```typescript Reset Password theme={null}
  "use server";

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

  export async function requestPasswordReset(email: string) {
    await auth.api.forgetPassword({ email });

    // Traced as: auth.api.forget_password
    // Includes: user.email, auth.success
  }

  export async function resetPassword(token: string, newPassword: string) {
    const result = await auth.api.resetPassword({
      token,
      newPassword,
    });

    // Traced as: auth.api.reset_password
    // Includes: user.id, auth.success

    return result;
  }
  ```
</CodeGroup>

### Email Management

<CodeGroup>
  ```typescript Verify Email theme={null}
  "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;
  }
  ```

  ```typescript Change Email theme={null}
  "use server";

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

  export async function changeEmail(newEmail: string) {
    const result = await auth.api.changeEmail({
      newEmail,
    });

    // Traced as: auth.api.change_email
    // Includes: user.id, user.email (new), auth.success

    return result;
  }
  ```
</CodeGroup>

## Complete Integration Example

Here's a full example of Better Auth with OpenTelemetry in a Next.js application:

```typescript lib/auth.ts theme={null}
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",
  }
);
```

```typescript app/api/auth/[...all]/route.ts theme={null}
import { auth } from "@/lib/auth";

export const { GET, POST } = auth.handler;
```

```typescript app/actions/auth.ts theme={null}
"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

<AccordionGroup>
  <Accordion title="Use Server Actions">
    Always use Server Actions for authentication operations in Next.js:

    ```typescript theme={null}
    "use server";

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

    export async function signIn(email: string, password: string) {
      return await auth.api.signInEmail({ email, password });
    }
    ```
  </Accordion>

  <Accordion title="Handle Errors Gracefully">
    Check for errors and provide appropriate feedback:

    ```typescript theme={null}
    const result = await auth.api.signInEmail({ email, password });

    if (result.error) {
      return { error: result.error.message };
    }

    return { success: true };
    ```
  </Accordion>

  <Accordion title="Verify Email Addresses">
    Always enable email verification for production:

    ```typescript theme={null}
    betterAuth({
      emailAndPassword: {
        enabled: true,
        requireEmailVerification: true,
      },
    })
    ```
  </Accordion>

  <Accordion title="Use Custom Tracer Names">
    Use descriptive tracer names for easier debugging:

    ```typescript theme={null}
    instrumentBetterAuth(auth, {
      tracerName: "my-app-auth",
    });
    ```
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Spans Not Appearing">
    Ensure OpenTelemetry is properly initialized before creating the auth instance:

    ```typescript theme={null}
    import { NodeSDK } from "@opentelemetry/sdk-node";

    const sdk = new NodeSDK({
      // ... configuration
    });

    sdk.start();

    // Then create auth instance
    export const auth = instrumentBetterAuth(betterAuth({ ... }));
    ```
  </Accordion>

  <Accordion title="Missing User IDs">
    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.
  </Accordion>

  <Accordion title="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`).
  </Accordion>
</AccordionGroup>

## Resources

<CardGroup cols={2}>
  <Card title="Better Auth Documentation" icon="book" href="https://better-auth.com/docs">
    Learn more about Better Auth
  </Card>

  <Card title="GitHub Repository" icon="github" href="https://github.com/kubiks-inc/otel/tree/main/packages/otel-better-auth">
    View source code and examples
  </Card>

  <Card title="npm Package" icon="box" href="https://www.npmjs.com/package/@kubiks/otel-better-auth">
    View package on npm
  </Card>

  <Card title="Report Issues" icon="circle-exclamation" href="https://github.com/kubiks-inc/otel/issues">
    Found a bug? Let us know!
  </Card>
</CardGroup>

## License

MIT
