Silgi

Better Auth

Trace Better Auth operations in silgi analytics — login flows, OAuth, session management with structured attributes.

The Better Auth integration traces every auth operation — sign-in, sign-up, OAuth flows, session management — into silgi analytics. Each operation appears as a span with user.id, session.id, auth.operation, and success/failure status. Two integration points cover both HTTP requests and programmatic API calls.

Setup

bash pnpm add silgi better-auth
bash npm install silgi better-auth
bash bun add silgi better-auth

There are two ways to trace auth operations:

  1. silgiTracing() plugin — traces HTTP-level auth requests (sign-in forms, OAuth callbacks)
  2. instrumentBetterAuth() — traces programmatic auth.api.* calls (server-side session fetches, background jobs)

Use one or both depending on your setup.

Plugin setup

Add silgiTracing() to your Better Auth plugins. Pass createAuthMiddleware from better-auth/api so the plugin can wrap the after-hook handler:

auth.ts
import {  } from 'better-auth'
import {  } from 'better-auth/api'
import {  } from 'silgi/better-auth'

export const  = ({
  : { : ..! },
  : [({  })],
})

Handler setup

In your silgi handler, set request.__silgiCtx = ctx before passing the request to Better Auth. This bridges the silgi request context to the auth plugin:

server.ts
import {  } from 'silgi'
import {  } from './auth'

const  = ()

const  = .(async ({ ,  }) => {
  ;( as any).__silgiCtx = 
  return .handler()
})

const  = .({
  : { :  },
})

export default { : .() }

request.__silgiCtx = ctx must be set before calling auth.handler(request). Without it, the plugin has no access to the request trace and spans are silently skipped.

What gets captured

Each auth operation produces a span with these attributes:

AttributeTypeDescription
auth.operationstringOperation name (signin, signup, signout, get_session, etc.)
auth.methodstringAuth method (email, oauth)
auth.providerstringOAuth provider name (google, github, etc.)
auth.successbooleanWhether the operation succeeded
user.idstringAuthenticated user's ID
user.emailstringAuthenticated user's email
session.idstringSession ID

Operation mapping

The plugin maps Better Auth URL paths to structured span names:

PathSpan nameOperation
/sign-up/emailauth.signup.emailsignup
/sign-in/emailauth.signin.emailsignin
/sign-outauth.signoutsignout
/get-sessionauth.get_sessionget_session
/update-userauth.update_userupdate_user
/delete-userauth.delete_userdelete_user
/change-passwordauth.change_passwordchange_password
/change-emailauth.change_emailchange_email
/verify-emailauth.verify_emailverify_email
/forget-passwordauth.forgot_passwordforgot_password
/reset-passwordauth.reset_passwordreset_password
/list-sessionsauth.list_sessionslist_sessions
/revoke-sessionauth.revoke_sessionrevoke_session
/sign-in/{provider}auth.oauth.initiate.{provider}oauth_initiate
/callback/{provider}auth.oauth.callback.{provider}oauth_callback

Unrecognized paths fall back to auth.{last_segment} with operation unknown.

Programmatic API tracing

For server-side calls to auth.api.* (session validation, background user management), use instrumentBetterAuth with withSilgiCtx:

auth.ts
import {  } from 'better-auth'
import {  } from 'silgi/better-auth'

export const  = (
  ({
    : { : ..! },
  }),
)

Then wrap API calls in withSilgiCtx:

server.ts
import {  } from 'silgi'
import {  } from 'silgi/better-auth'
import {  } from './auth'

const  = ()

const  = .(async ({ ,  }) => {
  return (, () => .api.getSession({ : .headers }))
})

Programmatic spans are named auth.api.{operation} (e.g. auth.api.get_session, auth.api.signin). All known API methods are mapped to their operation names automatically. Unknown methods are also traced using a camelCase-to-snake_case conversion of the method name.

Configuration

Options for the silgiTracing() plugin:

NameTypeDefaultDescription
captureInputbooleantrueCapture request body as span input
captureOutputbooleantrueCapture response data as span output
createAuthMiddlewarefunctionundefinedPass createAuthMiddleware from better-auth/api to wrap the after-hook handler

On this page