(ArgumentError) flash not fetched, call fetch_flash/2

Hi All,
I am following this documentation to setup Pow auth and role based access to my APIs.
https://hexdocs.pm/pow/api.html
https://hexdocs.pm/pow/user_roles.html

Based on this, I am trying to restrict access to certain APIs based on admin role.

If I provide invalid access_token, the API returns 401 - Not authenticated (Working fine)
If I provide a valid admin user access_token, API returns the expected JSON output. (Working as expected)
However, if I provide a valid non-admin user access_token, API returns the following output.

[error] #PID<0.2419.0> running MyAppWeb.Endpoint (connection #PID<0.2418.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: GET /api/v1/customers
** (exit) an exception was raised:
    ** (ArgumentError) flash not fetched, call fetch_flash/2

What is this error due to and how do I gracefully return a proper error code and error message when non-admin access_token is provided for the API?

Thanks,
Rajasekhar.

You should show the relevant sections of the code as well.

By default the :api pipeline in phoenix does neither use sessions nor flash messaging. So you want to make sure to now use them in api requests or add the relevant plugs to enable the features.

Hi dimitarvp,
My router pipeline and scope is as follows.

  pipeline :admin do
    plug MyAppWeb.EnsureRolePlug, :admin
  end

  scope "/api/v1", MyAppWeb, as: :api_v1 do
    pipe_through [:api, :admin]

    # API specific to Admin role
    resources "/customers", CustomerController, except: [:new, :edit]
  end

Hi Lostkobrakai,
Alternatively, I am also using plug MyAppWeb.EnsureRolePlug, :admin inside the controller as well. How do I use within api requests such that I return deferent set of results whether admin or user using the api? Thats my actual need.

Thanks,
Rajasekhar.

As @LostKobrakai explained, you have to specifically add other plugs to your pipeline (ones that are included in :browser but not in :api).

Hi dimitarvp,
I have included other plugs mentioned in browser, however I still get the same error.

Here is my new pipeline.
  pipeline :admin do
    plug MyAppWeb.EnsureRolePlug, :admin
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug Pow.Plug.Session, otp_app: :my_app
  end

I have just excluded the following couple of them which seem more specific to browser.

plug :accepts, ["html"]
plug :put_secure_browser_headers

If I’m not mistaken, plugs are executed top down in the pipeline, so your EnsureRole plug should be put at the end of the pipeline.

If I move the EnsureRole plug after fetch_flash and fetch_session plug entries, flash not fetched error is gone. However, I get the new error now.

[error] #PID<0.4524.0> running MyAppWeb.Endpoint (connection #PID<0.4488.0>, stream id 2) terminated
Server: localhost:4000 (http)
Request: GET /api/v1/customers
** (exit) an exception was raised:
    ** (UndefinedFunctionError) function MyAppWeb.Router.Helpers.page_path/2 is undefined or private

Following is the code snippet thats causing the issue.

alias MyAppWeb.Router.Helpers, as: Routes
...
    defp maybe_halt(_any, conn) do
      conn
      |> Controller.put_flash(:error, "Unauthorized access")  # Issue with this is resolved.
      |> Controller.redirect(to: Routes.page_path(conn, :index)) # This is causing the issue now
      # |> put_status(403)
      # |> json(%{error: %{code: 403, message: "Forbidden access"}})
      |> halt()
    end

However, if I use put_status() instead of the error code, it works fine.
Whats the reason why is page_path undefined though I have alias defined for Routes? And how to resolve this issue?

Might be a too obvious a question but do you have a route for page in your router.ex?

Hi dimitarvp,
Yes, I have a route for page, thats created by default when the project was created. However, we won’t be using this and plan to comment/remove this route.

  scope "/", MyAppWeb do
    pipe_through :browser

    get "/", PageController, :index
  end

Please hold. Looks like I have created this project as --no-html and copied routes from other project. Thats how this page route is added.

Thats gives me a doubt.
Is Routes.page_path specific to having page route or having a project with html?

It is not enough to copy like this… You also need the appropriate config, and web config.

It’s easy to create 2 projects, one with --no-html and the other without, and make the diff.

I will check that out. Thanks kokolegorille.