Feat: Initial cross-domain identify setup (#2533)

* feat: initial setup

* fix: factor out

* chore: lint

* fix: xss vuln

* feat: set up properly

* fix: lint

* fix: key

* fix: keys, cleanup
This commit is contained in:
matt
2025-11-18 13:30:37 -05:00
committed by GitHub
parent d77c5d8e3e
commit 16b9ae4700
3 changed files with 64 additions and 2 deletions

View File

@@ -1,7 +1,11 @@
import { User } from '@/lib/api';
import { useTenant } from '@/lib/atoms';
import useApiMeta from '@/pages/auth/hooks/use-api-meta';
import { useAnalytics } from '@/hooks/use-analytics';
import {
POSTHOG_DISTINCT_ID_LOCAL_STORAGE_KEY,
POSTHOG_SESSION_ID_LOCAL_STORAGE_KEY,
useAnalytics,
} from '@/hooks/use-analytics';
import React, { PropsWithChildren, useEffect, useMemo } from 'react';
interface AnalyticsProviderProps {
@@ -19,6 +23,13 @@ const AnalyticsProvider: React.FC<
const { identify } = useAnalytics();
const config = useMemo(() => {
if (import.meta.env.DEV) {
return {
apiKey: import.meta.env.VITE_PUBLIC_POSTHOG_KEY,
apiHost: import.meta.env.VITE_PUBLIC_POSTHOG_HOST,
};
}
return meta.data?.posthog;
}, [meta]);
@@ -38,12 +49,32 @@ const AnalyticsProvider: React.FC<
return;
}
let bootstrapConfig = '';
if (localStorage) {
const distinctId = localStorage.getItem(
POSTHOG_DISTINCT_ID_LOCAL_STORAGE_KEY,
);
const sessionId = localStorage.getItem(
POSTHOG_SESSION_ID_LOCAL_STORAGE_KEY,
);
if (distinctId && sessionId) {
bootstrapConfig = `bootstrap: ${JSON.stringify({
sessionID: sessionId,
distinctID: distinctId,
})},`;
}
}
console.log('Initializing Analytics, opt out in settings.');
setLoaded(true);
const posthogScript = `
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys onSessionId".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
posthog.init('${config.apiKey}',{
api_host:'${config.apiHost}',
${bootstrapConfig}
session_recording: {
maskAllInputs: true,
maskTextSelector: "*"

View File

@@ -15,6 +15,9 @@ interface UseAnalyticsReturn {
isAvailable: boolean;
}
export const POSTHOG_DISTINCT_ID_LOCAL_STORAGE_KEY = 'ph__distinct_id';
export const POSTHOG_SESSION_ID_LOCAL_STORAGE_KEY = 'ph__session_id';
/**
* Hook for PostHog analytics integration
* Provides a clean interface for tracking events and identifying users
@@ -61,6 +64,11 @@ export function useAnalytics(): UseAnalyticsReturn {
} catch (error) {
console.warn('Analytics identify failed:', error);
}
// important: clear out the distinct_id and session_id from local storage
// after identifying the user so we don't re-bootstrap over and over
localStorage.removeItem(POSTHOG_DISTINCT_ID_LOCAL_STORAGE_KEY);
localStorage.removeItem(POSTHOG_SESSION_ID_LOCAL_STORAGE_KEY);
},
[isAvailable],
);

View File

@@ -2,18 +2,41 @@ import { Link, useNavigate } from 'react-router-dom';
import { UserRegisterForm } from './components/user-register-form';
import { useMutation } from '@tanstack/react-query';
import api, { UserRegisterRequest } from '@/lib/api';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { useApiError } from '@/lib/hooks';
import useApiMeta from '../hooks/use-api-meta';
import { Loading } from '@/components/ui/loading';
import { GithubLogin, GoogleLogin, OrContinueWith } from '../login';
import useErrorParam from '../hooks/use-error-param';
import React from 'react';
import {
POSTHOG_DISTINCT_ID_LOCAL_STORAGE_KEY,
POSTHOG_SESSION_ID_LOCAL_STORAGE_KEY,
} from '@/hooks/use-analytics';
export default function Register() {
useErrorParam();
const meta = useApiMeta();
// allows for cross-domain tracking with PostHog
// see: https://posthog.com/tutorials/cross-domain-tracking
// for setup instructions and more details
// important: we need to set these in local storage from here,
// because once we redirect after the user signs up, we lose the hash params
const hashParams = new URLSearchParams(window.location.hash.substring(1));
const distinctId = hashParams.get('distinct_id');
const sessionId = hashParams.get('session_id');
useEffect(() => {
if (distinctId) {
localStorage.setItem(POSTHOG_DISTINCT_ID_LOCAL_STORAGE_KEY, distinctId);
}
if (sessionId) {
localStorage.setItem(POSTHOG_SESSION_ID_LOCAL_STORAGE_KEY, sessionId);
}
}, [distinctId, sessionId]);
if (meta.isLoading) {
return <Loading />;
}