Magic links are not great but they are the right choice sometimes
LLM disclaimer: this post was written based on a discussion with claude and drafted by claude. I have edited it heavily but if you’re allergic to LLM’s feel free to skip it.
The setup: we’re have a policy compliance system that employees use roughly once per year to check boxes confirming they’ve read our updated policies. The right solution is to integrate this with our existing SSO solution that has proper anomaly detection, brute force protection, and phishing-resistant auth and so on. But for various organizational reasons (aren’t there always), that integration will take longer than I’d like.
So I started thinking - could we build our own lightweight OIDC service? Something that just validates “yes, this person controls this email address” and nothing more. I’m usually not a fan of magic-links, but could they work in this case.
To do some rubber ducking, I asked claude. Claude’s initial reaction was something akin to telling me thats the stupidest idea ever (because I told it to argue with me, not debate and it dutifully complied). It made upp all kinds of stuff like telling me it was outsourcing authentication to email, a fundamentally insecure transport designed in 1982. Every magic link gets cached in browser history, server logs, forwarded emails, screenshots - it’s a mess.
But I pushed back. The tokens are time based and ephemeral. The alternative is to have yet another password, making people pick bad passwords or just click “forgot password” anyway, which… is just a magic link with extra steps. Magic links are not changing the security model, we’re just making it honest about where the security bundary is.
It also had arguments about how it trains people to click links in emails, as if they would training in that. We have told people not to do that for 20 years and it hasn’t done much good. You just can’t have that as a security boundary.
Claude eventually conceded this point (of course it did, it’s designed to please and not fight) but it did raise one thing. Social engineering. If you implement the magic link in a way clicking the link logs you in where you requested it, that lets attackers call pretending to be IT, say “we just sent you a verification link, can you click it?” and boom, they’re in.
The fix is device binding (with a cookie) - when someone requests a magic link, you set a random cookie. The magic link only works if that cookie is present. If they click from a different device or browser, you explain what happened and let them either open it in the original browser or request a new link (invalidating the old one).
But when I considered this, I realised this is the wrong solution. Not because it does not work, but because it introduces a bigger risk. Strong device binding means if I normally check email on my phone but I’m now trying to access the policy system on my home laptop, I’m now forced to log into webmail on my home computer. If that device is compromised, I now have a full email comprimise instead of giving an attacker access to a compliance system. The security measure backfired by pushing risk to a more valuable target.
The alternative is OTP codes - send a 6-digit number to email, user types it in. This solves the device forcing problem but it’s vulnerable to social engineering. “Hi, this is IT, we need that code to complete your compliance verification.” Most people are wired to trust humans over machines, and are likely to comply even when the email explicitly says “DO NOT SHARE THIS CODE.”
(Banks have been fighting this battle for years. The code comes with a big warning saying “DO NO SHARE”, but when the scammer says “ignore that, this is a special case,” many will comply. )
So what’s the right choice? First, let’s be honest: the right choice is to use your existing IdP with proper security infrastructure. If you can’t do that, then it depends on your threat model and what you’re protecting.
For a genuinely low-value system like policy compliance, OTP phishing gets the attacker access to boring checkboxes. Device binding, while stopping the social-engineering angle, introduces the risk of a full email compromise. The math is clear - accept the smaller vulnerability to avoid creating bigger ones.
It’s a running joke that the only answer you get from security people when you ask about something is “it depends” but that’s because it’s true.
Pragmatic security means working within organizational constraints and accepting smaller vulnerabilities to avoid creating bigger ones.