Demo, all content is generated
Question

Magic link always 'expired' for users with company Outlook email

Open · 693 views · asked by elena_p · edited

B2B tool, customers log in with magic links. Gmail users fine. Everyone on corporate Microsoft 365 gets:

/#error=access_denied&error_code=otp_expired&error_description=Email+link+is+invalid+or+has+expired

even when they click within 10 seconds of receiving it. Their IT people say nothing is blocked.

What I’ve tried

Raised the OTP expiry to 1 hour. Verified the redirect URLs. Sent myself a link to an outlook.com address and it works, so it's only the company accounts.

Comment
Do you know if their tenant uses Defender for Office 365? That would be my first guess. hannah_reyes · edited

3 answers

amir_h · edited

Classic: Microsoft Defender Safe Links (and similar scanners) open every link in incoming mail to check it. That GET request uses up the one-time token. When the human clicks, it's already spent.

Fix: the link in the email must not log anyone in by itself. Make it land on a page with a "Continue to log in" button, and only call verifyOtp with the token_hash when the button is clicked. Scanners fetch the page but don't press buttons.

Or skip links for these customers and send the 6-digit code instead.

Comment
This explains everything. Their IT literally said 'we scan links, but we don't block them'. elena_p · edited
Some scanners do run JS now. The button approach still holds up because they don't click, but don't do the verify in a useEffect on page load. chidi_eze · edited
lena_ops · edited

You can confirm the theory in the Supabase auth logs: the /verify request for a failing link comes a few seconds after the mail was sent, from a Microsoft IP range and a generic user agent, before your user ever clicked.

Comment
Checked: verify requests from an Azure IP, 3 seconds after sending. Mystery solved. Building the button page. elena_p · edited
hannah_reyes · edited

Agree with the button page. One more option if the customers are all on Microsoft: add Azure (Microsoft) as an OAuth provider. Corporate users usually prefer 'Sign in with Microsoft' anyway and you avoid email delivery entirely.

Comment