Ash Authentication Phoenix: displaying authentication error

I’m relatively new to both Phoenix and Ash. I followed the getting started guides to get up and running and now have ash authentication working and a basic phoenix live view that is only available upon login.

To help me to learn more, I’m trying to rewrite a few old projects in Ash and Phoenix. In the current case, I need to login with username and password. No need for tokens. Users cannot register themeselves. I’ve got all that working fine.

The next issue is to redirect back to the login page with an authentication error on failed login. Currently it redirects to another page (as per the getting started guide).

I’ve been reading through the sources of ash_authentication_phoenix and I have found reference to authentication_error_container_class and authentication_error_text_class, which are in the default overrides. This indicates that it should be possible to pass an error message to the login form. But I just can’t seem to figure out how to go about it. I can’t find the name of what to set in order for that authentication error to display on the login page.

Perhaps a bit more curious is that, of the few projects I seem to have found that use ash_authentication_phoenix, not one of them seems to redirect back to the login page on failure. Is there are reason for this? Am I the one that doesn’t understand something basic, perhaps security related or something?

For the time being I simply solved it by redirecting back to the login form. It would have been nice to display an “Authentication failed” message as well, but no matter what I try (flash or otherwise), it’s not working. At least it’s better than rendering “failure.html”, which in turn renders app.html.heex, which should only be shown to authenticated users.

I find authentication is one of those areas that is almost impossible for any framework to do right.

I did use Ash Authentication but in the end I had to remove it and went with PHX gen auth and guardian.

I think the Ash core team are doing an awesome job in making Elixir app development more productive, so I don’t want this to be seen as a criticism of their hard work. I however have significant reservations in respect of Ash Authentication.

Some of the reservations I have include:

  • insufficient escape hatches in handling of actions such as implementing password checks
  • No sign-in audit records or recording invalid signins, no account lock out after too many failed signins.
  • Results in security flaws as users should not be able to brute force passwords or tokens unfettered.
  • Lack of control over controller flows, like confirmation and in your case redirects, it’s all concreted up and difficult to reason about.
  • Lack of customisation of the UI and view
  • Use of pow which does not yet officially support liveview sockets
  • Inability to add TOTP
  • Still requires a lot of touch points on the codebase to implement and what you’re left with is quite inflexible.

In my view the Ash Authentication approach is too inflexible, noting the above limitations and security concerns. The Phoenix Ash Authentication integration is also a step too far as you don’t get a normal liveview experience with a template or components to work with. IMO generating a regular liveview with a render function or heex template is the right thing to do here. Yes that is not the Ash way to generate code, but I argue the Ash philosophy of “deriving the rest” doesn’t hold for view/UI concerns and any attempt at “liveview escape hatches” will be both cumbersome and alien vs having a regular liveview module in your code base that you can change as you see fit.

The taste that Ash Authentication left, borderlined on a near rejection of the entire “Ash way” as far too risky. But just removing Ash Authentication I got the necessary flexibility, security, and control back.

My view is if you limit Ash to the resource/domain model concerns, use the Ash code API as you would a Phoenix context and the Ash Phoenix forms support then Ash delivers significant value. The highlights besides the powerful resource model are authorisation / access policies, multi-tenancy, easy APIs and automatic migrations as a bonus.

There is sufficient flexibility and escape hatches in the core of Ash to do anything you need, which is sadly not the case with Ash Authentication which necessarily has to reach up into the view/controller layer.

Thank you for your reply. You are pretty much reflecting what I was thinking. But I also thought that maybe it’s just that I haven’t learned enough about Ash, or in the case Ash Authentication yet and that there would be a simple, obvious way to implement what I wanted.

Just like you, I really don’t want to seem like I’m criticizing Ash as a whole. So far so good on that front. I’m still figuring a lot out, so I’m going to continue experimenting a bit.

Hi @lcabrini :wave:

My original intent was that putting the correct behaviour in the auth controller should be a job for the person integrating AshAuthentication, but it quickly became apparent that this was a very common use case. We wound up implementing a system involving a short-lived token which performs the attempted sign-in right in the live-view and then redirects to a new endpoint to set the session on success.

1 Like

Hi @adw632

Thanks for the great feedback. I agree that AshAuthentication constrains folks in some unexpected ways. You’re right that it’s hard for a framework to get right - especially on the first try. Regardless, I’m going to try and address some of the feedback here.

insufficient escape hatches in handling of actions such as implementing password checks

Agreed. We do have this issue open to track changing/removing the password constraints, but if you have ideas for where you’d like to see other escape hatches I’d be keen to hear them.

No sign-in audit records or recording invalid signins, no account lock out after too many failed signins.

This is true, but I’m not 100% convinced that this should be AshAuthentication’s job as it would be fairly easy to implement. That could also be an argument for just doing it. I am definitely open to being convinced. Keen to hear more about what specifically you’d like to see.

Results in security flaws as users should not be able to brute force passwords or tokens unfettered.

Zach and I talked this over at length. There was some discussion of rate-limiting, denylisting, etc, but we couldn’t agree (he is for, I am against) building it into the library. Again this is something that would be fairly easy to implement, but maybe would be more useful as a separate extension?

Lack of control over controller flows, like confirmation and in your case redirects, it’s all concreted up and difficult to reason about.

I disagree on this point. I made the default auth controller/plug as flexible as possible to allow folks to implement the behaviour they want on top of it. As a general rule I didn’t want folks putting authentication in their app without understanding what it was doing, so I left the default behaviour as bare-bones as possible. Turns out I may have been wrong on that because the consistent feedback I’ve had is that people want it to do more for them by default.

Lack of customisation of the UI and view

It’s very customisable - but it turns out not in a very useful way. I expected folks to only use the default liveviews while bootstrapping and then move on to build their own afterwards maybe reusing some of our components. Turns out that’s not the case and they’re being pushed a lot further than I ever imagined. I’m keen to work on generators for these views and even for them to eventually to become the default behaviour.

Use of pow which does not yet officially support liveview sockets

We use assent (which is part of Pow) for our OAuth flows, but that’s it. No other part of Pow is used and OAuth cannot be performed within a liveview, so I’m not sure how this applies.

Inability to add TOTP

There is no inability to add it; just no one has done it yet. The ground work has been done and I can see it being relatively simple to add a new strategy/add-on to support it. See the custom strategy guide for a place to add it. PR’s definitely welcome.

Still requires a lot of touch points on the codebase to implement and what you’re left with is quite inflexible.

As above I don’t think the problem is that AshAuthentication is not flexible enough - it’s just not flexible in the right ways. I’m glad you found a solution that works for you and I’m sorry to read that I nearly scared you away from Ash entirely.

Over all I want to say thanks for the valuable feedback and that there are no sacred cows here. I want to encourage everyone to raise issues, open PR’s or otherwise engage with us to improve the state of the entire ecosystem.

3 Likes

Thanks for your reply. Since I posted the original question, I’ve played around quite a bit with it and figured something out that is reasonable for now at least. Takes a bit of work, but that’s fine. Much of it was figuring out how it all works under the hood.

I’m a bit curious about the short-lived token. Where are these stored? In memory? Does it require a token resource? Because I didn’t set one up as the idea for this particular app was users cannot self-register, it’s all done by an admin. Registration is username/password. Users can’t even change their own passwords (the specs of an old system I built many years ago).

In any case, thanks for the great work so far! Elixir, Phoenix and Ash have together managed to make it fun to write code again.

It took me a bit of time to figure it all out, I had to jump quite a bit between code and documentation, but once I figured out how the overrides worked and what was available, it was fairly straight-forward. I think maybe it’s a documentation thing more than anything else. I grew up flipping through the Commodore 64 reference manual in search for answers, so I’m used to working that way. Not sure your average modern web developer has that much patience.

Again, there is nothing wrong with provide bare-bones behavior by default IMO, as long as people can figure out how to go about bending it to their own needs. In my case for instance, I pretty much never build your typical “websites” with things like self-registration. I build applications that happen to run in a browser, so an administrator (or in some cases HR officer will create a staff record, which will involve a user account being set up). I need a way to log users in and I need a way for an administrator to perform user management tasks. I’ve cooked something up that works well enough for me for now, but I’m sure there are better ways to do it.

Thanks @jimsynz for considering the feedback.

I think part of the experience of using Ash Authentication is that it is often one of the first things we need to think about when building out an app so we come into Ash not knowing it in detail and it’s not obvious how we would change or customise the Authentication view or the controller or replace with our own without digging into the internals of Ash itself. I did see the class overrides for the Ash view but I didn’t see how I could change the layout, order of elements in the view, even add additional elements (like TOTP) along with the password strategy.

I’m glad to hear that a generator approach for liveviews is being considered.

With Pow/Assent I refer to the issue not officially supporting liveview yet, which IMO is not ideal given liveview has been around for some time, it begs the question of that projects ongoing relevance to modern Phoenix apps.

This may sound harsh but having a dependency that is unable to keep up the Phoenix releases is something we have to be very wary about, it’s one of the biggest risks we face that a library is no longer maintained. I’m not saying in this case that Pow Assent isn’t maintained but clearly it’s fallen behind and no-one should indulge a security component that does not officially support liveview if you’re building a liveview app as there can be many edge case that need to be considered.

Happy to provide further advice in respect of handling failed auth attempts, account lock out and recovery and fail2ban type of behaviour for IPs that are attempting brute forcing on auth endpoints be they tokens or passwords.

Assent, Pow and Pow Assent are different projects.

Pow is the full blown authentication library.

Assent only handles SSO/OAuth request/callback phases and has no dependency on Pow.

Pow Assent glues everything together.

Assent has nothing to do with LiveViews or Controllers etc. Pow’s not full integration with LiveView has no effect on one’s ability to securely use Assent.

2 Likes

That may be the case and I don’t really care how pow is carved up, official liveview support is a current gap.

Until liveview is supported officially it is a risk using the Pow or Pow Assent ecosystem within a liveview app.

To reiterate: using Assent has nothing to do with Pow (its maintained by the same team, but its a very specific subset of the functionality) , and bears no relationship to whether or not Pow supports liveview. So Pow or Pow Assent don’t have any bearing on AshAuthentication and

  • Use of pow which does not yet officially support liveview sockets

is not correct.

1 Like

My reason for this conclusion is the open issues on how to best support liveview are still not resolved.

I am not saying it cannot be made to work with sufficient consideration of all the edge cases (many of which are captured in those issues), but there is no canonical or official documented approach that succinctly describes what is required to support liveview.

My point is, if was so easy and everyone is confident all the edge case are resolved then why is it not simply documented instead of sitting open and still unresolved?

The following issues were raised in April 2019, and Sep 2022 with still no settled position.

You’ve linked pow and pow_assent several times and those libraries are not used by Ash.

Those aren’t assent issues. Assent works just fine and there’s nothing in it that requires work arounds.

Edit: never mind I got confused about your reply. Thought your new links were still related. :laughing:

1 Like

Pow != assent. Those issues have no bearing on AshAuthentication. AshAuthentication uses assent, for which there is no web socket related issues (because there is no web socket related functionality).

3 Likes

Yeah, to iterate what has already been said, Assent is not Pow. It does have the same maintainer (me) and was grouped with Pow as it first was part of CoherenceAssent and later part of PowAssent.

Assent is a multi-provider framework that abstracts away OAuth/OIDC integration, and normalizes the user format. It is up to you how you want to implement that into your app. Assent doesn’t have any notion of controllers, sessions, plug, or phoenix. Assent’s only concern is communicating with the provider, being RFC compliant, and returning necessary data so you can redirect the end-user with the right params with whatever request handler you have implemented in your app (e.g. phoenix).

I feel confident that Assent can be used in any circumstances with no issues. AshAuthentication using that won’t become an issue down the road. And even if for some reason Assent becomes an issue it should be pretty straight-forward to replace it with another provider library.

Now, Pow is a full feature auth library for Phoenix that deals with session management and everything else, completely taking over the request handling. It is fair to be cautious with the lack of LiveView support. I will eventually resolve that, but for different reasons it is not trivial (which you can read more on in the issues). I’m also the lone maintainer of Pow, which is another reason to be cautious.

Everyone should definitely consider that, and evaluate whether Pow, AshAuthentication, another auth library, or code gen with phx.gen.auth is the best choice for their project. Pow is still maintained, and I would say well suited for REST applications. LiveView auth support with Pow can be implemented in many ways depending on what your requirements are.

Also I think it did take a bit, but authorization and session handling in Phoenix WebSocket seems mostly settled in LiveView now. I’m implementing Pow with LiveView currently in a larger financial platform with strict security requirements. This is my ideal use case for Pow, and I plan to implement support upstream once I have enough confidence in the setup (and time!).

I could write a lot more on auth, Pow, code gen, my current work, etc, but I think this is more than enough off-topic discussion. I do hope this shed some light :slight_smile:

3 Likes

Thanks.

I agree that pow assent is not involved as a plug, and it is therfore agnostic to routes, controllers or liveviews.

Unlike the (unrelated) pow which is a plug based solution and does have outstanding issues, and which I thought you were saying my claim was incorrect, but actually what you were saying is that my claim that Ash used pow was incorrect and I fully agree that I was 100% wrong on this point.

The plug/router/liveview integration that AshAuthentication provides is similar to what phx.gen.auth does, where Ash uses the (confusingly) named pow assent for authentication strategies.

Thankyou @danschultzer for taking to time to chime in with your careful and considered explanation on Pow Assent vs Pow.

I agree with your position and taking a conservative approach with these things before we can have confidence all the edge cases have been worked through.