Main differences between Pow and the upcoming Phoenix Auth?

As has been announced relatively soon (start of April) Phoenix is going to get its own built-in authentication solution, injected in the project through generators.

I’ve used Pow twice and liked it.

Can somebody give me a breakdown on what are the main pros and cons of the upcoming Phoenix auth compared to Pow?

10 Likes

There is some discussion about this here: https://github.com/danschultzer/pow/issues/508

6 Likes

Actually extremely helpful discussion there.

I think I’d go with Pow in this case. Even though mix phx.gen.auth seems like it’s giving you a full control, currently it’t not clear how security practice upgrades (and bugfixes, and new features) would even make their way into that once-and-done generated code that now lives directly inside the sources of your project. @josevalim, or anybody else working on it, do you have a plan for that?

So I definitely agree with @danschultzer more at the moment. We should however closely monitor how does the officially-blessed auth library fares with time.

6 Likes

Doesn’t seem like a big deal to update a few lines of code in your own repo after changes are introduced into phx.gen.auth.

Actually, a lot of companies build their own auth, because libs are bloated and it’s much easier to tailor your own code to your own, sometimes very complex, needs.

From my point of view mix.gen.auth gives you a minimum that suits a lot of simple apps. You can even check hex.pm auth, which is very similar to the one generated by mix phx.gen.auth.

Libraries like Pow (or devise) have that disadvantage that they have to cover most of the cases, which is pretty much impossible, right? Thus, when you hit a roadblock, you have to fork or create a feature request. At that point you start to regret using a lib for auth, because you could do it by yourself the way you need it with much less code to play with.

Obviously it depends on what you build, but in 98% cases Pow or Phx Auth are most likely both valid choices and the only difference is tinkering with options vs writing code.

5 Likes

José with his unique perspective (after having worked on Devise for years) has a great and detailed explanation of the tradeoffs of mix phx.gen.auth compared to other solutions like Pow on this blog post: https://dashbit.co/blog/a-new-authentication-solution-for-phoenix

I think integrating changes made to the phx.gen.auth generators would be similar to how you can integrate boilerplate changes in Phoenix generators like when upgrading from 1.4 to 1.5… creating a same named project with the new generators then using a merge tool. Not as easy as bumping a dep but not too bad and with different tradeoffs.

4 Likes

I feel this grossly exaggerates the willingness of your average programmer to do that. I do that, you do that, but most people I ever worked with won’t even guess that they should be doing it. Sad but true, at least in my bubble (which has been negative for most of my career, I admit).

Absolutely, this is true. It’s just that any smaller business or a hobbyist initiative would like to go for the lowest maintenance solution while still having access to the latest security best practices. Having the auth code injected inside your app definitely makes this harder.

Disagreed here. It’s unbelievably easy to make a very rookie security mistake when rolling auth by yourself. I don’t trust myself doing it, at all. Plus Pow has showed that it’s easy to customise its parts, should the need arise. Its customisations are opt-in which has the slight disadvantage of believing that magic drives your auth :003: but I think it’s worth it.

4 Likes

I imagine the same but I’ve known many people who can’t be trusted with a delicate operation like that. :confused: (Not to sound too negative, I also met fantastic individuals like in my last job)

I believe it’s high-time for our community to invent tools that not only can generate (and thus inject) code but also reverse-engineer it, analyse it, “recognise” it, a la Java bytecode instrumentation, maybe.

We should have a tool that says “you have phx.gen.auth in your project with 10 lines of code in it customised and different than the current latest version”.

We should have a tool that says “your Phoenix project’s ModelCase lacks Ecto imports” (soft-linting?).

I might try and start working on something like this in the future after things in my life calm down a bit (and after I close a few important open-source efforts).

1 Like

But does Pow give you really more security though, and at what cost.

I’d like to remind that the generation tool relies on secured libraries/components that are independently updated. The hashing (argon2, pbkdf2, bcrypt, :crypto, …), Phoenix Token/Plug Session, …

I haven’t use Pow, but browsing through the docs, if you need the functionalities that the generation tool provides, you need to add pow-specific code in many layers of your application, and often through code injection.

Why? Because this auth code is not only about authenticating a user against an email and password, or verifying a token for email confirmation. It’s far more than that: actually most of the code is about providing form validation (changesets), controllers receiving auth-related requests, routes … it manages the flow of the request from the template or a link in an email, to routes, controllers, context, shema/changesets and the DB. And most code is actually not security-related. And comes with many possible customizations.
Just the amount of code injection (use) to make pow achieve what the generation tool provides shows that.

Say we don’t trust our junior developers too much, what will prevent the developer to remove some pow changeset in the code? It’s not that if you’re using pow your code can never be compromised.

Maybe it’s interesting to evaluate precisely where things might mess up before arriving to the conclusion that generating that code in the codebase is not worth the risks – concretely which risks? For almost every generated context function, you have to provide a user, and in every query generated, we link the query with that user; as long as you provide the right user, I don’t see right now how one can mess it up.

The generation tool also allows the developers to know exactly how the authentication system works. I would prefer to work with developers that know what they’re doing instead of developers that use such a black-box, but only because this black-box is spread throughout all the layers of your application.

Browsing pow’s documentation, I don’t understand how to revoke a particular session in a running system. For example, the code generated by the tool stores a token for every session in the database. I can delete such a token to revoke one specific session; and I clearly see in which table/field as I have total control; how do I do that with pow? (maybe I missed something, but it’s to express my last point; not only the flow of the requests is highly custom, but even the authentication mechanism itself can come in many different shapes).

For me it’s mix phx.gen.auth all the way.

Btw sorry if I sound harsh; while I believe changesets/context/controller/template/route/… all of this should be generated instead of injected with use and all possible configs, I would still use a library that manages the cookies, provide different plugs to extract a token from a header/cookie, plugs for Absinthe, etc. there’s a lot of stuff (more specialized) one can build around the generated code. I’d also rather see more tutorials to educate devs, helping understanding and customizing the auth code, rather than relying on tools that inject code in an obscure way, in every layer.

PS: sorry if there are mistakes; again, I didn’t use Pow, I only judge to what I read quite quickly through the docs, and understand why jose took the other approach of generating code. I personally use the generation tool for my project as it makes more sense to me and just seems the most logical way, as to my understanding expressed above.

1 Like

I actually agree with this. I like all my code to be visible – obviously not the dependency libraries but everything else should be visible, inspectable, and changeable.

But we have to take trade-offs into account. We don’t have good tools that recognise the previously generated code and are able to upgrade it when a bugfix is issued upstream. Maintainability is a huge boon of the more invisible code.

We should be able to do something with time about this. Stuff like module / function attributes shoud allow us to instrument the project’s bytecode – or, even better, just have something that parses the source code to AST and manipulate that (and then put it back in the source).

Until we have a much better tooling for manipulating generated code after the fact, then “invisible” code hiding in the library code is still the lesser evil.

1 Like

My two cents: I think it’s a very reasonable choice to have auth as part of the framework but as a generator that creates code in your application.

I worked with few kinds of auth and I can tell that unless the app is really simple, it’s always complex, so it’s about having this complexity as part of your app or as some kind of super extendable “one size fits all” solution inside of the framework. The problem with latter is that sooner or later you will have to go in and extend it and then you will have to deal with all the complexity anyway and since it’s a more general / abstract solution, there will be more complexity to deal with then if you have one specific to your app.

The way generators work would be the same as the code we have when we generate CRUD things, simplest most basic scenario that you can build upon according to your logic.

I’d argue it will be easier, not harder in the long run. If we want to make life easier for users with very standard solutions then we are better off with some ready made CMS for making standard websites, but it’s not what Phoenix is supposed to solve.

That is correct for low level things, nobody should invent their own logic of hashing or storing passwords and such, but as for higher level abstractions - it’s different. Trying to create an abstract enough solution so that it covers every use case will probably fail in that people will abuse it. Letting every app do what it wants but giving it a good starting point is the way to go in my opinion.

In the end it’s about the kind of users that you wish to serve, I’d argue that Phoenix’ user base will appreciate the generators way.

2 Likes

Well the point is that you change the generated code as much as you want, to your needs. After you generated the code, it’s now your application code.

If you do need a tool to somehow keep track of that code that you are likely to change, then the generation tool is just not a good fit for you, to my opinion.

But yes, if there is a security hole found, how am I now warned? Well, it will never happen:-D because this code has been tested thoroughly, and reviewed by expert programmers.
And it is also your responsibility to thoroughly understand the code, as it’s part of your application and not a third-party code.

Then I want to add something: say that someone introduces a bug in the generation tool, how fast will that be noticed?
Compare that to a bug in the library you are using, how fast will anyone notice? Pow for example relies on how many active developers with a thorough understanding of its code?

I’d bet that a bug introduced in the generation tool will be detected much faster, as 1. it will be integrated into Phoenix which has a lot of contributors and top developers are working on it and 2. every developer using the tool will forcibly have to study the code.

I agree that you won’t be able to update your code automatically, but you will hopefully be notified very fast for any bug VS a bug that might go unnoticed for a very, very long time, but with automatic code update.

1 Like

I feel we started talking past each other so let’s try and nail some misconceptions about what I said.

Some people were convinced programming was “a solved problem” back in the 1960s. It’s unrealistic to say that nothing major will ever change. Experts, too, make mistakes – all the time.

That’s the ideal theory. And it’s a discussion similar to that of FP vs everything else: “Why do you need immutable state? Mutable state is fine, you need to thoroughly understand your application’s state!”. Point is, tooling should reduce the cognitive load on the programmer. We are human and can’t keep everything in our heads. That’s why we have these machines in the first place, no?

Additionally, almost nobody that I’ve ever met in the commercial programming actually understands security (me included). That’s why there are experts that craft those libraries / frameworks for us.

That’s a pretty random assumption.

…Which changes nothing, because your generated code resides inside your project and won’t be automatically changed.

Again, an assumption. Many programmers are quite busy and they’ll completely miss that. Most of the commercial programmers have their time mercilessly sliced into sprints and tasks and stories. You think the average project manager will ever tell you “OK, use all of today to upgrade libraries and edit files in our project accordingly so we are on top of our dependencies”?. For 18.5 years of career I’ve met two such people. How many did you meet? Is your current workplace allowing you that?

“Hopefully” being the key word here. Unless you subscribe to the GitHub releases of a project – which many programmers never do! – then you’ll miss it.

I do mix deps.update 1-2 times a month exactly for such reasons. People fix bugs in their open-source projects all the time. They also do performance improvements, change code to adhere to best practices (especially in the security area!) etc. It’s very sensible to periodically update your dependencies. Bonus points for reading release notes as well.


Just asserting “you have to forcibly study your generated code and manually update it periodically” is not productive. And it’s definitely something that most project managers will frown at. “Wasn’t that thing working? Yeah? Why touch it, we have a backlog of 50 issues back here!”

I feel that having a library that I only update through mix deps.update, recompile, and I am then safe against an obscure credentials hijack exploit is the better option versus updating the library and re-generating the code – which will result in two slightly different generated codes, and you’ll have to go clean after the generator, and you might make a mistake in the process.

Not sure how the latter option sounds more appealing to you, will you explain it to me?

3 Likes

Pow is both. By default it’s a library but if you want to further customise things and make it entirely your own, you can make it generate code inside your project. Being a library plus the ability to opt-in for generated code is IMO superior compared to only having generated code.

That is 100% correct and is the exact problem with using a library vs. customising generated (or hand-crafted) code, yes. As mentioned above, this also mistakenly evokes the feeling of “magic” which is something I am not happy with. But the trade-offs of the other option seems worse to me.

Let’s not go that far. As mentioned in my above reply, I argue that cognitive load should be reduced. I definitely am not arguing for all of us to start using WordPress, no.

I am inclined and tempted to agree but you surely remember all the [dumb] security blunders in SSH (Heartbleed) and HTTPS (TLS 1.3) in the last several years, yeah? What guarantee do we have that the current widely accepted approach of doing auth does not have a fatal flaw? What then? We rely on millions of programmers to update their generated code? Very unrealistic. Most projects are governed extremely inefficiently.

I appreciate it as well and I am excited to see how it will look in its first release. The point of this thread was to discuss trade-offs. My opinion still is that while generated code is definitely more explicit and gives you a complete control – which I very much like, mind you! – it also has the fatal flaw of not giving you a path to [semi-]automatically update the said generated code when the authors find a better way of doing the task you use their generated code for.

4 Likes

I was used to though the same, but in my research to decide what OAuth2 package I would use in my project I saw that they are not that secure as one would though, thus I am writing my own one. This is sad to conclude, but is what I observed from my bubble.

Developers writing packages based on standards should question what the standard says is optional, and make it required when letting it be optional weakens the security, and I am looking at you state and nonce parameters in OAuth2/OpenIDConnect implementations, that are optional, but should indeed be REQUIRED, because for me security must be opt-out not opt-in.

DISCLAIMER: I work in the security space, thus I am more strict with the security requirements of anything I use.

2 Likes

Then we have two options: We have to adjust our app or we have to fix it after update when Phoenix adjusts their part. My point is that the first one is easier :slight_smile:

If we mention once the hobbyist, then the junior programmer, then the company in full agile mode with strict deadlines, … there will always be some argument.
The tool is optional. Developers can evaluate the trade-offs according to the environment they work in, the type of programmers they have, the time/money they can invest, their skills,…; trade-offs are obvious, the generated code becomes application code and should then preferably be understood; if one can’t afford it – don’t generate the code.

A reverse-engineering tool for detecting flaws from the once generated code? Nope. Will not happen. As you can change the generated completely to your needs.

Most generated code is trivial MVC stuff. I don’t need to understand how argon2 and :crypto work. That indeed, I leave to security experts. The generated code does not perform low-level security-related operations.

Reasons are too obvious to mention why Jose went for this approach. A developer can evaluate the trade-offs; you consider you prefer to delegate that code to a library, that’s also a choice.
For many (and I believe most), that will be a better trade-off than a lib that affects every layer.

I’m going in circles, so I’m off and leave place for other participants :sweat_smile:

1 Like