What are magic links, and what the click actually hands over
A magic link is a single-use URL emailed instead of a password. The token lives an hour. The session it buys lives thirty days, and that is the number worth arguing about.
In short
A magic link is a single-use, time-limited URL emailed instead of a password: the server mints a token, the user clicks, and the token is redeemed for a session. The token expires in minutes or an hour. The session it grants can last weeks with nothing behind it, and that is the real decision.
A magic link is a single-use, time-limited URL you email to someone instead of asking them for a password. They click it, the server redeems the token behind it, and they are signed in.
Most explainers stop about there, then spend the rest of the page arguing about the token: how long it should live, whether five minutes is safer than fifteen, whether it should be signed or opaque.
That argument is mostly theatre. The token is the one part of this system that lives for minutes and is used once. What the click actually hands over is a session, and in most products that session is measured in weeks.
Why they exist at all
The honest case for magic links is not that they beat passwords on security. It is that you already built one.
Every password system ships a reset flow, and that reset flow is almost always a single-use link or code emailed to the address on file. Ours sends a six digit code. So the inbox is already the master key to the account - it always was, quietly, behind a password that a reset mail can replace in thirty seconds. A magic link removes the password and admits what the email address was doing all along.
What you get for that is real: no password storage, no "must contain a symbol", and nothing for a user to reuse from a breached site. You do not get to delete the emailed-link flow, since that is now the front door rather than the back one, but you do stop maintaining two of them.
What you give up is a second thing the attacker has to hold. With a password in place, an inbox takeover is step one of two, and the second step is still guessing something you never emailed to anybody. Without it, the inbox is the whole job.
How it actually works
Four clocks, not one. The link is only the first.
Here is the chain BuildBase runs, because a concrete one beats a diagram. Asking for a link mints a signed token, stores it, and emails a URL.
What travels in the email is not the token: it is a 32-character opaque code, and the signed token sits server side in Redis, with a SHA-256 hash of it in the database alongside an expiry. The row holds the address, the hash and the expiry date, and never the token itself.
When the link is opened, the code is exchanged for the token and the token is
redeemed with a single atomic delete. That matters more than it sounds. Finding
the row and then deleting it is two operations, and two concurrent clicks can
both pass the find. One findOneAndDelete cannot: the first caller gets the
document, the second gets nothing. That is what makes the link genuinely single
use rather than approximately single use.
Then the part nobody draws:
| Stage | Lives | What it is |
|---|---|---|
| The emailed link | 1 hour | An opaque code standing in for a server-side token |
| The login token | 24 hours | What redeeming the link actually returns |
| The auth code | 10 minutes | Minted at the redirect step, traded for a session |
| The session | 30 days, or 90 remembered | The thing the user actually walks away with |
Redeeming the link does not mint the session. It returns a login token, the sign-in page trades that at the redirect step for a 10 minute auth code, and your app exchanges the code at the token endpoint. Only at that last exchange is the lifetime chosen: 30 days by default, 90 if the person ticked remember me, under a hard ceiling of 180 days that an organization's own settings cannot raise. The configurable range is 1 to 180 days, and a plan limit can pull it lower still.
Four hops to get there, and the longest-lived of the first three is a day. The thing waiting at the end of them lasts a month.
Key takeaway
The link expires in an hour. The session it produces lasts 30 days by default, which is 720 times longer. If you only tune one number in a magic link setup, tune the session.
The expiry everyone tunes
Published defaults for the link itself cluster in a narrow band. Clerk expires email links after 10 minutes. Auth0 defaults its passwordless email code to three minutes. Supabase and Stytch both default to an hour, and Stytch's signup links default to a week. Descope publishes no default and recommends three to five minutes.
Now the other number, from the same vendors' own docs. A Supabase session, by default, lasts indefinitely until the user signs out; time-boxing it is opt-in. Clerk gives new instances a seven day maximum session lifetime, with inactivity timeout off by default.
So a product can ship a 10 minute link and a 7 day session, and call the first number its security story. The real boundary is the second one. A stolen link is worthless in eleven minutes. A stolen session cookie is good for that whole week, and there is no password behind it to re-establish who is holding the laptop.
This is also why magic links and long sessions are an awkward pair. The method's whole premise is that proving control of an inbox is enough, and the session length quietly decides how long ago that proof is allowed to have happened. A month ago, on a laptop since sold.
What breaks in practice
Three failure modes, and only the first is widely discussed.
The link opens somewhere else. Someone requests sign-in on a laptop and taps the link in their phone's mail client. The sign-in completes using state held in the browser that asked for it, so the link does not sign them in on the phone, and it is now spent for the laptop too. Say "open this on the same device" on the sign-in screen and make a second request cheap.
A scanner clicks it first. Corporate mail security follows URLs before the human does. Microsoft's own documentation for Defender for Office 365 Safe Links says URLs are scanned prior to message delivery and that URLs without a good reputation are detonated asynchronously in the background.
Stytch documents the consequence plainly: the scanner "will end up consuming the token before the email actually makes its way into the user's inbox", which in some corporate environments makes email magic links unusable. Supabase names email prefetching as the most common cause of its expired-token errors.
The mitigations in the field are an interstitial page that needs a real click, a bot check at redemption, or dropping the link and sending a code to type.
They ask for a second link. Someone does not see the first mail, clicks send again, then finds the first one and taps that. Whether that works is a design decision each vendor makes differently, and almost nobody states it on the sign-in screen.
BuildBase deletes the previous token when a new one is issued, so only the newest link works. Descope documents the opposite: sending multiple magic links does not invalidate the earlier ones. Neither is wrong. Both will confuse someone with two tabs open.
A magic link is an email before it is an auth method. It inherits every delivery problem email has. If the mail lands in spam or arrives four minutes late, the method simply does not work, and no amount of token design fixes it. That is a deliverability problem, and it is the most common reason a magic link rollout gets reverted.
When a magic link is the wrong choice
An explainer that only lists upsides is a brochure. Four cases where this is the wrong tool:
When the inbox is what you are protecting. For an email client, a domain registrar, or anything holding the keys to other accounts, emailing the entry credential puts the lock inside the box.
When sign-in happens away from the mail. A kiosk, a TV app, a shared terminal, a workstation where corporate mail only exists on a phone. Every one of those turns a one-tap flow into a two-device errand. Send a code instead.
When the address is shared. billing@ and support@ are read by several
people, and a magic link to a shared mailbox is an account anyone in that
mailbox can enter. That is a provisioning problem wearing an auth costume.
When speed is in the critical path. Sign-in during a checkout or a live demo against an inbox that may take a minute is a bad trade. Keep passwords or passkeys on that path.
The alternative worth reaching for first is passkeys. They are phishing resistant in a way a mailed link is not, because the credential is bound to the origin and cannot be replayed on a lookalike domain.
The cost is recovery: a passkey lives on a device, and when the device goes, something has to let the person back in. That something is very often a magic link. The two methods pair better than they compete, which is why BuildBase ships both among its sign-in methods, and why most products that offer passkeys keep an emailed fallback behind them.
The session is the part that outlives the decision
Everything interesting happens after the click, and the sharpest edge shows up once authorization enters the picture. A 30 day session means a decision made a month ago is still being honoured, which is fine for identity and much less fine for permissions.
Somebody demoted this morning is still carrying a token minted a month ago. How often that is re-checked, and what a role change does to a live session, is its own question and worth settling before you lengthen a session to make sign-in less annoying.
If you are wiring this up rather than deciding about it, the implementation side is in how to add magic-link login in Next.js, which covers the call, the callback and the failure modes above in code.