lowercase user and invitation emails (#12130)

### Solution

> After discussion with charles & weiko, we chose the long term
solution.
> 
> Fix FE to request checkUserExists resolver with lowercased emails
> Add a decorator on User (and AppToken for invitation), to lowercase
email at user (appToken) creation. ⚠️ It works for TypeOrm .save method
only (there is no user email update in codebase, but in future it
could..)
> Add email lowercasing logic in external auth controller
> Fix FE to request sendInvitations resolver with lowercased emails
> Add migration command to lowercase all existing user emails and
invitation emails

> For other BE resolvers, we let them permissive. For example, if you
made a request on CheckUserExists resolver with uppercased email, you
will not found any user. We will not transform input before checking for
existence.

[link to comment
](https://github.com/twentyhq/twenty/pull/12130#discussion_r2098062093)

### Test 🚧 
- sign-in and up from main subdomain and workspace sub domain > Google
Auth (lowercased email) ✔️ | Microsoft Auth (uppercased email ✔️ &
lowercased email) | LoginPassword (uppercased email ✔️& lowercased
email✔️)
- invite flow with uppercased and lowercased ✔️
- migration command + sign-in ( former uppercased microsoft email ✔️) /
sign-up ( former uppercased invited email ✔️)

closes https://github.com/twentyhq/private-issues/issues/278, closes
https://github.com/twentyhq/private-issues/issues/275, closes
https://github.com/twentyhq/private-issues/issues/279
This commit is contained in:
Etienne
2025-05-21 11:06:29 +02:00
committed by GitHub
parent 6ff5a5bafa
commit 7461b7ac58
11 changed files with 161 additions and 11 deletions

View File

@ -9,10 +9,10 @@ import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { TextInput } from '@/ui/input/components/TextInput';
import { sanitizeEmailList } from '@/workspace/utils/sanitizeEmailList';
import { useLingui } from '@lingui/react/macro';
import { useCreateWorkspaceInvitation } from '../../workspace-invitation/hooks/useCreateWorkspaceInvitation';
import { isDefined } from 'twenty-shared/utils';
import { Button } from 'twenty-ui/input';
import { IconSend } from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { useCreateWorkspaceInvitation } from '../../workspace-invitation/hooks/useCreateWorkspaceInvitation';
const StyledContainer = styled.div`
display: flex;

View File

@ -21,4 +21,11 @@ describe('sanitizeEmailList', () => {
'toto@toto.com',
]);
});
it('should lowercase emails', () => {
expect(sanitizeEmailList(['TOTO@toto.com', 'TOTO2@toto.com'])).toEqual([
'toto@toto.com',
'toto2@toto.com',
]);
});
});

View File

@ -2,7 +2,7 @@ export const sanitizeEmailList = (emailList: string[]): string[] => {
return Array.from(
new Set(
emailList
.map((email) => email.trim())
.map((email) => email.trim().toLowerCase())
.filter((email) => email.length > 0),
),
);