Ash authentication with Auth0 in api pipeline

Hi! We are having issues when trying to set the actor going through the :api pipeline

Following the guides from Ash Authentication I was able to login using Auth0 on the browser, but when I want to do an API request setting the access_token generated by Auth0 in the headers, the actor is not present.

This is the plugs I have in the router

import AshAuthentication.Plug.Helpers
...

pipeline :api do
  plug :accepts, ["json"]
  plug :load_from_bearer
  plug :set_actor, :user
end

I was expecting load_from_bearer plug to get the user_info from the jwt access_token set in the Authorization: Bearer ******* header

However, when doing an api request and inspecting the conn map, the actor is nil

What I am missing? Let me know if it’s not clear enough

Hello!

Can you show the configuration of the strategy on the relevant resource and the relevant output of mix phx.routes? See below which routes are relevant.

I had an issue of mapping up the name of the resource and strategy properly, so it would fail to resolve it as the resource would be called MyApp.Accounts.Account rather than MyApp.Accounts.User, and there were some places I had missed to update.

It was a while though, but if you inspect the JWT token I think there should be a reference to the name of the resource, which should correspond to a piece of the route(s):

  GET   /auth/account/password/sign_in_with_token  MyApp.Accounts.Account.password :sign_in_with_token
  POST  /auth/account/password/register            MyApp.Accounts.Account.password :register
  POST  /auth/account/password/sign_in             MyApp.Accounts.Account.password :sign_in
  GET   /auth/account/google                       MyApp.Accounts.Account.google :request
  GET   /auth/account/google/callback              MyApp.Accounts.Account.google :callback

Here you can see that for the MyApp.Accounts.Account resource the routes start with "/auth/account", and this needs to be reflected in various places, I learned the hard way. For example, the @current_user attribute in LiveView was not set as a result and I had to update that to be @current_account, I think… (need to confirm this, so no Trust me, bro on this one)

The symptom was that my actor was not set when I expected it to be, and it was caused by my refactoring of MyApp.Accounts.UserMyApp.Accounts.Account. I had to hunt down the places where it was resolved in the imported plugs and so on. My issue was however primarily in a LiveView context, not an API with JWT tokens.

This part in your pipeline could be relevant given what I said above:

  plug :set_actor, :user

The :set_actor plug argument subject_name – :user here – is the relevant part here. See AshAuthentication.Plug.Helpers.set_actor/2 @ GitHub

And looking at AshAuthentication.Plug.Helpers.retrieve_bearer/3 – which is called by the macro-defined function :load_from_bearer added by use AshAuthentication.Phoenix.Router at the top of your router – we can see this logic:

with {:ok, %{"sub" => subject, "jti" => jti} = claims, resource}
           when not is_map_key(claims, "act") <- Jwt.verify(token, otp_app, opts),
           {:ok, token_record} <-
             validate_token(resource, jti),
           {:ok, user} <-
             AshAuthentication.subject_to_user(
               subject,
               resource,
               opts
             ),
           {:ok, subject_name} <- Info.authentication_subject_name(resource),
           current_subject_name <- current_subject_name(subject_name) do
        conn
        |> Conn.assign(current_subject_name, user)
        |> maybe_assign_token_record(token_record, subject_name)
      else
        _ -> conn
      end

It tries to identify the correct resource to load based on the JWT, and if it fails along the way, the conn will be untouched.

So, if AshAuthentication.Plug.Helpers.retrieve_from_bearer/3 fails, there will be no assign corresponding to current_subject_name, which also needs to match the argument passed to :set_actor after, as that plug in essence just takes the previous assign and adds it as the actor.

If there’s a mismatch there, such as in my case when I refactored the name of my resource, it would just get nil when attempting to access the subject_name because I had not updated the subject_name in some places.

So if you have a different name for your resource, this could be a reason. If you do not though, try to turn on `AshAuthenticationDebug to see if you can get some more information that way and we can see what else we can discover.

1 Like