feat: add PostHog identifyUser in auth hook with Redis dedup cache#5643
feat: add PostHog identifyUser in auth hook with Redis dedup cache#56430xArshdeep merged 10 commits intomainfrom
Conversation
- Add identifyUser calls in inject-identity.ts for JWT and MCP_JWT auth modes - Implement Redis-backed dedup cache (10-min TTL) to prevent excessive PostHog API calls - Add skipDedup option to identifyUser for login/signup routes that should always fire - Update all existing identifyUser call sites to use skipDedup: true Co-Authored-By: arsh <[email protected]>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Greptile SummaryThis PR enriches PostHog person profiles for users who authenticate via CLI, SDK, or API by adding
Confidence Score: 4/5
Important Files Changed
Last reviewed commit: 69219e1 |
…k, extract helper - Replace getItem+setItemWithExpiry with atomic setItemWithExpiryNX (TOCTOU fix) - Add in-memory dedup Set fallback to limit blast radius during Redis outages - Extract fireIdentifyForUser helper to deduplicate JWT/MCP_JWT identify logic - Add setItemWithExpiryNX to keystore interface, memory store, and mock Co-Authored-By: arsh <[email protected]>
Addresses Greptile review: moves helper to plugin scope so it's allocated once per plugin registration instead of once per authenticated request. Co-Authored-By: arsh <[email protected]>
…ions Co-Authored-By: arsh <[email protected]>
…hog-identify-on-auth
… shutdown Co-Authored-By: arsh <[email protected]>
…it-manager.devin.ai/proxy/github.com/Infisical/infisical into devin/1773102788-posthog-identify-on-auth
Context
PostHog
identifyUserwas previously only called during login, signup, and OAuth callback routes. Users accessing Infisical via CLI, SDK, or API would not get their PostHog person properties enriched if those one-time calls were missed or if the person record was created with a different distinctId.This PR adds
identifyUsercalls in theinject-identity.tsauthentication hook for JWT and MCP_JWT auth modes. This ensures that every authenticated request — regardless of client (CLI, SDK, API, UI) — enriches the PostHog person with basic properties (email, username, userId).To avoid excessive PostHog API calls, a Redis-backed dedup cache (10-minute TTL) is used via an atomic
SET NX EXoperation (setItemWithExpiryNX). If a user was already identified within the last 10 minutes, the call is skipped. All existing login/signup/OAuthidentifyUsercall sites are updated to useskipDedup: trueso they always fire (they carry richer properties like firstName, lastName, superAdmin).Note: This is the first of two PRs. A separate PR in the CLI repo (Infisical/cli#146) addresses the anonymous
distinctIdissue where the CLI generates random IDs instead of using the user's email/username.Updates since last revision
Addressed all code review feedback across four rounds of Greptile review and Devin Review:
getItem+setItemWithExpirywith a single atomicsetItemWithExpiryNX(RedisSET key value EX ttl NX). Only the first caller within the TTL window proceeds; all others getnullback and skip the identify call.Set<string>fallback in thecatchblock. If Redis is unreachable, the in-memory set absorbs the burst (entries auto-expire viasetTimeoutmatching the cache TTL) instead of flooding PostHog with identify calls on every request.fireIdentifyForUser(user)helper ininject-identity.ts, called in bothJWTandMCP_JWTcases.setItemWithExpiryNXtoTKeyStoreFactorytype, implementation, mock (e2e-test/mocks/keystore.ts), and in-memory store (keystore/memory.ts).fireIdentifyForUserto plugin scope (outsideonRequesthook, insideinjectIdentity) so the function is created once per plugin registration instead of per-request.postHog.identify(): Wrapped thepostHog.identify()call in atry/catchto prevent unhandled promise rejections from propagating when call sites usevoid.distinctIdin error log messages for both the dedup cache andpostHog.identify()failures, improving production debuggability..unref()to the fallbacksetTimeoutinidentifyUser, consistent with the existingidentifyIdentitypattern. Without.unref(), active timers during Redis outages would keep the Node.js event loop alive and prevent graceful shutdown for up to 600 seconds.Steps to verify the change
infisical secrets)email,username, anduserIdproperties set$identifyevents are sent (dedup cache working)identifyUseron every occurrence (skipDedup)Important review notes
identifyUseris nowasyncbut all call sites usevoid(fire-and-forget) — this is intentional for telemetryemail,username,userId— the richer property set (firstName, lastName, superAdmin, etc.) is still set by login/signup routes viaskipDedup: trueSetis per-process (not shared across instances). This is intentional — it's a last-resort rate limiter, not a primary dedup mechanismsetTimeoutentries accumulate one per distinct user during a Redis outage; review whether this is acceptable under expected concurrent user countsdistinctIdis derived fromuser.username ?? user.email. In the unlikely edge case where these values collide across accounts, dedup could suppress identification for up to 10 minutes (telemetry-accuracy impact only, not a security concern)Human review checklist
setItemWithExpiryNXreturn type ("OK" | null) is handled correctly —nullmeans key already existed (skip identify)setTimeoutaccumulation ininMemoryIdentifyDedupwon't cause issues during extended Redis outages with many distinct usersidentifyUserbecoming async doesn't break anything (all call sites usevoidso should be safe)fireIdentifyForUserhelper correctly extracts the shared logic without altering behaviorpostHog.identify()is appropriate and won't mask legitimate errorsdistinctId(email/username) in error logs doesn't introduce PII logging concerns in your environmenttimer.unref()is called on bothidentifyUserandidentifyIdentityfallback timers for graceful shutdown consistencyType
Checklist
Link to Devin Session: https://app.devin.ai/sessions/6cf49960d59a4f7b974a7fe4861d6206
Requested by: @0xArshdeep