Better Auth

identifyUser

The core building block — extract safe user fields, mask emails, and capture plugin data (organizations, roles, 2FA) from a Better Auth session.

identifyUser is the core building block. Take a RequestLogger and a Better Auth session, extract safe fields, and call log.set(). Returns true if the user was identified, false otherwise.

server/api/checkout.post.ts
import { identifyUser } from 'evlog/better-auth'

const session = await auth.api.getSession({ headers: event.headers })
if (session) {
  const identified = identifyUser(log, session)
  if (identified) {
    log.set({ subscription: 'premium' })
  }
}
Safe by default. Only whitelisted fields are extracted — passwords, tokens, and secrets are never written to the logger.

Options

OptionTypeDefaultDescription
maskEmailbooleanfalseMask emails as h***@example.com.
sessionbooleantrueInclude session metadata (session.id, session.expiresAt, session.ipAddress, session.userAgent).
fieldsstring[]['id', 'name', 'email', 'image', 'emailVerified', 'createdAt']User fields to extract.
extend(session) => Record<string, unknown>undefinedAdd custom fields from Better Auth plugins (organizations, roles, etc.).
server/api/checkout.post.ts
identifyUser(log, session, {
  maskEmail: true,
  fields: ['id', 'name'],
  session: false,
})

Mask emails

Emails are PII. In environments where the audit/log trail might be reviewed by support or third parties, mask them:

identifyUser(log, session, { maskEmail: true })

The maskEmail helper is also exported on its own:

import { maskEmail } from 'evlog/better-auth'

maskEmail('hugo@example.com') // 'h***@example.com'

Capture plugin fields

Better Auth ships with plugins (organizations, 2FA, roles, admin) that add fields to the session. Use extend to surface them on the wide event:

server/middleware/auth-identify.ts
import { createAuthMiddleware } from 'evlog/better-auth'

const identify = createAuthMiddleware(auth, {
  extend: (session) => ({
    organization: session.user.activeOrganization,
    role: session.user.role,
  }),
})

Wide event with plugin fields:

Wide Event
{
  "userId": "QBX9tPjJQExWawAbNll75",
  "user": { "id": "QBX9tPjJQExWawAbNll75", "name": "Hugo Richard" },
  "organization": { "id": "org_42", "name": "Acme" },
  "role": "admin"
}
Keep extend deterministic — it runs on every request. Avoid heavy computations or extra database calls inside it; query the data Better Auth already loaded into the session.

Captured fields

FieldSourceDescription
userIdsession.user.idTop-level user ID (used by PostHog adapter as distinct_id).
user.idsession.user.idUser ID.
user.namesession.user.nameDisplay name.
user.emailsession.user.emailEmail (maskable with maskEmail: true).
user.imagesession.user.imageAvatar URL.
user.emailVerifiedsession.user.emailVerifiedEmail verification status.
user.createdAtsession.user.createdAtAccount creation date (ISO string).
session.idsession.session.idSession ID.
session.expiresAtsession.session.expiresAtSession expiry (ISO string).
session.ipAddresssession.session.ipAddressClient IP from the session.
session.userAgentsession.session.userAgentUser agent string from the session.
session.createdAtsession.session.createdAtSession creation date (ISO string).
auth.resolvedInMeasuredSession resolution time in ms.
auth.identifiedComputedWhether the request was identified.