Magic links give users the simplest way to log in: c lick a link in your email, skip the password entirely. The catch is that this simplicity runs on a security system that has to work perfectly every time.
This guide covers the complete magic link flow, from generating secure tokens to sending emails that actually reach inboxes. You'll learn how to build a passwordless authentication system that keeps both users and security teams happy.
TL;DR: Magic Links Done Right
- Generate cryptographically secure tokens that expire in a short time frame, such as 15 minutes or less. One click, one use, then they're invalid.
- Send the email instantly through authenticated channels. Include clear copy about what's happening and when the link expires.
- Track everything: who requested links, when they clicked, which tokens got burned. You need this data to catch attacks early.
How Magic Links Actually Work
The flow looks simple to users, but there's a lot happening under the hood. Here's what goes on from request to login:
- User requests a link. They type their email address into your login form and hit submit.
- You generate and store a secure token. Your backend creates a high-entropy random string. You then store a hashed version of this token in your database, associated with the user's ID and an expiration timestamp.
- Email goes out immediately. Your transactional email service sends a message with the magic link embedded in a button. The URL contains the raw, unhashed token.
- User clicks the link. Their browser opens your app with the token in the URL parameters.
- Your app validates the token. Your backend finds the token in the database by hashing the one from the URL, verifies it hasn't expired, and confirms it hasn't been used before.
- Login happens. If all checks pass, you create a session and immediately invalidate the token in your database so it cannot be used again.
A weak token generator makes you vulnerable to brute force attacks and slow email delivery frustrates users. Missing validation checks let attackers perform replay attacks with old tokens1. Get any piece wrong and your "simple" login system could lose you customers.
Building Secure Magic Links
Security isn't optional with magic links. These tokens are temporary passwords, so treat them that way.
Generate Unguessable Tokens
Your tokens need real randomness, not just Math.random() or timestamp tricks. Use a cryptographically secure random number generator (CSPRNG) to create at least 256 bits of entropy2. In Node.js, that means crypto.randomBytes(32)3. In Python (version 3.6+), use secrets.token_urlsafe()4. Never try to roll your own randomness.
For a stateful system (where the server that generates the token also validates it), the best practice is to hash the token before storing it in your database, just as you would with passwords. This prevents attackers from being able to use the tokens if they manage to dump your database. When a user clicks the link, you hash the token from the URL and look up the hash in your database.
Alternatively, in a stateless system (where different services might need to validate the token without a shared database), you can use a signed token format like a JSON Web Token (JWT). In this model, the token itself contains the user ID and expiration time, and it is signed with a server-side secret. However, for most standard magic link implementations, the simpler, stateful approach of storing a hashed random token is more secure and easier to implement correctly5.
Keep the Time Window Short
Magic links should expire fast. A window of 5 to 15 minutes gives users enough time to check their email without leaving a huge window for attacks6. If a user's link expires, make it easy for them to request a new one. Nobody wants to dig through old emails looking for a working link.
One Token, One Login
Every token should work exactly once - the moment a magic link is used, your system should invalidate or "burn" that token. Mark it as used in your database or delete it entirely. This is a critical defense against replay attacks, where an attacker tries to reuse an intercepted link1.
Require HTTPS Everywhere
Your magic link URLs must use HTTPS. No exceptions. Unencrypted HTTP links can be intercepted by anyone on the same network, exposing the token7. While you're at it, ensure your emails are authenticated with SPF, DKIM, and DMARC. These standards help prove your emails are legitimate, which is a key factor in deliverability and avoiding spam folders8.
Add Context to Help Users
Include information in the email that helps users verify the request is legitimate. Consider showing the browser type, operating system, or city where the request originated. If someone in Tokyo gets a magic link request from Dallas, they'll know something's wrong. This context also helps your support team investigate suspicious activity.
Writing Magic Link Emails
The email is just as important as the token. A confusing or suspicious-looking message will either get ignored or reported as spam.
Start with a clear subject line like "Your login link for [App Name]" or "Sign in to [App Name]". Users should know exactly what this email is before they open it.
The email body needs four things:
-
First, acknowledge that they requested this link. Something like "You asked to sign in to [App Name]" sets the right context immediately.
-
Second, provide one obvious button that contains the magic link. Make it big, make it centered, make it impossible to miss. It is best to also include the raw URL below the button for email clients that strip out HTML.
-
Third, tell them when the link expires: "This link expires in 15 minutes" manages expectations and creates urgency. Users won't save it for later if they know it won't work.
-
Fourth, explain what to do if they didn't request this. "Didn't request this? You can safely ignore this email" prevents unnecessary panic. Add a support email for users who want to report suspicious activity.
Keep the whole message short. Something like three or four sentences plus the button is plenty. Use your standard email template so users recognize it as legitimate. If you normally send from noreply@app.com, don't suddenly switch to security@app.com for magic links.
Preventing Magic Link Abuse
Authentication systems attract attackers. You need defenses in place before you launch.
Rate Limiting Stops Brute Force
Limit how many magic links a user or IP address can request. While there is no single industry standard, a common heuristic is to limit requests to around three per email address per hour, and to flag IP addresses that make an excessive number of requests, such as ten or more in a single minute. When someone hits these limits, show a friendly message about trying again later and log the attempts for your security team to investigate.
Hide Whether Emails Exist
When someone requests a magic link for an email address that is not in your system, you should display the same generic success message that you would for a valid user. This is a critical practice to prevent account enumeration attacks, where an attacker uses your login form to discover valid email addresses9. You can still send an email to the non-existent address, but the content should simply state that no account was found.
Log Everything for Investigations
Keep detailed logs of magic link activity. Track who requested links, what IP address they came from, when they clicked, and whether validation succeeded. This data becomes critical when investigating attacks or debugging issues. While retention policies vary, storing these security-relevant logs for at least 30 to 90 days is a reasonable baseline, though regulated industries may require longer periods.
Build Fallback Options
Some users may not be able to receive emails reliably. Their corporate network might block external messages, or they might have lost access to that inbox. It is wise to provide another way in, such as backup codes, SMS verification, or a direct support contact. Magic links should be the primary passwordless option, but not necessarily the only option.
Technical Implementation Tips
The implementation details make or break your magic link system. Here's what developers need to know.
Database Schema
Store tokens in their own table with these fields at a minimum: a hashed version of the token, the user ID it belongs to, a creation timestamp, an expiration timestamp, a flag to mark if it has been used, and the IP address of the requester. Index the hashed token field for fast lookups. Consider adding a browser fingerprint or other session data for extra security.
Email Delivery
Use a dedicated transactional email service, not a marketing tool. For the transactional emails you absolutely need guaranteed, instant delivery, not scheduled batch sends. Services like Bento, SendGrid, or Postmark specialize in transactional email. Make sure to set up webhooks to track delivery and open events.
URL Structure
Your magic link URLs should look something like https://app.com/auth/magic?token=abc123xyz. Keep them reasonably short. While some older browsers had URL length limits around 2,000 characters, a magic link token will never approach this length, so it is not a practical concern10.
Session Creation
After validating a magic link, create a session the same way you would for a password login. Set appropriate session timeouts, enable "remember me" options if needed, and trigger any post-login workflows like analytics events or feature flags.
Error Handling
When validation fails, be specific enough to help legitimate users but vague enough to confuse attackers. "This link has expired" is a good, clear message. "This link was used 4 minutes ago from IP 192.168.1.1" gives away too much information. Log the detailed information for your own team, but show users generic messages.
Common Magic Link Problems
Even good implementations hit snags. Here's how to handle the common ones.
Email Delays
Sometimes email takes a few minutes to arrive. You can add a "Resend link" button to your UI that appears after 30 seconds. Implement exponential backoff if users spam the resend button. Consider showing estimated delivery times during known delays with your email provider.
Link Scanners
Corporate email security systems and some email clients often scan links in incoming emails for malware. This can accidentally trigger your magic link and consume the one-time-use token before the user ever clicks it11. To prevent this, implement a two-step process: clicking the link in the email should lead to a landing page with a "Confirm Login" button. The actual token validation and session creation should only happen when the user clicks this second button.
Mobile App Deep Linking
Magic links need special handling for mobile apps. Use Universal Links on iOS and App Links on Android to open your app directly from the link1213. It is also helpful to include a "Having trouble?" link that explains how to copy and paste the link manually if the automatic deep linking fails.
Token Collisions
With a proper source of randomness providing 256 bits of entropy, the probability of generating the same token twice is astronomically low. However, as a matter of defensive programming, you can implement a retry mechanism. If you generate a token that, by some fluke, already exists in your database, simply generate a new one. If you were to see three collisions in a row, it would be a sign of a critical failure in your random number generator, and you should alert your operations team immediately.
Testing Your Magic Link System
Testing passwordless authentication requires checking both the happy path and the edge cases.
Start with the basic flow. Request a link, click it immediately, and verify that the login works. Then, test the boundaries: expired tokens, already-used tokens, invalid or malformed tokens, and tokens for deleted users. Each of these scenarios should fail gracefully with a helpful error message.
Test delivery to different major email providers as Gmail, Outlook, Yahoo, and corporate email systems all handle email differently. Verify that your messages consistently reach the inbox, not the spam folder. Check that the links work correctly in different email clients, including web, desktop, and mobile versions.
Load test the system. What happens if 1,000 users request magic links simultaneously? Can your email provider handle the volume? Do your rate limits work as expected? It is better to find these limits in testing than in production.
Finally, conduct security testing. Try to guess tokens (you shouldn't be able to). Attempt replay attacks with used tokens. Request links for non-existent emails to check your enumeration defenses. Send malformed tokens to your validation endpoint. Your system should handle all of these attempts gracefully and securely.
How Bento Makes Magic Links Better
Bento handles the hardest parts of magic link delivery: getting emails to inboxes fast and reliably. Our transactional email infrastructure includes authentication (SPF, DKIM, DMARC) by default, so your magic links don't end up in spam.
You get templates for consistent branding, webhooks for tracking delivery and clicks, and batching controls that maintain strong deliverability even during traffic spikes. The dashboard shows you exactly what's happening with your magic link emails in real time. Our API makes it easy to send personalized context like device info or location.
Engineers get reliable infrastructure without managing multiple services. Operators get visibility into security events and delivery metrics. Your users get magic links that actually work.
Start your 30-day free trial today or book a demo.
Magic Link Implementation Checklist
Ready to build? Here's your step-by-step plan:
- Set up token generation. Implement a CSPRNG to create high-entropy random tokens. Set up your database to store hashed versions of these tokens.
- Build the request flow. Create the form where users enter their email. Add rate limiting and request logging.
- Design the email. Write clear copy, design an obvious CTA button, and include expiration info and security notes.
- Implement validation. Build the logic to look up the hashed token, check its expiration and usage status, and create a session for valid tokens.
- Add operational tools. Set up monitoring, alerting, and admin tools for investigating issues.
- Test thoroughly. Run through normal flows, attack scenarios, and edge cases. Load test at your expected volume.
- Plan for support. Document common issues, train support staff, and build fallback authentication methods.
For more context on email authentication, check out our guides on password reset email best practices and transactional email best practices.
Make Login Simple, Keep It Secure
Magic links strip away the friction of passwords while maintaining strong security. Users get a login experience that just works. You get an authentication system that's actually maintainable.
The key is respecting both sides of the equation. Make the UX smooth, but don't compromise on token security. Send emails fast, but include the right security context. Keep the time windows tight, but give users ways to recover when things go wrong.
Get these details right, and magic links become the login method your users actually prefer. No passwords to remember, no 2FA codes to type, just a click and they're in. That's the real magic.
References
Footnotes
-
Obsidian Security. (2024, February 24). Token Replay Attacks: Detection & Prevention. Retrieved from https://www.obsidiansecurity.com/blog/token-replay-attacks-detection-prevention ↩ ↩2
-
Python Software Foundation. (n.d.). secrets — Generate secure random numbers for managing secrets. Python 3.12.2 documentation. Retrieved from https://docs.python.org/3/library/secrets.html ↩
-
OpenJS Foundation. (n.d.). Crypto. Node.js v20.11.0 Documentation. Retrieved from https://nodejs.org/api/crypto.html ↩
-
Python Software Foundation. (n.d.). secrets — Generate secure random numbers for managing secrets. Python 3.12.2 documentation. Retrieved from https://docs.python.org/3/library/secrets.html ↩
-
Sjoerd. (2020, October 8). Magic links: JWT vs random string. Information Security Stack Exchange. Retrieved from https://security.stackexchange.com/questions/239272/magic-links-jwt-vs-random-string ↩
-
Ping Identity. (2024, February 4). What is Magic Link Login? How it Works. Retrieved from https://www.pingidentity.com/en/resources/blog/post/what-is-magic-link-login.html ↩
-
Kaspersky. (n.d.). What is a Replay Attack and How to Prevent it. Retrieved from https://www.kaspersky.com/resource-center/definitions/replay-attack ↩
-
Cloudflare. (n.d.). What are DMARC, DKIM, and SPF?. Cloudflare Learning Center. Retrieved from https://www.cloudflare.com/learning/email-security/dmarc-dkim-spf/ ↩
-
OWASP Foundation. (n.d.). Testing for Account Enumeration and Guessable User Account. OWASP Web Security Testing Guide. Retrieved from https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/03-Identity_Management_Testing/04-Testing_for_Account_Enumeration_and_Guessable_User_Account ↩
-
Microsoft. (2022, October 26). Outlook for Microsoft 365 truncating long URL's. Microsoft Learn. Retrieved from https://learn.microsoft.com/en-us/answers/questions/1063670/outlook-for-microsoft-365-truncating-long-urls ↩
-
NextAuth.js. (2023, June 3). Allow Email Signups Behind Corporate Link Checker. Retrieved from https://next-auth.js.org/tutorials/avoid-corporate-link-checking-email-provider ↩
-
Apple Inc. (n.d.). Supporting universal links in your app. Apple Developer Documentation. Retrieved from https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app ↩
-
Google. (n.d.). About deep links. Android Developers. Retrieved from https://developer.android.com/training/app-links ↩



