Absinthe Authorisation Questions / Thoughts


The Absinthe book is thought provoking,


and has helped me navigate the library.

I was wondering about a few things.

Putting Authorisation in Schema middleware is on the one hand convenient, e.g.

field :user_profiles, list_of :user_profile do
      middleware Schema.Middleware.Authorise, :some_permission
      (&UserProfileResolver.all/2) |> Utils.handle_errors |> resolve

on the other repetitive (because I have have so many useful ‘edges’ to protect as well as ‘nodes’), e.g.

# types
object :user do
    field :id, non_null :positive_int
    field :user_profile, :user_profile do
      resolve &UserResolver.user_profile_association/3

Taking FB’s advice I will push my authorisation into business logic and away from the schema, where I have one source of truth, which seems sensible.

Perhaps it would be good to emphasise this trade-off more in the book?

The book does in fact show a way of pushing authentication context into the business logic & resolvers, e.g.

# schema
field :user_profiles, list_of :user_profile do
      (fn a, b, %{context: context} -> UserProfileResolver.all a, b, context end) 
      |> Utils.handle_errors 
      |> resolve

It seems like a good, and potentially popular way to implement things.

Is there a neater way of doing this? I suspect there is, but I cannot figure it out.

It seems v verbose, but perhaps that’s OK.

Here I found a nice way of treating partially errored results, which is really smart, especially when it comes to authorisation.

Is there anywhere I can find some good examples for this?

I suspect it’s the nature of the GraphQL beast that many edges / fields need such a Union type. Perhaps providing something like a ‘maybe’ or ‘option’ type out of the box from the library would be helpful…


I move all of authorisation into what’s currently called “contexts” around here. Not sure if it’s any better though.


Do you do something like this?

type fields <-> permissions
schema fields <-> permissions

I think I am better off doing it close to the ecto db queries, and as I said, have one source of truth


Do you do something like this?

type fields <-> permissions
schema fields <-> permissions

Not sure … Web/graphql layer doesn’t actually know anything about authorization in my case.

All of the protected functions in a context accept additional parameters that help authorize the action.

@spec create_resource(attrs, by: User.t) :: {:ok, Resource.t} | {:error, Ecto.Changeset.t} | no_return
def create_resource(attrs, by: %User{id: user_id} = user) do
  Resource.Policy.authorize!(:create, user)

  %Resource{owner_id: user_id}
  |> Resource.changeset(attrs)
  |> Resource.insert()

Same for get, update, …

If the action is not authorized, currently I raise an UnauthorizedError with a plug status set. I can catch this exception and return nil or empty list for absinthe to render partially erred results (haven’t tried it yet though).


Yep, this looks similar to my thinking (without the exceptions - I try to avoid those whenever poss, but maybe I should revisit after seeing this!)

Also, I will have a union type, so in MLish pseudocode…

Users = (…, MaybeGroups)
MaybeGroups = Groups | UnauthorisedError
Groups = [Group]

The only thing is, the FB spec recommends returning a null or [], as you suggest, and then include a list of errors in the returned JSON…

This way, I suppose the error will be under groups, i.e.

users = {…, groups: {"error", "unauthorised"}}

which isn’t bad per se, just perhaps not idiomatic.



I’m a bit curious where you saw the Utils.handle_errors pattern. The book would have been promoting a middleware based approach, which isn’t what is happening here. The approach here will fail if any asynchronous execution is used by the resolver.


Good pt! I must’ve picked it up somewhere, I will amend it : ) thanks : )