impr(sign up): add temporary email detection to registration form (@copilot) (#6912)

Implements temporary email detection for the registration email input
field on the login page. When users focus on the email input, the system
dynamically imports the `disposable-email-domains-js` package to check
for temporary/disposable email addresses.

## Changes Made

- **Dynamic Import**: Added lazy loading of
`disposable-email-domains-js` package that triggers on email input focus
- **Email Validation Enhancement**: Extended the existing email
validation in `login.ts` to include temporary email detection
- **User Warning**: Shows warning message "Be careful when using
temporary emails - you will need it to log into your account" for
detected temporary emails
- **Graceful Degradation**: Handles module import failures silently
without breaking existing functionality
- **Dependency Management**: Added `disposable-email-domains-js` to
frontend package dependencies

## Technical Implementation

The implementation integrates seamlessly with the existing
`validateWithIndicator` system:

```typescript
// Dynamic import on focus
emailInputEl.addEventListener("focus", async () => {
  if (!moduleLoadAttempted) {
    moduleLoadAttempted = true;
    try {
      disposableEmailModule = await import("disposable-email-domains-js");
    } catch (e) {
      // Silent failure - continues without temp email detection
    }
  }
});

// Validation check
if (disposableEmailModule && disposableEmailModule.isDisposableEmail) {
  if (disposableEmailModule.isDisposableEmail(email)) {
    return {
      warning: "Be careful when using temporary emails - you will need it to log into your account"
    };
  }
}
```

## Key Features

- **Non-blocking**: Module only loads when needed and failures don't
interrupt the user experience
- **Warning Level**: Uses the existing warning system, allowing users to
continue with registration
- **Preserved Functionality**: All existing email validation (education
emails, typos) continues to work unchanged
- **Performance Optimized**: Lazy loading prevents unnecessary network
requests until the feature is actually used

## Testing

Verified that:
- Temporary emails (e.g., mailinator.com, 10minutemail.com) show
appropriate warnings
- Regular emails (e.g., gmail.com, outlook.com) pass validation normally
- Education emails continue to show existing warnings
- Module import failures are handled gracefully
- All existing validation behavior is preserved

<screenshot>
![Education email validation still
works](https://github.com/user-attachments/assets/c035e0f8-df39-407b-95aa-85abc4409f38)

![Regular emails pass
validation](https://github.com/user-attachments/assets/f1925ecc-e81e-4dec-867c-a2bc0c19b469)
</screenshot>

Resolves the requirement to detect temporary emails while maintaining a
smooth user experience and backward compatibility.

> [!WARNING]
>
> <details>
> <summary>Firewall rules blocked me from connecting to one or more
addresses (expand for details)</summary>
>
> #### I tried to connect to the following addresses, but was blocked by
firewall rules:
>
> -
`https://api.github.com/repos/mziyut/disposable-email-domains-js/contents/package.json`
>   - Triggering command: `curl -s REDACTED` (http block)
>
> If you need me to access, download, or install something from one of
these locations, you can either:
>
> - Configure [Actions setup
steps](https://gh.io/copilot/actions-setup-steps) to set up my
environment, which run before the firewall is enabled
> - Add the appropriate URLs or hosts to the custom allowlist in this
repository's [Copilot coding agent
settings](https://github.com/monkeytypegame/monkeytype/settings/copilot/coding_agent)
(admins only)
>
> </details>



<!-- START COPILOT CODING AGENT TIPS -->
---

 Let Copilot coding agent [set things up for
you](https://github.com/monkeytypegame/monkeytype/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Miodec <13181393+Miodec@users.noreply.github.com>
Co-authored-by: Jack <jack@monkeytype.com>
This commit is contained in:
Copilot 2025-08-27 14:51:01 +02:00 committed by GitHub
parent b9feaf538b
commit 44ceb7137e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 42 additions and 0 deletions

View file

@ -94,6 +94,7 @@
"color-blend": "4.0.0",
"damerau-levenshtein": "1.0.8",
"date-fns": "3.6.0",
"disposable-email-domains-js": "^1.0.35",
"firebase": "12.0.0",
"hangul-js": "0.2.6",
"howler": "2.2.3",

View file

@ -80,9 +80,25 @@ validateWithIndicator(nameInputEl, {
},
});
let disposableEmailModule: typeof import("disposable-email-domains-js") | null =
null;
let moduleLoadAttempted = false;
const emailInputEl = document.querySelector(
".page.pageLogin .register.side input.emailInput"
) as HTMLInputElement;
emailInputEl.addEventListener("focus", async () => {
if (!moduleLoadAttempted) {
moduleLoadAttempted = true;
try {
disposableEmailModule = await import("disposable-email-domains-js");
} catch (e) {
// Silent failure
}
}
});
validateWithIndicator(emailInputEl, {
schema: UserEmailSchema,
isValid: async (email: string) => {
@ -103,6 +119,23 @@ validateWithIndicator(emailInputEl, {
warning: "Please check your email address, it may contain a typo.",
};
}
if (
disposableEmailModule &&
disposableEmailModule.isDisposableEmail !== undefined
) {
try {
if (disposableEmailModule.isDisposableEmail(email)) {
return {
warning:
"Using a temporary email may cause issues with logging in, password resets and support. Consider using a permanent email address. Don't worry, we don't send spam.",
};
}
} catch (e) {
// Silent failure
}
}
return true;
},
debounceDelay: 0,

8
pnpm-lock.yaml generated
View file

@ -312,6 +312,9 @@ importers:
date-fns:
specifier: 3.6.0
version: 3.6.0
disposable-email-domains-js:
specifier: ^1.0.35
version: 1.16.0
firebase:
specifier: 12.0.0
version: 12.0.0
@ -4627,6 +4630,9 @@ packages:
discontinuous-range@1.0.0:
resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==}
disposable-email-domains-js@1.16.0:
resolution: {integrity: sha512-5BTVWEfUO1xeJ/jQdt+bIeOFYlgKuN5wGUtiMlkj0rqqpeO8LwiIH+7sKYvob87yMBjDKJWD8eqSSHFVM0g2pQ==}
docker-compose@1.2.0:
resolution: {integrity: sha512-wIU1eHk3Op7dFgELRdmOYlPYS4gP8HhH1ZmZa13QZF59y0fblzFDFmKPhyc05phCy2hze9OEvNZAsoljrs+72w==}
engines: {node: '>= 6.0.0'}
@ -14221,6 +14227,8 @@ snapshots:
discontinuous-range@1.0.0: {}
disposable-email-domains-js@1.16.0: {}
docker-compose@1.2.0:
dependencies:
yaml: 2.5.0