itopiz

itopiz

In my GraphQL schema, I am using an authentication middleware similar to the one in the documentation. This middleware is set for all mutation fields.
In some mutation fields, I also want to use the Absinthe.Relay.Node.ParseIDs middleware.

In case a user is not authenticated and submits a mutation with an input that does not pass the ParseIDs middleware, the server returns the error message:

In argument \"input\": In field \"userId\": Could not decode ID value `foobar'

but I was expecting:

unauthenticated

After reading the Absinthe.Middleware documentation, I understand that both the authentication and ParseIDs middlewares are run anyway.

All middleware on a field are always run, make sure to pattern match on the state if you care.

When no user is authenticated, is there a way to receive the error message “unauthenticated” regardless of the input provided?

Showing Posts 1 to 9

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Are you not getting the unauthorized error at all? Or are you getting both unauthorized AND the parseids error?

What absinthe / absinthe_relay error are you getting?

itopiz

itopiz OP

I only get the ParseIDs error message if no user is authenticated and the userId is invalid. If no user is authenticated and the userId is valid, then I get the error message from the authentication middleware.

$ mix test test/myapp_web/schema_accounts_test.exs:163
Excluding tags: [:test]
Including tags: [line: "163"]



  1) test given no user is signed in, changeUserUsername should return an error with code 'UNAUTHENTICATED' (MyappWeb.SchemaAccountsTest)
     test/myapp_web/schema_accounts_test.exs:163
     match (=) failed
     code:  assert %{"data" => %{"changeUserUsername" => nil}, "errors" => [%{"message" => "Cannot process the query because no principal is authenticated.", "path" => ["changeUserUsername"], "extensions" => %{"code" => "UNAUTHENTICATED"}}]} = result
     right: %{
              "data" => %{"changeUserUsername" => nil},
              "errors" => [
                %{
                  "locations" => [%{"column" => 0, "line" => 2}],
                  "message" => "In argument \"input\": In field \"userId\": Could not decode ID value `foobar'",
                  "path" => ["changeUserUsername"]
                }
              ]
            }
     stacktrace:
       test/myapp_web/schema_accounts_test.exs:177: (test)



Finished in 1.2 seconds
43 tests, 1 failure, 42 excluded

Randomized with seed 365933
benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

How are you setting up the middleware?

itopiz

itopiz OP

The authentication middleware is setup in the schema:

defmodule MyappWeb.Schema do
  @moduledoc false

  use Absinthe.Schema

  use Absinthe.Relay.Schema,
    flavor: :modern

  import_types(MyappWeb.Schema.Types.Common)
  import_types(MyappWeb.Schema.Types.Accounts)
  import_types(MyappWeb.Schema.Types.Authentication)

  import_types(MyappWeb.Schema.Common)
  import_types(MyappWeb.Schema.Accounts)
  import_types(MyappWeb.Schema.Authentication)

  query do
    import_fields(:common_queries)
    import_fields(:authentication_queries)
  end

  mutation do
    import_fields(:accounts_commands)
    import_fields(:authentication_commands)
  end

  def middleware(
        middleware,
        %Absinthe.Type.Field{identifier: field_identifier},
        %Absinthe.Type.Object{identifier: object_identifier}
      )
      when field_identifier not in [:sign_in_user, :sign_out_user] and
             object_identifier in [:query, :subscription, :mutation] do
    [MyappWeb.Middleware.EnsureAuthenticated | middleware]
  end

  def middleware(middleware, _field, _object) do
    middleware
  end
end

Here is my authentication middleware:

defmodule MyappWeb.Middleware.EnsureAuthenticated do
  @behaviour Absinthe.Middleware

  def call(resolution, _opts) do
    case resolution.context do
      %{principal: _principal} ->
        resolution

      _ ->
        resolution
        |> Absinthe.Resolution.put_result({:error, unauthenticated()})
    end
  end

  defp unauthenticated() do
    %{
      message: "Cannot process the query because no principal is authenticated.",
      extensions: %{
        code: "UNAUTHENTICATED"
      }
    }
  end
end
itopiz

itopiz OP

And the ParseIDs middleware is setup this way:

defmodule MyappWeb.Schema.Accounts do
  @moduledoc false

  use Absinthe.Schema.Notation
  use Absinthe.Relay.Schema.Notation, :modern

  alias MyappWeb.Resolvers.Accounts

  object :accounts_commands do
    @desc "Change the username of a user."
    field(:change_user_username, type: :change_user_username_payload) do
      arg(:input, non_null(:change_user_username_input))

      middleware(Absinthe.Relay.Node.ParseIDs, input: [user_id: :user])
      resolve(&Accounts.ChangeUserUsername.resolve/3)
    end
  end
end
itopiz

itopiz OP

For information, there is no problem when I also use Absinthe.Relay.Mutation.Notation.Modern and setup the ParseIDs middleware this way:

defmodule MyappWeb.Schema.Accounts do
  @moduledoc false

  use Absinthe.Schema.Notation
  use Absinthe.Relay.Schema.Notation, :modern

  alias MyappWeb.Resolvers.Accounts

  object :accounts_commands do
    @desc "Change the username of a user."
    payload field(:change_user_username) do
      @desc "Input type of ChangeUserUsername."
      input do
        @desc "The ID of the user."
        field(:user_id, non_null(:id))
        @desc "The username of the user."
        field(:username, non_null(:string))
      end

      @desc "Return type of ChangeUserUsername."
      output do
        @desc "Indicates if the command succeeded."
        field(:success, non_null(:boolean))
        @desc "Error details."
        field(:error, :error)
      end

      middleware(Absinthe.Relay.Node.ParseIDs, user_id: :user)
      resolve(&Accounts.ChangeUserUsername.resolve/3)
    end
  end
end
tspenov

tspenov

@itopiz

        resolution
        |> Absinthe.Resolution.put_result({:error, unauthenticated()})

changes the state of the Resolution struct to resolved.
So you can add a clause in ParseIDs middleware to catch this case and pass like this:

  def call(%Resolution{state: :resolved} = resolution, _) do
    resolution
  end
itopiz

itopiz OP

Thank you for the suggestion. However, the ParseIDs middleware being part of the absinthe_relay project, I cannot change it.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

The ParseIDs middleware should definitely be pattern matching on the unresolved state, I’ll look at fixing that today.

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews