alexslade

alexslade

Alternative title: A yearning for mackerel

I have a specific problem (I want to load a :team resource on the current_user that AshAuthentication puts into conn assigns. I could do this as a follow-on query, but I’m trying to do it in one).

But really, I want to learn to fish solve this myself.

I tried adding a preparation, at first to the global preparations, and later to get_by_subject which seems to be used by the password strategy.

Either way, I get errorless redirects back to the sign-in page as if I wasn’t logged in. I can’t see anything with logging set to :debug, I can’t see anything relevant with log_successful_policy_breakdowns or policy_breakdowns turned on.

What are my next steps for finding out why ash_auth is failing here? I’m assuming there must be another way to get to an error code, though I accept it may be something that I simply need to understand and avoid. (I also assume there may be a better way of doing this team load).

Any tips are much appreciated.

Showing Posts 1 to 6

barnabasJ

barnabasJ

Ash Core Team

Not seeing any logs seems a bit strange.

I assume you have some policies for your team, because AshAuth calls the read action with a private context set that uses the bypass policy set up by AshAuth

    bypass AshAuthentication.Checks.AshAuthenticationInteraction do
      authorize_if always()
    end

# in the policy  
def match?(_, %{subject: %{context: %{private: %{ash_authentication?: true}}}}, _), do: true

So, there is not really an actor at that point, which could lead to the load failing. You could use the accessing_from policy to allow a user to read their team if they are loading through a relationship

policy action_type(:read) do
  authorize_if accessing_from(User, :team)
end

And than the LiveUserAuth module would redirect because there is no user.

What did you set log_successful_policy_breakdowns / policy_breakdown to? I think it has to be a log level.

Otherwise, using a preparation for this is a valid approach.

alexslade

alexslade OP

After going through the source a bit more and experimenting, I found the issue. Our Team resource has multi-tenancy turned on. I’ve fixed the issue now (setting tenant in our router) but I’m still keen to learn how I could have debugged this better.

As it stands, I got zero warnings about the multitenancy issue (even with all debug stuff on), so maybe ash_auth is incorrectly swallowing a warning somewhere? (If I can work out where, I’m happy to PR something)

I’ll paste the configs and logs to demonstrate.

I have a user (99% setup by the ash_auth igniter)

  # user.ex
...
  policies do
    bypass AshAuthentication.Checks.AshAuthenticationInteraction do
      authorize_if always()
    end
    
    # I have temporarily set this policy to always allow to rule it out of debugging
    policy always() do
      authorize_if always()
    end
  end
...
actions do
  ...
  read :get_by_subject do
    description "Get a user by the subject claim in a JWT"
    argument :subject, :string, allow_nil?: false
    get? true
    prepare AshAuthentication.Preparations.FilterBySubject
    prepare build(load: :team) # This is the problematic line
  end
  ...
# dev.exs
...
config :logger, level: :debug
config :ash, :policies, log_policy_breakdowns: :debug
config :ash, :policies, log_successful_policy_breakdowns: :debug
config :ash, :policies, show_policy_breakdowns?: true
...

Here are the full logs if I don’t have the prepare statement active.

[info] GET /
[debug] Processing with WinkWeb.PageController.home/2
  Parameters: %{}
  Pipelines: [:browser, :require_session, :require_team]
[debug] Successful authorization: Wink.Accounts.User.get_by_subject


Policy Breakdown
unknown actor

  Bypass: Policy | 🌟:

    condition: AshAuthentication is performing this interaction

    authorize if: always true | ✓ | 🌟

  Policy | 🌟:

    condition: always true

    authorize if: always true | ✓ | 🌟


[debug] QUERY OK source="users" db=0.3ms idle=1002.1ms
SELECT u0."id", u0."role", u0."email", u0."team_id", u0."hashed_password" FROM "users" AS u0 WHERE (u0."id"::uuid = $1::uuid) ["8e59877b-82d6-4379-ac11-deeb88c8f99a"]
↳ anonymous fn/3 in AshPostgres.DataLayer.run_query/2, at: lib/data_layer.ex:767
[info] Sent 200 in 6ms

Here are the logs if I do have the prepare statement active

[info] GET /
[debug] Processing with WinkWeb.PageController.home/2
  Parameters: %{}
  Pipelines: [:browser, :require_session, :require_team]
[debug] Successful authorization: Wink.Accounts.User.get_by_subject


Policy Breakdown
unknown actor

  Bypass: Policy | 🌟:

    condition: AshAuthentication is performing this interaction

    authorize if: always true | ✓ | 🌟

  Policy | 🌟:

    condition: always true

    authorize if: always true | ✓ | 🌟


[debug] QUERY OK source="users" db=0.2ms idle=1292.0ms
SELECT u0."id", u0."role", u0."email", u0."team_id", u0."hashed_password" FROM "users" AS u0 WHERE (u0."id"::uuid = $1::uuid) ["8e59877b-82d6-4379-ac11-deeb88c8f99a"]
↳ anonymous fn/3 in AshPostgres.DataLayer.run_query/2, at: lib/data_layer.ex:767
[info] Sent 302 in 2ms
[debug] Phoenix.Router halted in :require_session/2
# At this point, the conn has no `current_user` and so gets bounced by our checks for current user 

What I can see is that the same SQL is run, the same policy is run (and passes), but I mysteriously don’t have a current_user being set on the conn. As I said above, I assume an Ash.Error.Invalid.TenantRequired is being thrown somewhere but caught by AshAuth.

barnabasJ

barnabasJ

Ash Core Team

I think this happens here:

https://github.com/team-alembic/ash_authentication_phoenix/blob/4a43c238c7de711551fc86d0a535c2dbd186cac0/lib/ash_authentication_phoenix/live_session.ex#L119

This calls the action on the user resource internally, and if that returns an error, nil is set as the user. It seems the tenant error is detected before any policies are run, so you don’t get a log for the team.

Not sure about how you could debug this better. But here is the approach I took.

I looked at it more like a regular bug than anything Ash specific. From the little LiveView experience I had, I knew that the user would be most likely set in a live_session. So i looked at the router setup and saw the ash_authentication_live_session setup by the ash_auth igniter and went to it’s definition. There I saw that the Module was added to the list of on_mount hooks. That lead me to look at the on_mount function in the module.
I saw the call to AshAuthentication.subject_to_user and looked at that function. There I saw the Ash.Query being constructed and the read being called.

I saw that there was this special context that would allow to bypass the policies and that there is no real actor yet. This is what I put in my first answer.

barnabasJ

barnabasJ

Ash Core Team

As a next step I would add a debug statement there. To see what is actually returned from the read.

I’m not sure how you would integrate the tenant here, though. Maybe instead of having just a preparation that adds the load, you create your own that looks at the subject to get the tenant and adds the load with an initial query that sets the tenant.

alexslade

alexslade OP

Thanks Barnaby, I may PR a logger statement there or something so that failure errors are at least logged.

No next step needed on the issue itself, I solved the tenancy issue with Ash.PlugHelpers.set_tenant in the router, this is picked up by AshAuth.

Thanks for your help :heart:

— All posts loaded —

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
itzmidinesh
Hi all, I’m working on a Phoenix project where I need to make external HTTP requests and I’m trying to choose between Req and HTTPoison....
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
leoschweizer
Hi, we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New
csmr
Hi all, I’m new here and would like to hear peoples views on my Elixir/Phoenix based game-engine! :smiley: Mutonex gamedesign was finish...
New
Stef-Verniers
Hello I’ve been coding in Elixir for almost two years now because of my current job where we implemented Elixir in our application. But ...
New

Other Trending Topics Top

dominicletz
Hey there, I haven’t seen a list of useful Elixir specific skills for the popular AI tools yet so I’m starting one here with my first. ...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
OndrejValenta
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly) AI dream has many faces, but general...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
kip
In 2021 I started a new library called Tempo with the objective of modelling time as a set of intervals - not as instants. In 2022 I gave...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews