Relationships
#1475 oauth_sign_in telemetry records provider as the literal ":id" for every event
Opened by keeb · 7/29/2026· Shipped 8/2/2026
Every oauth_sign_in telemetry event ever emitted records the provider as the literal string :id instead of google/github/discord/microsoft. The provider dimension on OAuth sign-in is therefore unusable, and has been since the event was introduced.
Cause
lib/auth.ts, in the hooks.after middleware:
} else if (ctx.path.startsWith("/callback/") && newSession) {
const provider = ctx.path.split("/callback/")[1];Better Auth registers that endpoint as createAuthEndpoint("/callback/:id", ...) (better-auth/dist/api/routes/callback.mjs), so ctx.path is the unresolved route pattern, not the concrete request path. "/callback/:id".split("/callback/")[1] is ":id", which is truthy, so the guard passes and the bad value is tracked.
The sibling branch for oauth_redirect is correct because it reads ctx.body.provider (a real value supplied by the client) rather than parsing the path.
Evidence (ClickHouse, swamp.events)
| event | provider | count | first seen | last seen |
|---|---|---|---|---|
| oauth_redirect | 535 | 2026-02-27 | 2026-07-29 | |
| oauth_redirect | github | 393 | 2026-02-27 | 2026-07-29 |
| oauth_redirect | discord | 54 | 2026-02-27 | 2026-07-27 |
| oauth_redirect | microsoft | 25 | 2026-03-06 | 2026-07-29 |
| oauth_sign_in | :id |
791 | 2026-02-27 | 2026-07-29 |
791 of 791 oauth_sign_in events carry :id. Zero carry a real provider.
Second, related defect
swamp.events has a materialized column:
oauth_provider LowCardinality(String) MATERIALIZED JSONExtractString(properties, 'oauth_provider')Nothing writes an oauth_provider property — the code writes provider. That column is populated on 0 of 54,754,417 rows. It is either dead weight or was intended to read provider; worth resolving alongside the fix so the two do not drift again.
Impact
- No provider breakdown for successful OAuth sign-ins. "Which IdP do operatives actually complete signup with?" is unanswerable from the ledger, even though the funnel's first half (
oauth_redirect) has clean provider data. Comparing the two halves to find a provider-specific failure — exactly the analysis that would have surfaced swamp-club#1459 sooner — is impossible. - Go-forward only. Historical events cannot be repaired: the provider was never captured, and
distinct_idalone does not recover which provider was used.
Suggested fix
Read the resolved param rather than parsing the pattern — ctx.params?.id — and treat a missing or :id-shaped value as unknown instead of tracking it, so a future Better Auth routing change fails loudly rather than silently poisoning the dimension again. A regression test should assert the tracked provider for a concrete callback path; none currently does.
Provenance
Found while tracing michael's session for swamp-club#1459 (the /choose-username SSR 500). Unrelated to that fix and deliberately kept out of it.
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.