Access `get` `plug_opts` or `options`

phoenix
router
Tags: #<Tag:0x00007fbcac799620> #<Tag:0x00007fbcac799378>

#1

Question

The documentation for Router mentions that all routing macros (get, post, etc…) can take a plug_opts and options arguments.

From what I understand, when doing this:

get("/foo/bar", FooController, :bar, [a: 1, b: 2])

The Router will call the function bar in FooController with a Conn and its params.

However, the extra options ([a: 1, b:2] in my cases) are not available to the function.
Should they be ? Is there a way to get those params ?

Side note

To give some context, I would like to be able to write something like this:

get "/foo/bar", FooController, :bar, [role: "some_role"]
get "/foo/baz", FooController, :baz, [role: "some_other_role"]

And I want my controller’s function to be able to receive this “role” field, to test if the conn contains the proper role.

Since the [role: ...] plug options would be different for each route, I don’t think I can use a pipeline for that, since I would not be able to initialize the pipeline’s options:

pipeline "check_role" do
  plug MyPlugThatCheckRole, [role: ??????] # How do I configure 
end

scope "/foo" do
   pipe_through "check_role"
   
   get "/foo/bar", FooController, :bar, ????? # How would I pass the role here ?
   get "/foo/baz", FooController, :baz, ????? # How would I pass the role here ?
end
defmodule MyPlugThatChecksRole do

  def init(???) do
    # This is only called once when the plug is defined in a pipeline, right ?
  end

  def call(conn, opts) do
    ## Is there a way to send the options from the `get "/foo/bar`, FooController, :bar, ....` line here ???
  end

end

#2

The supported options are documented under Phoenix.Router.scope/2

So

get "/foo/bar", FooController, :bar, [assigns: %{role: "some_role"}]
get "/foo/baz", FooController, :baz, [assigns: %{role: "some_other_role"}]

should merge the :role value into conn.assigns making it possible to retrieve the value in the action function with, for example, conn.assigns[:role] or Map.fetch(conn.assigns,:role).

Note:
If you are using an authentication plug in your router then both user roles and the role required by the route should be available inside the authentication plug because the pipeline is only run after the route is matched.

It should be possible to create an authorize_user/2 function plug similar to authenticate_user/2 that checks that the required role is included in the user roles. That plug could then be used directly in the router pipeline to stop the controller from being called or inside the controller to stop the action from being invoked.