Hacker News new | ask | show | jobs
by robrobrobrob 1477 days ago
MagicLinks are a mobile nightmare. Mobile email clients use their own browser and cookie jar which consume the session cookie you're trying to put into the user's main browser. This results in users 'never staying signed in' and a lot of frustration.

Sending a one-time code via email fixes this, and is in practice about as easy to use as a link on desktop.

In our app (Loomio) we default to magic/codes, but let users use passwords if they prefer.

13 comments

> MagicLinks are a mobile nightmare. Mobile email clients use their own browser and cookie jar which consume the session cookie you're trying to put into the user's main browser.

It might be howling into the void but the conclusion I reach from this is that "Mobile email clients are a nightmare".

Generally speaking, embedded browsers in mobile apps are a terrible idea and break user expectations in multiple ways.

On the flip side, I find that opening links in isolated sessions very valuable, if not just because I don't want links sent by others to affect the Algorithm's perception of me. 80% of the time, I want to be a blank slate when interacting with in-app browsers.
I really want the choice. I hate that most iOS apps want to open links in their own webview instead of my default browser. Of course, when they DO use the default browser, I would like to have control over what context that browser uses to handle the link. Default? Private? Task-specific tab/container group? Ask me, please.
On Android, if you have more than 1 browser, and no default browser app (settings / apps / default apps / browser app), clicking a link will ask you which browser you want to use. For random one-time-usage links, I use Firefox Focus for that purpose. When it's closed it wipes all history, cookies etc. automatically.

Meanwhile if the stupid app decides to hijack the link and use in-app browser, you can't do that. And IAB typically means you can't have app and browser in parallel and switch between them from app launcher, which would be sometimes useful. Slack does that, and it's annoying. But at least you can right click> copy URL and open it manually in preferred browser.

Back in the day on the web site the marketing team always insisted links open in a new windows b/c customers won't ever come back if you direct them away. Eventually browsers let users override that nonsense. The insistence by our marketing team on all these embedded browsers reminds me of the same mindset in 2020.
I also believe links should open in the same window, but I've met people who religiously believe otherwise and they are not a part of marketing team
> It might be howling into the void but the conclusion I reach from this is that "Mobile email clients are a nightmare".

This is true, and as a user I want something better, but as a developer I need to build for what our users use.

I don't put "Site best viewed in Netscape Navigator", and I don't tell users they can only login on desktop.

Totally agree. Same apps have an option to disable this behavior, but it's a chore to track down and update everywhere. I would love it if Android had an OS level setting to disable these and send every request to the default browser. That might cost Google some data/money so sadly it'll probably never happen.
Totally agree with this statement. In my apps, I have used magic links for desktop usage but on mobile apps, I opt for an OTP approach. Solves the same problem without the mobile email client issue.
It's actually pretty insane: there is a setting on Android to always use the main browser (Chrome) to open links, I've turned it on, and yet the Gmail app still opens the links in the embedded Chrome browser — which has different cookies, different history that apparently is not synced with my other Chromes, and even looks sli-i-ightly different than "normal" Chrome, somehow.

I've no idea why it is so difficult for apps to open links in the user-selected browser. Isn't it just "xdg-open https://example.com/"?

Uninstall chrome and use Firefox, and it works for me with full cookie sharing. There is even an "open in Firefox" in the hamburger drop-down which transfers the page to the browser app without reloading.
As an added bonus, Firefox on Android also support ublock origin.
I get that with Kiwi browser's custom tab as well. Kiwi is a fork of chrome for Android with extension support. Is it not in stock chrome?
Last time I tried Firefox on my Android phone it showed weird drawing artifacts (half/quarter of the page is full black) on pretty must all websites, so thanks for your suggestion but I'd stick with an actually working browser.

In any case, I don't need to bother about whether any "cookie sharing" works or not if I only use one browser, do I?

Can you uninstall chrome from most Android phones? I think I've tried in the past but failed.
Go to Settings, apps, search for Chrome and click the disable button.
The setting you're talking about is probably the OS level setting. There's an additional setting in the GMail app to do the same thing.
> It's actually pretty insane: there is a setting on Android to always use the main browser (Chrome) to open links

That is quite insane indeed.

And on top of that, Android keeps using Chrome as embedded browser. Even if you installed another one. MSHTML all over again.

Before that (Android <5.0 times), you'd have a WebView strictly tied to the OS version and not upgradable if OS goes EOL. Even if device had Chrome 60, webview was based on Chromium 28-30 (depending on manufacturer). With no flexbox etc. As a dev, it was a MASSIVE pain to develop webview integrations with this. On par with IE8. Being able to have the webview upgraded without upgrading OS is a major win.

[Note there are two things: 1) stupid apps putting a generic in-app browser inside the app - that's annoying; and 2) apps using a webview with a bridge between native app and a website, injecting some stuff and properly integrating the embedded website; something impossible to do by opening a website in regular browser]

Many browsers such as Firefox and Kiwi let you enable them for 'custom tabs' which is the term for popup browser windows that you may be thinking of as embedded browsers. I think the setting is usually within the app's settings rather than the general android settings.
> I've no idea why it is so difficult for apps to open links in the user-selected browser. Isn't it just "xdg-open https://example.com/"?

No, theres implementation differences across API levels and the "shared cookie jar" implementation is newer and had some quirks. Its been a couple years since I was deep in this so it might have changed, but that was my experience back then.

> I've no idea why it is so difficult for apps to open links in the user-selected browser.

It's not, it's a conscious decision to open links in chrome from gmail.

That’s odd. I have used the Gmail app on iOS for many years, and as far as I can remember it has always just opened links directly in Safari.
I get a choice of Chrome, Google app (which isn’t even installed) and Safari. I mainly use Firefox, though, and that isn’t an option.
At Clerk, (https://clerk.dev, auth SaaS) we did a ton of work to get magic links to play nice with multiple devices, it's certainly a pain, and there's tradeoffs.

The crux of it is, do you sign in the "originating" device (where the magic link was sent from), or the "consuming" device (where it was clicked).

Because it's veryyy common for people to be on a website on their computer, then click the link on their phone, but still want to be logged into their computer. We opted for the "originating" device. But this has security concerns.

(you also need to worry about multiple tabs on the same browser, but cookies make this easy to deal with)

But, what if an attacker just sends a link to loads of email addresses, and one of the receivers clicks on it by accident? Their account now has been taken-over.

So, this needs to be combined with the notion of "trusted devices", and of course, not breaking what the user expects. All in all, a TON of work to get some marginal gains in some sectors, mainly B2C. IMO, B2B should always have passwords as an option. 1Pass is too widely used to ignore.

Never do this. Automated email scanners may fetch the URL. For example, Hotmail’s mail delivery process is notorious for triggering magic links.
You can defend against this a little bit by having the magic link load a page with a button (which submits a POST action) that you have to click.
> Mobile email clients use their own browser and cookie jar which consume the session cookie you're trying to put into the user's main browser. This results in users 'never staying signed in' and a lot of frustration.

The earliest Magic Links I remember using didn't have this problem, and this was before smartphones. The Link, back then at least, was only to authenticate that you can receive emails at a certain mailbox, not to authenticate whichever browser you used to open it. Your login attempt would keep the login (not logged-in) session open wherever you attempted to login from, while waiting for the Magic Link to be clicked on in the background. Whenever the Magic Link was clicked, irrespective of where it was clicked, your original browser session would stop waiting and log you in.

This also avoids the tedium of having to copy a link or code from one device/app to another. Just click, a browser tab/window opens for a short time, and then closes automatically after doing its job; you go back to wherever you attempted to log in, and simply proceed. This also simplifies post-login flows (like visiting a specific page you tried to access before being forced to login) in that such flows don't need to be tied to the Magic Link itself.

You have to deal with 'email antivirus scanners' which click every link in a mail sent to every user in some sandboxed browser.

If your user has one of those, then their account effectively has zero security since an attacker can attempt to log in, and the web security software will click the link, and the attacker (in the originating browser) is now logged in.

Those destroy the links anyway; I can never reset a Salesforce password because the link is always “expired”.
That seems extremely unsafe, email being what email is, there are many layers of automation that may access that link unprompted: a spam/virus filter on the server or the client may access it to verify it is not some malware, a email client may access it to preload assets, etc. So if a malicious actor starts a login on one of my accounts, and somewhere along my email chain-of-custody/stack a helpful software access that link for some reason, then the malicious actor will be authenticated.
A site could mitigate this by responding with a form to be posted, asking the user to click a button to verify. Only the POST request after user interaction would verify the email address.
I think this is an important point that is often overlooked.

I can quickly access email on my phone - that doesn't necessarily mean I want to log in on my phone.

As a user the problem I run into with some frequency is passwordless login where the waiting session loses its “waiting” status because I switched the active application in iOS from my browser to my email and back again. When I get back, the page for whatever reason (refresh after being backgrounded?) requires a new auth attempt.

A wrinkle in this complaint is that in most scenarios I don’t especially want a browser tab to be able to hold onto a persistent session while it’s closed due to privacy reasons, regardless of where that session state is held (and I realize there are a number of options).

> (refresh after being backgrounded?)

This is a problem on low memory devices - the page gets forced to refresh because the system ran out of RAM. The iPhone 13 only has 4GB of RAM, and with many apps requiring 2+GB, it is frequent a web page can't stay loaded in the background.

Most android devices have 8GB plus RAM, so shouldn't hit that issue, although it still happens on very low end devices.

They can take my iPhone 7 and its 2GB of RAM over its dead body.
This took me a bit of time to realise, but well-designed software can avoid this problem rather simply: Combine the Magic Link with a short-code that can be manually typed into the browser session attempting to login. This short-code can then be prominently displayed in the email header and/or early on in the email body, so a user can simply read it off the notification that the email would generate. No need to switch apps from browser to email.
A simple workaround for the implementation is to redirect you to a URL with the code hashed as a query parameter.
Yes, I was thinking about that, as something like a TOTP. But that needs to be done via DNS-over-HTTPS or DNS-over-TLS or else the code can be sniffed by a MITM since the URL is sent in the clear, at which point you’re back to the problem of persistent TLS anyway.

Edit: In fact, nevermind. My thinking on that was flawed. I think you’re right, that should be fine, as long as the DNS is encrypted.

I just recently implemented a magic link flow for a project I’m working on, and “authenticated in the originating browser” was a high priority item for me.

I think it’s just so much nicer of an experience to not have to worry where you go check the email or which browser opens when you click the link there.

The site you used to login was clearly where you intended to be, so that should definitely be one of the authenticated sessions when you click the link.

The issue I ran into with this was I could initiate a login for anyone and if they accidentally hit the link I could remote login.

Switched to pin code entry, less fancy but achieves all the desired UX

I think that’s totally fair. The site I’m working on is pretty low stakes from a security perspective (a private personal blog), with less technical people in the audience, so I was willing to make that trade off for the UX outcome I wanted, but that was specific to the damage of a breach in my specific use case.
>Your login attempt would keep the login (not logged-in) session open wherever you attempted to login from

While I generally agree that this behavior is correct from a usability standpoint, it's not safe. That design allows a user to do a one-click account compromise when they receive a magic link that was sent in response to the login attempt of an attacker that happened to know the user's email address.

So add a step that requires "knowledge transfer" between the login device and confirmation device. Microsoft does this in their authenticator app showing essentially three confirmation buttons with different numbers on them, and requiring you to press the one being shown on the login screen. Worst case you now have one-in-three-clicks account compromise rater than one-click. And hopefully this also causes the user to ask why they are being required to log in if the app is not displaying the confirmation data.
How I handle this is as follows, within a B2B context. The email sent to the user contains both a magic link and a code.

If the user clicks the magic link from the same device that they initiated the login from, it just opens straight into the app.

If the user clicks the magic link from a different device, it confirms the token and then the browser waiting on the original device logs in. It uses a generic browser/server messaging library to achieve this, based on whichever of HTTP SSE, WebSockets and polling is available.

If the user can't do either of these for whatever reason, such as email virus scanning or whatever, the bottom of the email also contains a code which can be entered onto the waiting login form, along with some information about never giving that code out to anyone else on the phone and checking the URL.

The magic link also has an interstitial page with an automatic POST form to deal with browsers and other things pre-opening links. There are also various points along the way that it attempts to check for phishing and perform additional interventions, such as if the magic link is clicked from a different country to the login request, or if the request is coming from a suspicious location, etc.

After all this is complete, it then does 2FA using SMS. The magic link login is only needed the first time you log in on a given browser as it saves a persistent cookie, but the SMS 2FA is needed every time. We don't support persistent sessions due to legal requirements.

We are also working on adding webauthn authentication, but early testing suggests most users find it very confusing. Mobile devices tend to handle it acceptably, but Windows' implementation in particular is currently very user hostile and often users worry they are being hacked. It also doesn't replace the email/SMS-OTP flow, because if the user is logging in from a new device, we still need a way to validate that it's them before they can subsequently authenticate with webauthn. We also support SAML SSO.

Most users are not interested whatsoever in carrying around a Yubikey. Even just using SMS-OTP got a lot of push back for being inconvenient. I think FIDO2 will work better for users when there's a good way of automatically synchronising their keys across all of their devices in a vendor-neutral.

I worked on a project where we used magic links (using Firebase) for users to checkin at an event using an app. It was a disaster.

For a lot of users the link didn't open in the app the way it was supposed to. Another surprisingly large subset of users had two phones, but the app wasn't on the phone they used for email. A one-time code definitely would have been better than a link.

Lots of issues with magic links. And yet, and yet. For a certain class of accounts, I'm okay deferring to my email inbox.

From a security perspective as a user, it's no different than deferring to a social provider like Google, but has the benefit in that it gives a user more control over the third party they choose to delegate control to.

You allow username/password access and that's great too. I'm a fan of giving users options, but I don't know if anyone has done studies and found that a single login option actually is better for conversion. I'd be interested in seeing some numbers around that.

I wouldn't recommend a magic link for high value accounts, but I have pretty easy access to my mailbox, from everywhere (in contrast to my password manager, which is cumbersome to use from different accounts). I can also forward the email with the link to any device where I have email.

If it is a low-value account that I use infrequently, I'm a fan. The alternative is to just use 'forgot password' to get a link that resets my password. That is pretty much the same functionality.

I guess the hard part there is that no one building software thinks their app is 'low-value'. :)

From a security perspective, most passwords can be reset by email, so sending a magic link or code to the email has approximately the same security properties.

The only real difference is if the password reset forces you to change the password and logs you out everywhere, then you can at least detect the break-in more quickly with the password approach.

>I guess the hard part there is that no one building software thinks their app is 'low-value'.

I know the things I'm working on are low-value and appreciate it. In fact, I genuinely dread ever working on something high-value.

If the high value account does password resets via email a magic link email is no less secure. Even banks do password resets by email but combine it with private info like SSN which really isn't private due to all the breaches like equinox.

Email is the master key to most of your accounts.

> Email is the master key to most of your accounts.

I’ve been mulling over ideas for this in my head for a while, about fully standardizing the magic-link-to-login approach as a full single-sign-on implementation.

Sites could craft an email with a specific header scheme that denotes what it’s trying to do, for what website, and what the magic link key is. The body of the email would still look like a regular magic link email as it does today, as a graceful fallback behavior.

The website login screen could detect if the user agent supported magic links (best way to do this detection is TBD…), the requirements being be that (a) The OS has an email account configured for the user (like via Mail.app or similar), and (b) the OS can broker the security information of the magic links to the browser so that it doesn’t need “full” access to your email, just a standardized API for the browser to get the magic link header info.

If everything checks out, the UX for the login page would be:

- User: enters email address to log in with, hits submit

- Webpage: renders a quick loading spinner

- Website: Sends a magic link email to the email address

- OS: Sees the special magic link email and notices that the browser is in focus on the same origin (using an origin policy that requires the magic link’s headers matches the same origin as the page)

- Browser: Gets magic link/token from the OS

- Browser: Submits token to website

- User sees the login succeed.

- (Optional:) The mail client puts the email itself in a trash folder or some other place so the user doesn’t even notice it (although they should be able to dig it up if they want.)

The graceful fallback if the browser/OS setup is not supported would be that the website just renders the same “check your email for a link” page it would have normally sent.

Apple has enough integration to easily implement this on iOS and macOS, it would be great to see this happen. They already implement autofill for security codes sent to SMS or email, so they already have a similar level of integration, it just needs to be made seamless and not even require user input. It could also be a totally open standard so that any OS/desktop environment/etc could implement it.

I was recently thinking something similar for implementing a 2fa similar to how Google and Apple do it: pop up a notification on your phone asking "Is this login you?"

Throw some structured data into the email. Email clients can detect and do the popup that hits a callback endpoint. Clients that don't support it still get the code via regular email.

I avoid the OS-layer implementation at the expense of the user having to confirm it's them, but this also means it works cross-device, which is what I'd want for 2fa anyway.

What is the win of this over webauthn (if the website supports it)? I guess the big win is that the graceful fallback is smoother.

Seems like a lot of moving pieces that only an OS vendor (who controls both the browser and the email client and could build the integration between them) could implement.

Webauth authenticates the device. Still need a way to authenticate the user then you can use webauth going forward on the device. This is assuming you’re using the TPM chip on the device and another FIDO2 device. Standard user won’t have a FIDO2 device like a yubikey but if their phone or computer is relatively new, they will have a TPM chip.
They don't need to be - this is a limitation of how the site has implemented magic links. When implemented best, clicking the magic link in your email should simply confirm the authentication, and your _original_ browser window/tab should be signed in.

That way, you can be signing in on a desktop browser, click the email link on your phone (or a different browser sandbox), and the desktop window will be signed in.

An adversary prompts the website to send you a magic link. You happen to click on it by chance. Guess who has access to your account?

As others have pointed out, either login when the user has both generated and clicked on the magic link from the same IP (browser, where possible), or use magic codes (as opposed to links).

It’s just like anti-CSRF tokens, lots of edge cases and it’s hard to get right.

I don’t have a pw manager on my mobile device nor do I want one, getting a 35+ char into an input field on my phone is a PITA.

Does a password manager and browser extension not fix this? I use 1password on iOS and almost never have to copy and paste or (worse) type a password in. I just FaceID then pick the credentials I want to use to log in and the extension fills in the relevant form fields.
> It’s just like anti-CSRF tokens, lots of edge cases and it’s hard to get right.

Do you have an example of the problems you've encountered? This is normally a turn-on-and-forget setting which is only slightly harder if you're using JavaScript to submit forms.

> I don’t have a pw manager on my mobile device nor do I want one, getting a 35+ char into an input field on my phone is a PITA.

What's your threat model for using long passwords but not using a password manager? I can understand why you might not want to store it on a cloud service even if it is encrypted but local storage is going to be more secure than entering it by hand if for no reason other than that it's harder to shoulder surf and harder to phish.

Whats so hard about anti csrf tokens?
yeah on Discord this is almost magic.
I really like the QR code system they implemented, pretty handy
Yeah the QR code login is slick, TikTok also uses that.

The only caveat for implementing is that you essentially need to have a mobile app for your site, while SMS/email auth are more universal.

The stock mail app on iOS just opens links in safari
one time code is far, far easier when apple is able to autocomplete it
Interesting - thanks for sharing!