[Payment due @ShridharGoel] Block sign-in for agent email accounts#91896
Conversation
When a user enters an agent email (agent_*@expensify.ai) in the login form, show an error message instead of proceeding to the magic code step. Agents can't receive emails, so the magic code flow dead-ends. This adds a client-side guard using the existing isAgentEmail() utility. Co-authored-by: Nicolás Bonet <NicolasBonet@users.noreply.github.com>
🦜 Polyglot Parrot! 🦜Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues: View the translation diffdiff --git a/src/languages/de.ts b/src/languages/de.ts
index 8a8a74285e1..33e0a05271a 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -2955,7 +2955,8 @@ ${amount} für ${merchant} – ${date}`,
phoneOrEmail: 'Telefon oder E-Mail',
error: {
invalidFormatEmailLogin: 'Die eingegebene E-Mail-Adresse ist ungültig. Bitte korrigiere das Format und versuche es erneut.',
- agentSignInBlocked: 'Bei Agentenkonten ist eine direkte Anmeldung nicht möglich. Um einen Agenten zu verwenden, melde dich mit deinem eigenen Konto an und greife über Copilot darauf zu.',
+ agentSignInBlocked:
+ 'Agent-Konten können nicht direkt verwendet werden. Um ein Agent-Konto zu nutzen, melden Sie sich mit Ihrem eigenen Konto an und greifen Sie über Copilot darauf zu.',
},
cannotGetAccountDetails: 'Kontodetails konnten nicht abgerufen werden. Bitte melde dich erneut an.',
loginForm: 'Anmeldeformular',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 5e646096d5d..a7ec746835e 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -2831,7 +2831,8 @@ ${amount} para ${merchant} - ${date}`,
phoneOrEmail: 'Número de teléfono o correo electrónico',
error: {
invalidFormatEmailLogin: 'El correo electrónico introducido no es válido. Corrígelo e inténtalo de nuevo.',
- agentSignInBlocked: 'No se puede iniciar sesión directamente con cuentas de agente. Para usar un agente, inicia sesión con tu propia cuenta y accede a través de Copilot.',
+ agentSignInBlocked:
+ 'No se puede iniciar sesión directamente en las cuentas de agente. Para usar un agente, inicia sesión con tu propia cuenta y accede a él a través de Copilot.',
},
cannotGetAccountDetails: 'No se pudieron cargar los detalles de tu cuenta. Por favor, intenta iniciar sesión de nuevo.',
loginForm: 'Formulario de inicio de sesión',
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 9fe6e1840fa..df4c85125a2 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -2962,7 +2962,8 @@ ${amount} pour ${merchant} - ${date}`,
loginForm: {
phoneOrEmail: 'Téléphone ou e-mail',
error: {
- agentSignInBlocked: 'Les comptes agents ne peuvent pas être connectés directement. Pour utiliser un agent, connectez-vous avec votre propre compte et accédez-y via Copilot.',
+ agentSignInBlocked:
+ 'Les comptes d’agent ne permettent pas de se connecter directement. Pour utiliser un agent, connectez-vous avec votre propre compte et accédez-y via Copilot.',
invalidFormatEmailLogin: 'L’adresse e-mail saisie est invalide. Veuillez corriger le format et réessayer.',
},
cannotGetAccountDetails: 'Impossible de récupérer les détails du compte. Veuillez essayer de vous reconnecter.',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index d2be1310c7c..3dc80464e65 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -2950,7 +2950,7 @@ ${amount} per ${merchant} - ${date}`,
loginForm: {
phoneOrEmail: 'Telefono o email',
error: {
- agentSignInBlocked: 'Non è possibile accedere direttamente agli account agente. Per utilizzare un agente, accedi con il tuo account e accedi tramite Copilot.',
+ agentSignInBlocked: 'Non puoi accedere direttamente agli account agente. Per usare un agente, accedi con il tuo account e raggiungilo tramite Copilot.',
invalidFormatEmailLogin: 'L’email inserita non è valida. Correggi il formato e riprova.',
},
cannotGetAccountDetails: 'Impossibile recuperare i dettagli dell’account. Prova ad accedere di nuovo.',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index a1c8bd5a217..21f4138e59c 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -2923,7 +2923,8 @@ ${date} の ${merchant} への ${amount}`,
phoneOrEmail: '電話番号またはメールアドレス',
error: {
invalidFormatEmailLogin: '入力されたメールアドレスが無効です。形式を修正して、もう一度お試しください。',
- agentSignInBlocked: 'エージェントアカウントには直接サインインできません。エージェントを使用するには、ご自身のアカウントでサインインし、コパイロット経由でアクセスしてください。',
+ agentSignInBlocked:
+ 'エージェントアカウントには直接サインインすることはできません。エージェントを利用するには、ご自身のアカウントでサインインし、Copilot 経由でアクセスしてください。',
},
cannotGetAccountDetails: 'アカウントの詳細を取得できませんでした。もう一度サインインしてください。',
loginForm: 'ログインフォーム',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 1c733e425c4..60c19f6baa0 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -2947,7 +2947,7 @@ ${amount} voor ${merchant} - ${date}`,
phoneOrEmail: 'Telefoon of e-mail',
error: {
invalidFormatEmailLogin: 'Het ingevoerde e-mailadres is ongeldig. Corrigeer de notatie en probeer het opnieuw.',
- agentSignInBlocked: 'Er kan niet rechtstreeks worden ingelogd op agentaccounts. Om een agent te gebruiken, log je in met je eigen account en open je deze via Copilot.',
+ agentSignInBlocked: 'Je kunt niet rechtstreeks inloggen op agent-accounts. Log in met je eigen account en gebruik de agent via Copilot.',
},
cannotGetAccountDetails: 'Accountgegevens konden niet worden opgehaald. Probeer opnieuw in te loggen.',
loginForm: 'Aanmeldformulier',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 73784acc791..29bfc5e9248 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -2941,7 +2941,7 @@ ${amount} dla ${merchant} - ${date}`,
phoneOrEmail: 'Telefon lub e-mail',
error: {
invalidFormatEmailLogin: 'Wprowadzony adres e-mail jest nieprawidłowy. Popraw jego format i spróbuj ponownie.',
- agentSignInBlocked: 'Nie można logować się bezpośrednio na konta agentów. Aby użyć agenta, zaloguj się na swoje konto i uzyskaj dostęp przez Copilot.',
+ agentSignInBlocked: 'Na konta agenta nie można logować się bezpośrednio. Żeby korzystać z agenta, zaloguj się na własne konto i uzyskaj do niego dostęp przez Copilota.',
},
cannotGetAccountDetails: 'Nie można pobrać szczegółów konta. Spróbuj zalogować się ponownie.',
loginForm: 'Formularz logowania',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 3af9f5de06d..df41fba7aa7 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -2941,7 +2941,7 @@ ${amount} para ${merchant} - ${date}`,
phoneOrEmail: 'Telefone ou e-mail',
error: {
invalidFormatEmailLogin: 'O e-mail inserido é inválido. Corrija o formato e tente novamente.',
- agentSignInBlocked: 'Não é possível entrar diretamente em contas de agente. Para usar um agente, entre com sua própria conta e acesse-o via Copilot.',
+ agentSignInBlocked: 'Contas de agente não podem ser acessadas diretamente. Para usar um agente, entre com a sua própria conta e acesse-o via Copilot.',
},
cannotGetAccountDetails: 'Não foi possível recuperar os detalhes da conta. Tente entrar novamente.',
loginForm: 'Formulário de login',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 4b0df8dbcb9..5907c5b7675 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -2864,10 +2864,7 @@ ${amount},商户:${merchant} - 日期:${date}`,
},
loginForm: {
phoneOrEmail: '电话或邮箱',
- error: {
- invalidFormatEmailLogin: '输入的邮箱无效。请修正格式后重试。',
- agentSignInBlocked: '无法直接登录代理账户。要使用代理,请使用您自己的账户登录,然后通过 Copilot 访问。',
- },
+ error: {invalidFormatEmailLogin: '输入的邮箱无效。请修正格式后重试。', agentSignInBlocked: '代理帐户无法直接登录。要使用代理,请先登录您自己的帐户,然后通过 Copilot 访问该代理。'},
cannotGetAccountDetails: '无法获取账户详情。请尝试重新登录。',
loginForm: '登录表单',
notYou: (user: string) => `不是 ${user} 吗?`,
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
Co-authored-by: Nicolás Bonet <NicolasBonet@users.noreply.github.com>
|
Fixed the failing Prettier check: the |
|
@MelvinBot Let's add steps to test this |
|
Here are suggested test steps for this PR: Tests
Offline testsN/A — this is a client-side validation that does not involve network requests. QA Steps
|
|
🚧 @NicolasBonet has triggered a test Expensify/App build. You can view the workflow run here. |
|
@MelvinBot apply this changes: #91896 (comment) |
Co-authored-by: NicolasBonet <NicolasBonet@users.noreply.github.com> Co-authored-by: Nicolás Bonet <NicolasBonet@users.noreply.github.com>
|
Applied the improved translations from the Polyglot Parrot comment. Updated all 9 language files (de, es, fr, it, ja, nl, pl, pt-BR, zh-hans) with the refined |
This comment has been minimized.
This comment has been minimized.
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
|
@MelvinBot let's address this lack of coverage: #91896 (comment) |
Adds two tests covering the new isAgentEmail guard in validateAndSubmitForm: one verifies the error message appears and beginSignIn is not called for agent emails, the other verifies normal emails proceed to sign-in as expected. Co-authored-by: Nicolás Bonet <NicolasBonet@users.noreply.github.com>
|
Added
Both tests pass and lint is clean. |
|
@Eskalifer1 Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
Reviewer Checklist
Screenshots/Videos |
|
@MelvinBot We should make CC: @NicolasBonet |
|
Good catch — the current regex (
|
|
@MelvinBot implement that |
Add the `i` flag to AGENT_EMAIL_REGEX so inputs like AGENT_123@EXPENSIFY.AI are also blocked. Add a test case for mixed-case agent emails. Co-authored-by: Nicolás Bonet <NicolasBonet@users.noreply.github.com>
|
Made |
|
🎯 @ShridharGoel, thanks for reviewing and testing this PR! 🎉 A payment issue will be created for your review once this PR is deployed to production. If payment is not needed (e.g., regression PR review fix etc), react with 👎 to this comment to prevent the payment issue from being created. |
|
🚧 @yuwenmemon has triggered a test Expensify/App build. You can view the workflow run here. |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/yuwenmemon in version: 9.3.90-0 🚀
Bundle Size Analysis (Sentry): |
|
🤖 Help site review: no changes required I reviewed this PR against the help site articles under What this PR changes (user-facing): a client-side guard in the sign-in form that stops AI agent emails ( Why no help site change is warranted:
What I checked
@NicolasBonet, if you'd like a help site article created anyway (e.g., a short "Why can't I sign in with an agent account?" entry in Login Troubleshooting), let me know and I'll draft it. Otherwise no further action is needed here. |
|
🚀 Deployed to production by https://github.com/lakchote in version: 9.3.90-3 🚀
|
|
🤖 Payment issue created: #92310 |





Explanation of Change
AI agent accounts (
agent_*@expensify.ai) can't receive emails, so the magic code sign-in flow dead-ends when a user enters an agent email. This PR adds a client-side guard in the login form that checks the entered email against the existingisAgentEmail()utility and shows an error message instead of proceeding to the magic code step.The error message directs users to sign in with their own account and access agents via Copilot instead.
Fixed Issues
$ https://github.com/Expensify/Expensify/issues/641772
Tests
agent_123@expensify.aiin the email field and press ContinueOffline tests
N/A
QA Steps
Same as tests
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps./** comment above it */thisproperly so there are no scoping issues (i.e. foronClick={this.submit}the methodthis.submitshould be bound tothisin the constructor)thisare necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);ifthis.submitis never passed to a component event handler likeonClick)Screenshots/Videos
MacOS: Chrome / Safari
Screen.Recording.2026-05-27.at.5.10.56.PM.mov