How do you recommend I connect an Ash/Phoenix app to a bespoke auth backend? The backend provides an endpoint that takes an email and password and returns a session key, and an endpoint that takes the session key and returns roles. The Ash/Phoenix app will provide an email/password login screen and store the session key for use in API calls to a resource server. Is ash_authentication a good fit for this use case? Or do I wrap the backend in an Ash resource and write a plug? For context, I’m new to Ash, Phoenix, Elixir, and web apps, in general, so there are gaps in my understanding.
AshAuthentication could be a good fit, but ultimately all that you need to do to make things work is to “get an actor however you want”, i.e in a plug, and assign it with Ash.PlugHelpers.set_actor/1
. So you can definitely do something without AshAuthentication.
Ah, thank you for pointing me to Ash.PlugHelpers.set_actor/1
.
Would you consider using a plug to be idiomatic in an Ash app? Would you ever use either of the strategies outlined here?
In your case, while I would probably create a resource with a single generic action for authenticating the user from the external service, I would likely not attempt to wrap the external API “generically” in a resource.
For example:
defmodule MyApp.Accounts.Authentication do
use Ash.Resource
action :authenticate, :struct do
constraints instance_of: MyApp.Accounts.User
argument :email, :string, allow_nil?: false
argument :password, :string, allow_nil?: false
run fn input, _ ->
case Req.get(....) do
{:ok, ...} -> MyApp.Accounts.get_user_by_id(....)
end
end
end
end
The custom authentication strategy could be a viable route, but ultimately it seems to me like what you are doing is so simple as to be better fit on the “edge”, i.e a simple plug who calls an action that ultimately retrieves the actor. Doing stuff with plugs or at the ingress layer of whatever you’re doing is totally fine and normal in an Ash application
There are a lot of potential ways to do it, like perhaps it’s a read action on the user resource that fetches the necessary info and then filters the query. As long as the logic is modeled as an action, generic or otherwise, then you’re doing it right as far as I’m concerned