supabase / auth

A JWT based API for managing users and issuing JWT tokens
https://supabase.com/docs/guides/auth
MIT License
1.55k stars 375 forks source link

Email Links don't pass through enterprise email systems #713

Open kylerummens opened 2 years ago

kylerummens commented 2 years ago

Bug report

Describe the bug

One of the organizations that uses our web app has an enterprise email system (part of the Microsoft platform?). As part of the system's security, when an email is to be delivered to a user's inbox the system will open the email, scan through the email, and open all of the links that are contained within the email. If the system deems that the email is safe, it then delivers it into the user's inbox.

The issue with this system is that when a reset-password link (or any auth link) is sent through this system, by the time it reaches the user's inbox the link has been opened and the single-use token is expired.

To Reproduce

The only way to reproduce this behavior would be to send a reset-password link (or any other auth link) to an email address that is controlled by an enterprise system. I'm not certain which systems work like this, except that this certain organization's system does.

Expected behavior

As seen from these discussions

a single-use token is important for security purposes. However, a possible alternative that would work for these systems would be to have an access_token that isn't single-use, but rather has an expiration timestamp (10 minutes, one hour, one day, etc.). I'm not a security expert, but this seems like a good solution that would solve the enterprise email issue.

rokk4 commented 2 years ago

Same here, not fit for production with this.

chrisb2244 commented 2 years ago

I think (although, if there's a good way to confirm this, that would be appreciated) that this is also causing an issue for me - some of my users are reporting an inability to log-in. On the first user, I created a page to track the URL hash parameters and redirect to a more descriptive page, because I thought they'd waited more than the expiry time (hour, day, whatever). But when I attempted to reproduce with my gmail account - no problems. The first link was invalid, but regenerating a new link and then immediately using it had no problems (my first reporting user said this wasn't possible even on repeated attempt, and when I deleted their account and had them recreate it, still no dice).

Now I see in the supabase auth-logs page (app.supabase.com/project//auth/logs) that the requests and verify attempts are close together for the next user who reported it, so it's not an expiry time issue...

florianwalther-private commented 1 year ago

I have a question regarding this: Does this also activate the link (verify the email address) or is there some mechanism that prevents this? Because everyone is writing that the link is just invalid.

eleddie commented 1 year ago

Any updates on this?

erikgoins commented 1 year ago

@florianwalther-private it does activate the link and verify the email, but then the user can't add a password.

This will also cause them to fail the password reset, again because the link has been used.

Basically meaning they can't access the system at all.

florianwalther-private commented 1 year ago

@erikgoins Thank you for the clarification!

EskelCz commented 1 year ago

@hf I've seen similar issues from Gmail on iOS as well, probably from attempts to preload the page. Can we get an option to use time window instead of a single use token? I think this is critical to make it more reliable and production ready.

andivis commented 1 year ago

I worked around the issue by changing the link in the invite user email template to

{{ .SiteURL }}sign-up?url={{ .ConfirmationURL }}

My page has a button that redirects to searchParams.get('url')

erik-beus commented 1 year ago

I worked around the issue by changing the link in the invite user email template to

{{ .SiteURL }}sign-up?url={{ .ConfirmationURL }}

My page has a button that redirects to searchParams.get('url')

We had to do the same thing using a custom page.

ham-evans commented 8 months ago

I worked around the issue by changing the link in the invite user email template to

{{ .SiteURL }}sign-up?url={{ .ConfirmationURL }}

My page has a button that redirects to searchParams.get('url')

We did this same thing, but ran into some issues (Universities using Outlook) where their system would then go visit the next links (I'm assuming the HTML pre-fills the href) and invalidate the link still...

Think I may be switching to the {{ .Token }} system and having the user paste it but that feels super lame. I saw potentially putting a captcha on that button to avoid systems from pressing the links? But not sure thats a much better solution.

Novistus commented 3 months ago

I worked around the issue by changing the link in the invite user email template to {{ .SiteURL }}sign-up?url={{ .ConfirmationURL }} My page has a button that redirects to searchParams.get('url')

We did this same thing, but ran into some issues (Universities using Outlook) where their system would then go visit the next links (I'm assuming the HTML pre-fills the href) and invalidate the link still...

Think I may be switching to the {{ .Token }} system and having the user paste it but that feels super lame. I saw potentially putting a captcha on that button to avoid systems from pressing the links? But not sure thats a much better solution.

You can set up an invisible button as a trap to the bot that when pressed, it removes the real button. Also add an 1s disable delay on first load to the actual real button. For us doing a combination of both worked.

AndreiHudovich commented 3 months ago

This is a real HUGE issue, Supabase do you have any updates on it?

iamjonjackson commented 2 months ago

+1 highly frustrating

Gabsha commented 2 months ago

The supabase docs has a section on email pre-fetching security systems that detonates unique links:

https://supabase.com/docs/guides/auth/auth-email-templates#email-prefetching

From my experience, the {{ .ConfirmationUrl }} approach failed intermittently, as if those system would also trigger url-encoded search params redirection links.

The {{ .TokenHash }} approach suggested here in invite and password reset email seems to have solved the issue for us.