Skip to content

Commit

Permalink
Merge pull request #602 from audioverse-org/feat/cookie-user-id
Browse files Browse the repository at this point in the history
feat: add user_id cookie
  • Loading branch information
alangumer authored Dec 4, 2024
2 parents 097ce7d + 2574453 commit 275f903
Show file tree
Hide file tree
Showing 12 changed files with 87 additions and 42 deletions.
2 changes: 1 addition & 1 deletion next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ module.exports = withBundleAnalyzer(
has: [
{
type: 'cookie',
key: 'avSession',
key: 'session_token',
value: undefined,
},
{
Expand Down
2 changes: 1 addition & 1 deletion src/components/HOCs/withAuthGuard.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ describe('withAuthGuard', () => {
});

it('displays content on successful social login', async () => {
Cookies.get = jest.fn().mockReturnValue({ avSession: 'abc123' });
Cookies.get = jest.fn().mockReturnValue({ session_token: 'abc123' });

const { getByText, queryByText } = await render();

Expand Down
28 changes: 15 additions & 13 deletions src/components/molecules/socialLogin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { FormattedMessage, useIntl } from 'react-intl';

import { useRegisterSocialMutation } from '~containers/account/__generated__/register';
import { FACEBOOK_APP_ID, GOOGLE_CLIENT_ID } from '~lib/constants';
import { setSessionToken } from '~lib/cookies';
import { setSessionTokenAndUserId } from '~lib/cookies';
import useDidUnmount from '~lib/useDidUnmount';
import { UserSocialServiceName } from '~src/__generated__/graphql';
import { analytics } from '~src/lib/analytics';
Expand Down Expand Up @@ -39,27 +39,29 @@ export default function SocialLogin({
const { mutate: mutateSocial, isSuccess: isSuccessSocial } =
useRegisterSocialMutation({
onSuccess: async (response, variables) => {
const errors = response?.loginSocial.errors || [];
const token = response?.loginSocial.authenticatedUser?.sessionToken;

if (token && !errors.length) {
setSessionToken(token);
const user = response?.loginSocial.authenticatedUser?.user;
analytics.identify(user?.id + '', {
firstName: user?.givenName,
lastName: user?.surname,
email: user?.email,
const errors = response.loginSocial.errors || [];
const authenticatedUser = response.loginSocial.authenticatedUser;

if (authenticatedUser && !errors.length) {
setSessionTokenAndUserId(
authenticatedUser.sessionToken,
authenticatedUser.user.id.toString(),
);
analytics.identify(authenticatedUser.user.id + '', {
firstName: authenticatedUser.user.givenName,
lastName: authenticatedUser.user.surname,
email: authenticatedUser.user.email,
source: 'Login',
});
if (response?.loginSocial.isNewUser) {
gtmPushEvent('sign_up', {
sign_up_method: variables?.socialName,
user_id: user?.id,
user_id: authenticatedUser.user.id,
});
} else {
gtmPushEvent('sign_in', {
sign_in_method: variables?.socialName,
user_id: user?.id,
user_id: authenticatedUser.user.id,
});
}
onSuccess ? onSuccess() : await queryClient.invalidateQueries();
Expand Down
13 changes: 9 additions & 4 deletions src/components/organisms/navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import DownloadAppButton from '~components/molecules/downloadAppButton';
import LanguageButton from '~components/molecules/languageButton';
import NavItem from '~components/molecules/navItem';
import Header from '~components/organisms/header';
import { getSessionToken, setSessionToken } from '~lib/cookies';
import {
getSessionToken,
getUserId,
setSessionTokenAndUserId,
} from '~lib/cookies';
import root from '~lib/routes';
import useLanguageRoute from '~lib/useLanguageRoute';
import { INavigationItem, useNavigationItems } from '~lib/useNavigationItems';
Expand All @@ -32,6 +36,7 @@ const Navigation = ({ onExit }: { onExit: () => void }): JSX.Element => {
const router = useRouter();
const [submenu, setSubmenu] = useState('');
const sessionToken = getSessionToken();
const userId = getUserId();

useEffect(() => {
const onRouteChange = (url: string) => {
Expand All @@ -48,10 +53,10 @@ const Navigation = ({ onExit }: { onExit: () => void }): JSX.Element => {
}, []);

useEffect(() => {
if (sessionToken) {
setSessionToken(sessionToken);
if (sessionToken && userId) {
setSessionTokenAndUserId(sessionToken, userId);
}
}, [router.asPath, sessionToken]);
}, [router.asPath, sessionToken, userId]);

const { user } = useIsAuthenticated();

Expand Down
4 changes: 2 additions & 2 deletions src/components/organisms/profileForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Checkbox from '~components/molecules/form/checkbox';
import Input from '~components/molecules/form/input';
import Modal from '~components/organisms/modal';
import { refetchUserQueries, resetUserQueries } from '~src/lib/api/login';
import { clearSessionToken } from '~src/lib/cookies';
import { clearSessionTokenAndUserId } from '~src/lib/cookies';
import root from '~src/lib/routes';
import useLanguageRoute from '~src/lib/useLanguageRoute';

Expand Down Expand Up @@ -83,7 +83,7 @@ export default function ProfileForm(): JSX.Element {

const { isPending, mutate: deleteAccountMutate } = useDeleteAccountMutation({
onSuccess: async () => {
clearSessionToken();
clearSessionTokenAndUserId();
resetUserQueries(queryClient);
router.push(root.lang(languageRoute).discover.get());
},
Expand Down
24 changes: 17 additions & 7 deletions src/components/organisms/registerForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { FormattedMessage, useIntl } from 'react-intl';
import Button from '~components/molecules/button';
import Input from '~components/molecules/form/input';
import { useRegisterMutation } from '~containers/account/__generated__/register';
import { setSessionToken } from '~lib/cookies';
import { setSessionTokenAndUserId } from '~lib/cookies';
import { gtmPushEvent } from '~src/utils/gtm';

import { analytics } from '../../lib/analytics';
Expand Down Expand Up @@ -33,19 +33,29 @@ function RegisterForm({ showLogin, onSuccess }: Props): JSX.Element {
const intl = useIntl();

useEffect(() => {
if (dataRegister?.signup.errors.length) {
setErrors(dataRegister?.signup.errors.map((e) => e.message));
} else if (dataRegister?.signup.authenticatedUser?.sessionToken) {
setSessionToken(dataRegister?.signup.authenticatedUser?.sessionToken);
analytics.identify(dataRegister?.signup.authenticatedUser?.user.id + '', {
if (!dataRegister) {
return;
}

const {
signup: { errors, authenticatedUser },
} = dataRegister;
if (errors.length) {
setErrors(errors.map((e) => e.message));
} else if (authenticatedUser) {
setSessionTokenAndUserId(
authenticatedUser.sessionToken,
authenticatedUser.user.id.toString(),
);
analytics.identify(authenticatedUser.user.id + '', {
firstName: firstName,
lastName: lastName,
email: email,
source: 'Sign up',
});
gtmPushEvent('sign_up', {
sign_up_method: 'email',
user_id: dataRegister.signup.authenticatedUser.user.id,
user_id: authenticatedUser.user.id,
});

onSuccess();
Expand Down
20 changes: 18 additions & 2 deletions src/containers/account/register.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,19 @@ describe('register page', () => {
it('renders social login success', async () => {
const { getByText } = await renderPage();

when(fetchApi)
.calledWith(RegisterSocialDocument, expect.anything())
.mockResolvedValue({
loginSocial: {
authenticatedUser: {
sessionToken: 'the_token',
user: {
id: 1,
},
},
},
});

await userEvent.click(await screen.findByText('Sign up with Facebook'));

await waitFor(() => {
Expand Down Expand Up @@ -224,6 +237,9 @@ describe('register page', () => {
loginSocial: {
authenticatedUser: {
sessionToken: 'the_token',
user: {
id: 1,
},
},
},
});
Expand All @@ -233,7 +249,7 @@ describe('register page', () => {
await userEvent.click(await screen.findByText('Sign up with Facebook'));

await waitFor(() => {
expect(Cookie.set).toBeCalledWith('avSession', 'the_token', {
expect(Cookie.set).toBeCalledWith('session_token', 'the_token', {
expires: 14,
});
});
Expand Down Expand Up @@ -273,7 +289,7 @@ describe('register page', () => {
});

it('does not display form if user logged in', async () => {
Cookie.get = jest.fn().mockReturnValue({ avSession: 'abc123' });
Cookie.get = jest.fn().mockReturnValue({ session_token: 'abc123' });

const { queryByPlaceholderText } = await renderPage();

Expand Down
2 changes: 1 addition & 1 deletion src/lib/api/fetchApi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const setRequest = (cookie = '') => {
describe('fetchApi', () => {
it('uses saved request', async () => {
mockFetchResponse();
setRequest('avSession=the_token');
setRequest('session_token=the_token');

await fetchApi(noopQuery);

Expand Down
7 changes: 5 additions & 2 deletions src/lib/api/login.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { QueryClient } from '@tanstack/react-query';

import { setSessionToken } from '~lib/cookies';
import { setSessionTokenAndUserId } from '~lib/cookies';
import { gtmPushEvent } from '~src/utils/gtm';

import { analytics } from '../analytics';
Expand Down Expand Up @@ -42,7 +42,10 @@ export async function login(email: string, password: string): Promise<true> {
login: { authenticatedUser, errors },
} = await _login({ email, password });
if (authenticatedUser) {
setSessionToken(authenticatedUser.sessionToken);
setSessionTokenAndUserId(
authenticatedUser.sessionToken,
authenticatedUser.user.id.toString(),
);
const user = authenticatedUser.user;
analytics.identify(user.id + '', {
firstName: user.givenName,
Expand Down
4 changes: 2 additions & 2 deletions src/lib/api/useLogout.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { useQueryClient } from '@tanstack/react-query';

import { clearSessionToken } from '~lib/cookies';
import { clearSessionTokenAndUserId } from '~lib/cookies';
import isServerSide from '~lib/isServerSide';

import { resetUserQueries } from './login';

export function useLogout(): Promise<void> {
const queryClient = useQueryClient();

clearSessionToken();
clearSessionTokenAndUserId();

if (!isServerSide()) {
window.Beacon && window.Beacon('logout');
Expand Down
21 changes: 15 additions & 6 deletions src/lib/cookies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@ import cookie from 'cookie';
import { IncomingMessage } from 'http';
import JSCookie from 'js-cookie';

const SESSION_KEY = 'avSession';
const SESSION_TOKEN_KEY = 'session_token';
const USER_ID_KEY = 'user_id';
const LANGUAGE_KEY = 'lang';

export function getSessionToken(
req: IncomingMessage | null = null,
): string | undefined {
return getCookies(req)[SESSION_KEY];
return getCookies(req)[SESSION_TOKEN_KEY];
}

export function getUserId(
req: IncomingMessage | null = null,
): string | undefined {
return getCookies(req)[USER_ID_KEY];
}

export function getLanguageId(
Expand All @@ -17,16 +24,18 @@ export function getLanguageId(
return getCookies(req)[LANGUAGE_KEY];
}

export function setSessionToken(token: string): void {
JSCookie.set(SESSION_KEY, token, { expires: 14 });
export function setSessionTokenAndUserId(token: string, userId: string): void {
JSCookie.set(SESSION_TOKEN_KEY, token, { expires: 14 });
JSCookie.set(USER_ID_KEY, userId, { expires: 14 });
}

export function setLanguageId(lang: string): void {
JSCookie.set(LANGUAGE_KEY, lang, { expires: 365 });
}

export function clearSessionToken(): void {
JSCookie.remove(SESSION_KEY);
export function clearSessionTokenAndUserId(): void {
JSCookie.remove(SESSION_TOKEN_KEY);
JSCookie.remove(USER_ID_KEY);
}

function getCookies(req: IncomingMessage | null): {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/test/loadAuthGuardData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { fetchApi } from '~lib/api/fetchApi';
import { GetIsAuthenticatedDocument } from '~lib/hooks/__generated__/useIsAuthenticated';

export function loadAuthGuardData(email: any = 'the_email'): void {
Cookie.get = jest.fn().mockReturnValue({ avSession: 'abc123' });
Cookie.get = jest.fn().mockReturnValue({ session_token: 'abc123' });

when(fetchApi)
.calledWith(GetIsAuthenticatedDocument, expect.anything())
Expand Down

0 comments on commit 275f903

Please sign in to comment.