Blog
Explainer

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.

Dharmendra Jagodana8 min read

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:

StageLivesWhat it is
The emailed link1 hourAn opaque code standing in for a server-side token
The login token24 hoursWhat redeeming the link actually returns
The auth code10 minutesMinted at the redirect step, traded for a session
The session30 days, or 90 rememberedThe 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.

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.

auth
passwordless
sessions
magic-link

Frequently Asked Questions

Are magic links secure?

The link is the least interesting part of the answer. It is single use and short lived, so an intercepted link is usually already spent. The exposure is the inbox that receives it, which is also where your password reset lands, and the length of the session the click creates, because there is no credential to re-enter afterwards. Pick the session length deliberately and the method is roughly as strong as your password flow. Leave it on a long default and you have quietly made the inbox a month-long key.

How long should a magic link be valid?

Published defaults sit between three minutes and an hour: Clerk expires email links after 10 minutes, Auth0 defaults its passwordless email code to three minutes, and Supabase and Stytch both default to one hour for sign-in links. Anything in that range is defensible. Tuning it from 15 minutes to five buys far less than shortening the session the link grants, and costs you the users whose mail arrives slowly.

What happens if I open a magic link on a different device?

Usually nothing good. The sign-in is finished using state held in the browser that asked for the link, so opening it elsewhere can consume the token without signing anyone in. Request and open on the same device, say so on the sign-in screen, and make requesting another link easy.

Magic link or one-time code?

A code can be typed into the browser that requested it, which survives both the cross-device problem and email link scanners that follow URLs before the human does. A link is one tap. Send the code when people sign in on a device that is not holding their mail, and the link when they are reading email on the same machine.

Put this into practice

The modules in this post are one console away. 7-day free trial, no credit card.

7-day free trialNo credit card requiredCancel anytime