@kubiks/otel-better-auth
One-line OpenTelemetry instrumentation for Better Auth, tracing every sign-in, sign-up, OAuth callback, and session operation.
Repository Health
Technical Analysis
@kubiks/otel-better-auth wraps a Better Auth instance with OpenTelemetry tracing so every authentication flow becomes observable without touching the rest of your auth configuration. A single instrumentBetterAuth() call patches all api.* methods (session lookups, email/OAuth sign-in and sign-up, password resets, account linking, and more) to emit spans carrying operation type, auth method, provider, user ID, session ID, and success/error status.
A companion otelPlugin() hooks into Better Auth’s HTTP request/response lifecycle to also trace OAuth callbacks and redirect-based flows that don’t go through the direct API surface, closing the gap that method-wrapping alone can’t cover. The package ships as a small, dependency-free TypeScript module built specifically to sit alongside Better Auth’s plugin system, part of Kubiks’ broader otel-* family of one-line OpenTelemetry integrations for the TypeScript ecosystem.
What You Get
instrumentBetterAuth(auth)— wraps an existing Better Auth instance in place, auto-detecting and tracing allapi.*methods including ones not explicitly listed in its metadata mapotelPlugin()— a Better Auth plugin added via thepluginsarray that traces HTTP-level operations (OAuth initiation/callback, sign-in, sign-out, password/email flows) using request path matching- Rich span attributes on every trace —
auth.operation,auth.method,auth.provider,auth.success,auth.error,user.id,user.email,session.id - Idempotent instrumentation — a hidden flag prevents double-wrapping if
instrumentBetterAuth()is called more than once on the same instance - Custom tracer support — pass a
tracerNameor a pre-builtTracerinstance instead of using the defaulttrace.getTracer()tracer
Common Use Cases
- Adding distributed tracing to a Next.js or Node app’s Better Auth setup to see auth latency and failures inside an existing OpenTelemetry pipeline
- Debugging intermittent OAuth sign-in failures by inspecting
auth.http.oauth.callback.{provider}spans for the specific provider and error message - Correlating authentication spans with downstream request traces (API calls, DB queries) to see the full request lifecycle from login to session-backed action
- Monitoring password-reset and email-verification funnels in production dashboards built on OpenTelemetry-compatible backends (e.g. Honeycomb, Grafana Tempo, Datadog)
Under The Hood
Architecture
The package is a single-file (src/index.ts) instrumentation layer with two independent entry points that compose rather than depend on each other. instrumentBetterAuth() walks the live auth.api object at runtime, wrapping each method with wrapAuthMethod(), which starts a span, runs the original call inside context.with(), and inspects the result shape ({data, error} from Better Fetch vs. a direct object) to pull out user/session IDs before closing the span. A parallel otelPlugin() registers as a genuine Better Auth plugin with onRequest/onResponse/after hooks, using a module-level Map<string, Span> keyed by method:url to bridge span state across the request/response lifecycle for HTTP-only flows like OAuth redirects that never touch auth.api directly. An idempotency flag (__kubiksOtelBetterAuthInstrumented) guards against double-wrapping if instrumentBetterAuth() runs twice, and ensureOtelPlugin() checks for an existing id: "otel" plugin before injecting its own, so the two entry points can be combined without duplicate spans.
Tech Stack
Written in TypeScript, compiled with tsc to ESM output (dist/index.js plus a dist/types declaration folder) and published under the standard npm exports/main/types fields. It depends on @opentelemetry/api as a peer dependency (>=1.9.0 <2.0.0) so consumers control the actual SDK/exporter, and on better-auth (>=0.1.0) purely for its createAuthMiddleware helper and plugin/type surface. The package lives inside a pnpm + Turborepo monorepo (kubiks-inc/otel) alongside ten sibling @kubiks/otel-* integrations, sharing root-level tsconfig.json, turbo.json, and a GitHub Actions release pipeline that runs type-checking and unit tests per package.
Code Quality
Tests live in src/index.test.ts (Vitest, ~250 lines) and cover the two main entry points with mocked Better Auth server objects, asserting span-wrapping behavior, the instrumented flag, and non-object/null inputs. There is no dedicated linter config visible in the package itself, but the repo runs tsc --noEmit type-checking and vitest --run in CI (.github/workflows/type-check.yml, unit-test.yml) on every change. strict mode is disabled in the package’s own tsconfig.json even though the monorepo root enables strict: true, and several internal helpers use any casts (AnyAuth = Auth<any>) to work around Better Auth’s complex generic types — a pragmatic but real type-safety compromise. Error handling in the HTTP-hook path is defensive: onRequest/onResponse/after hooks each wrap their body in try/catch and log to console.error rather than throwing, so a tracing bug can’t take down an auth request.
What Makes It Unique
Most OpenTelemetry auto-instrumentation packages patch a library’s internals or monkey-patch require(); this package instead uses Better Auth’s own plugin system (otelPlugin()) combined with direct method-table wrapping, which means it stays correct across Better Auth’s frequent API surface changes as long as the plugin contract holds. The dual-path design — API-method wrapping for direct calls, HTTP-hook tracing for redirect-based OAuth flows the API surface can’t see — is a deliberate answer to a real gap: OAuth callbacks return an HTTP redirect, not a value the caller awaits, so span attribution has to happen out-of-band via the request/response Map. It is a narrow, single-purpose package rather than a general-purpose tracing framework, which is standard for the instrumentation-package category it sits in.