Skip to content

Commit

Permalink
Prevent auto-connecting as anonymous (#514)
Browse files Browse the repository at this point in the history
  • Loading branch information
antoinejaussoin authored Mar 28, 2023
1 parent ba94d9b commit b31394a
Show file tree
Hide file tree
Showing 30 changed files with 220 additions and 126 deletions.
37 changes: 8 additions & 29 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ import {
getIdentityByUsername,
associateUserWithAdWordsCampaign,
TrackingInfo,
registerAnonymousUser,
} from './db/actions/users.js';
import { isLicenced } from './security/is-licenced.js';
import rateLimit from 'express-rate-limit';
Expand All @@ -71,7 +70,6 @@ import { deleteAccount } from './db/actions/delete.js';
import { noop } from 'lodash-es';
import { createDemoSession } from './db/actions/demo.js';
import cookieParser from 'cookie-parser';
import { generateUsername } from './common/random-username.js';
import { mergeAnonymous } from './db/actions/merge.js';
import aiRouter from './ai/router.js';

Expand Down Expand Up @@ -336,37 +334,18 @@ db().then(() => {

app.get('/api/me', async (req, res) => {
const user = await getUserViewFromRequest(req);
const trackingString: string = req.cookies['retro_aw'];
if (trackingString && user) {
const tracking: Partial<TrackingInfo> = JSON.parse(trackingString);
// We don't await this because we don't want to block the response
associateUserWithAdWordsCampaign(user, tracking);
}

if (user) {
res.status(200).send(user.toJson());
} else if (!config.DISABLE_ANONYMOUS_LOGIN) {
const anonUser = await registerAnonymousUser(
generateUsername() + '^' + v4(),
v4()
);
if (anonUser) {
const view = await getUserView(anonUser.id);
if (view) {
req.logIn(
{ userId: anonUser.user.id, identityId: anonUser.id },
() => {
res.status(200).send(view.toJson());
}
);
} else {
res.status(500).send('Could not get user view');
}
} else {
res.status(401).send('Not logged in');
const trackingString: string = req.cookies['retro_aw'];
if (trackingString) {
const tracking: Partial<TrackingInfo> = JSON.parse(trackingString);
// We don't await this because we don't want to block the response
associateUserWithAdWordsCampaign(user, tracking);
}

res.status(200).send(user.toJson());
} else {
res.status(403).send('Cannot login anonymously');
res.status(401).send('Not logged in');
}
});

Expand Down
3 changes: 2 additions & 1 deletion frontend/src/auth/AccountMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ const AccountMenu = () => {
logout();
setUser(null);
setMenuOpen(false);
}, [setUser]);
navigate('/');
}, [setUser, navigate]);

const handleAccount = useCallback(() => {
navigate('/account');
Expand Down
86 changes: 70 additions & 16 deletions frontend/src/auth/modal/LoginContent.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import { useContext } from 'react';
import { useCallback, useContext } from 'react';
import DialogContent from '@mui/material/DialogContent';
import { useTranslation } from 'react-i18next';
import UserContext from '../Context';
import SocialAuth from './SocialAuth';
import AccountAuth from './AccountAuth';
import useOAuthAvailabilities from '../../global/useOAuthAvailabilities';
import useBackendCapabilities from '../../global/useBackendCapabilities';
import { Alert } from '@mui/material';
import { Alert, Button } from '@mui/material';
import styled from '@emotion/styled';
import { anonymousLogin, me, updateLanguage } from 'api';
import { trackEvent } from 'track';
import { useLanguage } from 'translations';
import { Login } from '@mui/icons-material';

interface LoginContentProps {
anonymous: boolean;
onClose: () => void;
}

export default function LoginContent({ onClose }: LoginContentProps) {
export default function LoginContent({
anonymous,
onClose,
}: LoginContentProps) {
const { any } = useOAuthAvailabilities();
const { disableAnonymous, disablePasswords } = useBackendCapabilities();
const hasNoSocialMediaAuth = !any;
Expand All @@ -22,6 +30,23 @@ export default function LoginContent({ onClose }: LoginContentProps) {
const hasNoWayOtherThanAnonymous = hasNoSocialMediaAuth && disablePasswords;
const { t } = useTranslation();
const { setUser } = useContext(UserContext);
const [language] = useLanguage();

const handleAnonLogin = useCallback(async () => {
const user = await anonymousLogin('Anonymous User');
if (!user) {
return;
}
trackEvent('register/anonymous');
let updatedUser = await me();
if (updatedUser?.language === null) {
updatedUser = await updateLanguage(language.locale);
}
setUser(updatedUser);
if (onClose) {
onClose();
}
}, [setUser, onClose, language]);

if (hasNoWayOfLoggingIn) {
<Alert severity="error">{t('AuthCommon.noAuthWarning')}</Alert>;
Expand All @@ -34,19 +59,34 @@ export default function LoginContent({ onClose }: LoginContentProps) {
) : (
<>
<DialogContent>
<Container>
{!hasNoSocialMediaAuth ? (
<SocialAuth onClose={onClose} onUser={setUser} />
) : null}
{!hasNoSocialMediaAuth && !disablePasswords ? (
<Separator>
<span>{t('AuthCommon.or')}</span>
</Separator>
) : null}
{!disablePasswords ? (
<AccountAuth onClose={onClose} onUser={setUser} />
) : null}
</Container>
<Main>
<Container>
{!hasNoSocialMediaAuth ? (
<SocialAuth onClose={onClose} onUser={setUser} />
) : null}
{!hasNoSocialMediaAuth && !disablePasswords ? (
<Separator>
<span>{t('AuthCommon.or')}</span>
</Separator>
) : null}
{!disablePasswords ? (
<AccountAuth onClose={onClose} onUser={setUser} />
) : null}
</Container>
</Main>
{anonymous ? (
<Footer>
<Button
onClick={handleAnonLogin}
variant="text"
color="secondary"
startIcon={<Login />}
data-cy="login-anonymous"
>
{t('AuthCommon.skipAndAnonLogin')}
</Button>
</Footer>
) : null}
</DialogContent>
</>
)}
Expand Down Expand Up @@ -96,3 +136,17 @@ const Container = styled.div`
flex-direction: column;
}
`;

const Main = styled.div``;

const Footer = styled.div`
display: flex;
justify-content: flex-end;
@media screen and (max-width: 1000px) {
border-top: 1px solid #ccc;
justify-content: center;
margin-top: 30px;
padding-top: 30px;
}
`;
2 changes: 1 addition & 1 deletion frontend/src/auth/modal/LoginModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default function LoginModal({
onClose={handleClose}
hideBackdrop={!backdrop}
>
<LoginContent onClose={handleClose} />
<LoginContent onClose={handleClose} anonymous />
</Dialog>
);
}
14 changes: 9 additions & 5 deletions frontend/src/components/EditableLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface EditableLabelProps extends CenteredProp {
multiline?: boolean;
label?: string;
focused?: boolean;
wrap?: boolean;
onChange: (value: string) => void;
}

Expand All @@ -25,6 +26,7 @@ const EditableLabel = ({
multiline,
label,
focused,
wrap = false,
onChange,
}: EditableLabelProps) => {
const [editMode, setEditMode] = useState(false);
Expand Down Expand Up @@ -98,11 +100,11 @@ const EditableLabel = ({
<InvisibleEditIcon fontSize="inherit" />
</EditMode>
) : readOnly ? (
<ViewMode aria-label={label}>
<ViewMode wrap={wrap} aria-label={label}>
<span>{current || placeholder}</span>
</ViewMode>
) : (
<ViewMode onClick={enableEditMode}>
<ViewMode wrap={wrap} onClick={enableEditMode}>
<span aria-label={label} data-testid={label}>
{current || placeholder}
</span>
Expand All @@ -118,10 +120,12 @@ export default EditableLabel;

const LabelContainer = styled.span``;

const ViewMode = styled.span`
display: inline-block;
const ViewMode = styled.span<{ wrap: boolean }>`
display: inline-flex;
flex-wrap: nowrap;
align-items: center;
> span {
white-space: pre-wrap;
white-space: ${(props) => (props.wrap ? 'pre-wrap' : 'nowrap')};
line-height: 1.5;
}
Expand Down
34 changes: 34 additions & 0 deletions frontend/src/molecules/NameEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import UserContext from 'auth/Context';
import useUser from 'auth/useUser';
import EditableLabel from 'components/EditableLabel';
import { useSnackbar } from 'notistack';
import { useCallback, useContext } from 'react';
import { useTranslation } from 'react-i18next';
import { updateUserName } from 'views/account/api';

export function NameEditor() {
const { enqueueSnackbar } = useSnackbar();
const { t } = useTranslation();
const user = useUser();
const { setUser } = useContext(UserContext);
const handleEditName = useCallback(
async (name: string) => {
const trimmed = name.trim();
if (!trimmed.length) {
enqueueSnackbar(t('AccountPage.noEmptyNameError'), {
variant: 'warning',
});
} else {
const updatedUser = await updateUserName(name);
setUser(updatedUser);
}
},
[setUser, enqueueSnackbar, t]
);

if (!user) {
return <>Stranger</>;
}

return <EditableLabel value={user.name} onChange={handleEditName} />;
}
5 changes: 3 additions & 2 deletions frontend/src/translations/locales/ar-SA.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"title": "مرحبًا! الرجاء تسجيل الدخول للبدء."
},
"Home": {
"welcome": "مرحبا، {{name}}",
"welcome": "مرحباً،",
"anonWarning": "أنت تستخدم حساب مجهول. قد ترغب في تسجيل الدخول للتأكد من أن البيانات الخاصة بك يمكن استرجاعها عند استخدام جهاز آخر.",
"howDoesThatWork": "كيف يعمل ذلك؟",
"login": "تسجيل الدخول",
Expand Down Expand Up @@ -221,7 +221,8 @@
"ليس تماما",
"جيد",
"قوي"
]
],
"skipAndAnonLogin": "تخطي وتسجلي في مجهول"
},
"AccountLogin": {
"header": "كلمة المرور",
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/translations/locales/de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"title": "Willkommen! Bitte melden Sie sich an, um zu starten."
},
"Home": {
"welcome": "Willkommen, {{name}}",
"welcome": "Willkommen,",
"anonWarning": "Du verwendest ein anonymes Konto. Du solltest dich einloggen, um sicherzustellen, dass deine Daten abgerufen werden können, wenn du ein anderes Gerät verwendest.",
"howDoesThatWork": "Wie funktioniert das?",
"login": "Anmelden",
Expand Down Expand Up @@ -221,7 +221,8 @@
"nicht ganz",
"gut",
"stark"
]
],
"skipAndAnonLogin": "Überspringe und logge mich anonym ein"
},
"AccountLogin": {
"header": "Passwort",
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/translations/locales/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"title": "Welcome! Please login to start."
},
"Home": {
"welcome": "Welcome, {{name}}",
"welcome": "Welcome,",
"anonWarning": "You are using an Anonymous account. You might want to login to make sure your data can be retrieved when using another device.",
"howDoesThatWork": "How does that work?",
"login": "Login",
Expand Down Expand Up @@ -221,7 +221,8 @@
"not quite",
"good",
"strong"
]
],
"skipAndAnonLogin": "Skip and log me in Anonymously"
},
"AccountLogin": {
"header": "Password",
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/translations/locales/es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"title": "¡Bienvenido! Inicie sesión para empezar."
},
"Home": {
"welcome": "Bienvenido, {{name}}",
"welcome": "Bienvenido,",
"anonWarning": "Estás usando una cuenta anónima. Tal vez quieras iniciar sesión para asegurarte de que tus datos pueden ser recuperados cuando usas otro dispositivo.",
"howDoesThatWork": "¿Cómo funciona esto?",
"login": "Ingresar",
Expand Down Expand Up @@ -221,7 +221,8 @@
"no muy bien",
"bueno",
"fuerte"
]
],
"skipAndAnonLogin": "Saltar y registrarme anónimamente"
},
"AccountLogin": {
"header": "Contraseña",
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/translations/locales/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"title": "Bienvenue! Veuillez vous connecter pour commencer."
},
"Home": {
"welcome": "Bienvenue, {{name}}",
"welcome": "Bienvenue,",
"anonWarning": "Vous utilisez un compte anonyme. Connectez-vous afin que vos données puissent être récupérées lorsque vous utilisez un autre appareil.",
"howDoesThatWork": "Comment cela fonctionne-t-il ?",
"login": "Connexion",
Expand Down Expand Up @@ -221,7 +221,8 @@
"encore un effort",
"suffisant",
"excellent"
]
],
"skipAndAnonLogin": "Connectez-moi en mode anonyme à la place"
},
"AccountLogin": {
"header": "Mot de Passe",
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/translations/locales/hu-HU.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"title": ""
},
"Home": {
"welcome": "Isten hozott, {{name}}",
"welcome": "",
"anonWarning": "",
"howDoesThatWork": "Hogyan működik?",
"login": "",
Expand Down Expand Up @@ -221,7 +221,8 @@
"nem egészen",
"",
"erős"
]
],
"skipAndAnonLogin": ""
},
"AccountLogin": {
"header": "Jelszó",
Expand Down
Loading

0 comments on commit b31394a

Please sign in to comment.