-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update phx.gen.auth with sudo mode and magic links #6041
Comments
Not necessarily related, as the magic link wouldn't be the only means of auth, but do we want to have passkeys built in some time in the future as well? https://rmondello.com/2025/01/02/magic-links-and-passkeys/ |
I support your analysis that Magic Links do not change the threat model (option 2). And reducing the complexity/amount of code in security critical code paths seems to be a good idea in general. Passkeys and two Factor support by default would be great, but this would add complexity, maybe that would be a discussion for another issue? |
I think passkeys would be great but that would require creating some abstractions so we don't dump too much into the users. I think it is a separate conversation, maybe for v1.9? |
Please note that in our experience with Zotonic quite some people read their email on another device than what they use for browsing. Maybe take that into account when using magic links to log in. |
We had a conversation about this in the Security WG. The identity is tied to the emailThere is one important premise in Phoenix authentication system: the user identity is tied to their email account, therefore, someone with access to the email will have access to the account. Therefore, if someone creates an account with a typo and they don't correct it, then the user who owns the email shall effectively own the account. Suggestion to improve UX: when we tell the user has to confirm their account, we should show a link to their email. "An email was sent to [email protected], please access it and confirm your account". Expiring existing sessionsAnother concern is session fixation attacks. Someone may create an account for "[email protected]" with the hope that "[email protected]" will eventually create an account and they will have access to it. We already address this today since resetting your password always expire existing sessions. However, the magic link adds one additional concern in that people may just use the magic link and never reset their password, so session fixation attacks are easier. A solution here is that account confirmation must expire all existing sessions, except the one in the browser of the user confirming the account, if any. The downside of this approach is that, if I create an account in my computer and then confirm on my phone, I will be signed out on my computer. However, this worst case scenario where you sign in twice is the same as today, since you need to sign in to confirm your account. Summing upIn other words, account confirmation, password recovery, and sign in with magic link can all sign the user in, provided that, if we are doing so for the first time, previous accounts must be signed off. This should considerably simplify the amount of code we generate. The only difference is that we need to send different emails and welcome pages based on the token, but that's quite straight-forward. So overall:
So as long as the sudo functionality is less code than password recovery, which I am fairly confident it is, we should end-up with less code and more features. |
That wouldn't be a problem if the registration does not create a session in the first place. Why should we log users in just to tell them to use the magic link for confirmation? |
@SteffenDE we talked about this in the meeting. I don't remember which default we have right now but most SaaS (and perhaps most applications) will allow you to use the app before you confirm your account, because asking the user to swap to their email will add unneeded friction. So our design must consider that users will do this modification, even if our default is to require confirmation upfront. |
This is wrong, expiring all sessions is not enough. We need to guarantee that the user confirming the account actually has access to the system via password. If they don't, then they can only proceed by changing the password and signing everyone out. |
Here is a new proposal. I need to do some back of the napkin thinking if this will actually lead to less code or better security. The proposal above is quite along as it assumes both registration with magic link and with password is possible. We can trim it down by removing the latter. Magic links and confirmationMagic links and account confirmation will use the exact same token,
The welcome screen will look like this:
The scenario where the account is not confirmed and the password is set must require login. If the user does not have the password, they need to trigger the password recovery flow. Login pagesEither one page, two sections, similar to Slack. Or two pages.
Forgot your password can be a link to a separate page Password recoveryPassword recovery is the second type of token that we have. Changing email and passwordChanging email uses its own separate token. Changing email or |
Here is a simplified version of the proposal with only magic link registration: RegistrationRegistration is done exclusively with magic links (or say, Uberauth, although with Uberauth you can mark the account as confirmed). Magic links and confirmationMagic links and account confirmation will use the exact same token,
The welcome screen will look like this:
Login pagesTwo pages.
Changing email and passwordChanging email uses its own separate token. Changing email or |
Is this issue ok to discuss also producing a full LiveView experience for gen auth or no? |
Thanks for asking, I appreciate it. We already produce a LiveView experience for gen auth but if things are missing, I do agree this is a good place to propose them, so we do it all in one pass. If they are distinct enough, then I can separate it to another issue. |
What I'm referring to here with a "full Liveview" experience is the POST back to the server and a disconnect then reconnect after redirect. Many SPAs are using Fetch to make their auth POST and allowing their cookie to update the client from the result of the Fetch. This means they don't need to reload their application |
That's a separate discussion then because we would need to first add features for it in LiveView but it is not trivial. The reason why we reestablish the connection today is to make sure the WebSocket gets the update session. Otherwise the session in the WebSocket and HTTP diverge and that can lead to other issues, including security ones. One thing we could do is to allow the HTTP request to ping the WebSocket process, and then synchronize them, but that implies Distributed Erlang and perhaps that too much of an assumption for the built-in generators. We don't want someone to deploy an app to production with two machines and then learn auth only works half of the time (when it by luck goes to the same machine). However, if there is a LiveView feature that is already implemented today and we could use it, let me know. |
I like the goals of this change, but I'm wondering about it from a users perspective. By now phx.gen.auth gave you the bare "baseline" of what people understand to be authentication on the web: logins with email + password – even if very thoroughly implemented. Moving to magic links and and this "sudo mode" imo moves away from "no unnecessary fluff" to a, maybe simpler code wide, but more complex to understand and use solution. At least to me it starts to feel more prescriptive – where I need to "build back" to get to a different solution – rather than open to extension – where I'd add on top. I don't think such concerns should really block this being done, but wondering if there's ways to get ahead of them with the release of those changes. |
Hi @LostKobrakai, To be clear, more complex to understand and use solution for whom? Code wise, the bet is that it would be simpler. I would also say that it is actually more extensible than the current one for the most common use cases: 3rd party auth. You see, the generator code today is centered around password: it is a required field and also necessary for doing sensitive changes to your account. Therefore, if you want to allow users to sign in via GitHub in your application, you need to either ask them to add a password after the fact (unnecessary friction) or you need to do carefully touch several parts of the generated auth code. The magic link is closer to 3rd party auth, and by making the password optional while also generalizing the access to sensitive pages, we have a much better foundation for extension. UX and security wise, there are plenty of discussions on the trade-offs magic links. However, I would highlight that a study points out that 52% out of 28 million reuse passwords. And password will be there if you use managers. Finally, for apps that require account confirmation, magic links also provide a faster getting started experience, because it is a single email field and you are in. You set up passwords later only if you want to. |
The user – developer using phoenix – of the generator. email + password are quite well known and understood concepts with literal tons of resources around tradeoffs and requirements in implementation – and even if you don't read them you likely know how the email + password stuff works. With this updated solution I'd personally ask the following questions / thought – some are answered here, but my point is how would they be answered to people not reading this issues discussion: Some contextual notes in italic
Maybe that's just my context of answering "why doesn't phoenix do X" shining through here. None of these are concerns about the actual code doing the work behind this and I fully support simplifying. |
Fantastic, we should at least include some comments in the release notes as well as to the auth guides. |
Today phx.gen.auth generates the following functionality:
I believe we can improve both the user experience and reduce the amount of generated code with a few changes. Behind the scenes, forgot password already behaves as magic links with extra steps. My proposal is:
This keeps the threat model roughly the same given anyone with access to the user email today is already capable of resetting and changing the user password. Anyone with access to the email can also change the email used in the account too.
Furthermore, requiring the user to enter "sudo mode" upfront actually improves the user experience as changes to email/password will immediately take effect instead of requiring an email confirmation later, which the user may forget. It also simplifies the code considerably and provides a more reusable functionality (as it can be used to authenticate other features as done by GitHub).
My only question is if we should change the confirmation feature to be implemented on top of magic links too. Today, confirming an account does not sign the user in, as we want to avoid leaked tokens from giving user access. However, password recovery (and magic links) give a leaked token access. There are two possible arguments here:
Keep confirmation separate, as such emails are sent by default after sign in, and theoretically certain users may not even use password recovery or magic links (therefore they would never be vulnerable to token exposure)
Realistically most users will use magic links and password recovery. Furthermore a user who is being targeted will be vulnerable to those attacks anyway. Therefore there is little benefit in not giving users access after confirmation
In any case, using a magic link should automatically confirm the account anyway. Thoughts?
The text was updated successfully, but these errors were encountered: