---
title: "Weigh email links before making them the · Agent Wranglers"
url: https://agentwranglers.se/playbook/accounts/sign-in-methods/email-sign-in-trade-offs
updated: 2026-10-20
licence: "All rights reserved"
---

# Weigh email links before making them the only way in

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Recommended
- Chapter: [Sign-in methods](https://agentwranglers.se/playbook/accounts/sign-in-methods)
- Rule: Use email links or codes as the only sign-in for low-risk accounts only. Bind each link to the browser that asked for it, and offer a passkey after the first sign-in.
- How: An email link proves access to an inbox, not who someone is. NIST SP 800-63B-4 section 3.1.3.1 forbids email for out-of-band authentication. Mail also arrives late, lands in spam, or opens on a phone when sign-in began on a laptop. Set a random cookie in the browser that asks. When the link opens in a browser without that cookie, show a 6-digit code to type in the first browser instead of signing in. Make the token as the [sign-in links rule](https://agentwranglers.se/playbook/email/email-security/sign-in-links) says. Require a passkey or second factor for admins and accounts that hold money or business data.
- Check: Ask for a link in one browser and open it in another. The second browser is not signed in and shows a code. Typing the code in the first browser signs it in.

Example:

```js
app.get('/sign-in/link', async (req, res) => {
  const request = await signInRequests.findUnusedByTokenHash(sha256(req.query.token));
  if (!request) return res.status(410).render('link-expired');
  const sameBrowser = safeEqual(req.cookies.signin_nonce ?? '', request.browserNonce);
  res.render(sameBrowser ? 'confirm-sign-in' : 'type-this-code', { code: request.code });
});
```

```python
@app.get("/sign-in/link")
def open_sign_in_link():
    sign_in = sign_in_requests.find_unused_by_token_hash(sha256(request.args["token"]))
    if sign_in is None:
        return render_template("link-expired.html"), 410
    same_browser = hmac.compare_digest(request.cookies.get("signin_nonce", ""), sign_in.browser_nonce)
    page = "confirm-sign-in.html" if same_browser else "type-this-code.html"
    return render_template(page, code=sign_in.code)
```

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers accounts playbook: https://agentwranglers.se/playbook/accounts/sign-in-methods/email-sign-in-trade-offs`.

- Previous: [Keep the password manager's passkey list current](https://agentwranglers.se/playbook/accounts/sign-in-methods/passkey-signals)
- Next: [Never remove the last way to sign in](https://agentwranglers.se/playbook/accounts/sign-in-methods/last-sign-in-method)
- [Sign-in methods](https://agentwranglers.se/playbook/accounts/sign-in-methods)
