Skip to content

Commit 05a87c4

Browse files
committed
feat(backend): include user email in session creation and update auth utilities
1 parent ac80173 commit 05a87c4

File tree

6 files changed

+39
-13
lines changed

6 files changed

+39
-13
lines changed

packages/backend/src/auth/controllers/auth.controller.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ class AuthController {
116116
: await this.signup(gUser, gRefreshToken);
117117

118118
const sUserId = supertokens.convertToRecipeUserId(cUserId);
119-
await Session.createNewSession(req, res, "public", sUserId);
119+
await Session.createNewSession(req, res, "public", sUserId, {
120+
email,
121+
});
120122

121123
const result: Result_Auth_Compass = {
122124
cUserId,

packages/backend/src/common/types/supertokens.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ export interface SupertokensAccessTokenPayload {
99
parentRefreshTokenHash1: string | null;
1010
antiCsrfToken: string | null;
1111
iss: string;
12+
email?: string;
1213
}

packages/web/src/auth/UserContext.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,50 @@
1-
import React, {
1+
import { usePostHog } from "posthog-js/react";
2+
import {
23
ReactNode,
34
createContext,
45
useContext,
6+
useEffect,
57
useLayoutEffect,
68
useState,
79
} from "react";
810
import { AbsoluteOverflowLoader } from "@web/components/AbsoluteOverflowLoader";
9-
import { getUserId } from "./auth.util";
11+
import { getUserEmail, getUserId } from "./auth.util";
1012

1113
const UserContext = createContext<
1214
{ isLoadingUser: boolean; userId: string } | undefined
1315
>(undefined);
1416

1517
export const UserProvider = ({ children }: { children: ReactNode }) => {
1618
const [userId, setUserId] = useState<string | null>(null);
19+
const [email, setEmail] = useState<string | null>(null);
1720
const [isLoadingUser, setIsLoadingUser] = useState(false);
21+
const posthog = usePostHog();
1822

1923
useLayoutEffect(() => {
20-
const fetchUserId = async () => {
24+
const fetchUserData = async () => {
2125
try {
2226
const uid = await getUserId();
27+
const userEmail = await getUserEmail();
28+
console.log("userEmail", userEmail);
2329
setUserId(uid);
30+
setEmail(userEmail);
2431
} catch (e) {
2532
console.error("Failed to get user because:", e);
2633
} finally {
2734
setIsLoadingUser(false);
2835
}
2936
};
3037

31-
void fetchUserId();
38+
void fetchUserData();
3239
}, []);
3340

41+
// Identify user in PostHog when userId and email are available
42+
useEffect(() => {
43+
if (userId && email && posthog) {
44+
posthog.identify(email, { email, userId });
45+
}
46+
}, [userId, email, posthog]);
47+
3448
if (isLoadingUser || userId === null) {
3549
return <AbsoluteOverflowLoader />;
3650
}

packages/web/src/auth/auth.util.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Session from "supertokens-auth-react/recipe/session";
22

33
interface AccessTokenPayload {
44
sub: string;
5+
email?: string;
56
}
67

78
export const getUserId = async () => {
@@ -10,3 +11,17 @@ export const getUserId = async () => {
1011
const userId = accessTokenPayload["sub"];
1112
return userId;
1213
};
14+
15+
/**
16+
* Get the user's email from the session access token payload
17+
*/
18+
export const getUserEmail = async (): Promise<string | null> => {
19+
try {
20+
const accessTokenPayload =
21+
(await Session.getAccessTokenPayloadSecurely()) as AccessTokenPayload;
22+
return accessTokenPayload.email || null;
23+
} catch (error) {
24+
console.error("Failed to get user email:", error);
25+
return null;
26+
}
27+
};

packages/web/src/views/Login/Login.tsx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { usePostHog } from "posthog-js/react";
21
import React, { useEffect, useRef, useState } from "react";
32
import { useNavigate } from "react-router-dom";
43
import { useAuthCheck } from "@web/auth/useAuthCheck";
@@ -31,7 +30,6 @@ type FlowStep = "initial" | "checkingWaitlist" | "waitlistStatusKnown";
3130
export const LoginView = () => {
3231
const emailInputRef = useRef<HTMLInputElement>(null);
3332
const navigate = useNavigate();
34-
const posthog = usePostHog();
3533

3634
// New state for the waitlist check flow
3735
const [emailInput, setEmailInput] = useState("");
@@ -71,15 +69,10 @@ export const LoginView = () => {
7169
loading: isGoogleLoginLoading,
7270
} = useGoogleLogin({
7371
onSuccess: async (code) => {
74-
const response = await AuthApi.loginOrSignup(code);
72+
await AuthApi.loginOrSignup(code);
7573

7674
// Set flag to track that user has completed signup
7775
markSignupCompleted();
78-
79-
// Identify user in PostHog with email as distinct ID
80-
if (response.email && posthog) {
81-
posthog.identify(response.email, { email: response.email });
82-
}
8376
},
8477
onError: (error) => {
8578
console.error(error);

packages/web/src/views/Onboarding/steps/events/SomedaySandbox/sandbox.util.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ jest.mock("@core/types/event.types", () => ({
2323

2424
jest.mock("@web/auth/auth.util", () => ({
2525
getUserId: jest.fn().mockResolvedValue("test-user-id"),
26+
getUserEmail: jest.fn().mockResolvedValue("test@example.com"),
2627
}));
2728

2829
jest.mock("@web/common/styles/theme.util", () => ({

0 commit comments

Comments
 (0)