Speakeasy
Middleware based authentication and authorization for Absinthe GraphQL powered by Bodyguard.
Installation
If available in Hex, the package can be installed
by adding speakeasy to your list of dependencies in mix.exs:
def deps do
[
{:speakeasy, "~> 0.3.0"}
]
end
Configuration
Configuration can be done in each Absinthe middleware call, but you can set global defaults as well.
config :speakeasy,
user_key: :current_user, # the key the current user will be under in the GraphQL context
authn_error_message: :unauthenticated # default authentication failure message
Note: no authz_error_message is provided because it is set from Bodyguard.
Usage
tl;dr: A full example authentication, authorizing, loading, and resolving an Absinthe schema:
This example assumes:
- You are authorizing a standard phoenix context
- You already have a bodyguard policy
- Your
:current_useris already in the Absinthe context or you are usingSpeakeasy.Plug
defmodule MyApp.Schema.PostTypes do
use Absinthe.Schema.Notation
alias Spectra.Posts
object :post do
field(:id, non_null(:id))
field(:name, non_null(:string))
end
object :post_mutations do
@desc "Create post"
field :create_post, type: :post do
arg(:name, non_null(:string))
middleware(Speakeasy.Authn)
middleware(Speakeasy.Authz, {Posts, :create_post})
middleware(Speakeasy.Resolve, &Posts.create_post/2)
middleware(MyApp.Middleware.ChangesetErrors) # :D
end
@desc "Update post"
field :update_post, type: :post do
arg(:name, non_null(:string))
middleware(Speakeasy.Authn)
middleware(Speakeasy.Authz, {Posts, :update_post})
middleware(Speakeasy.Resolve, &Posts.update_post/3)
middleware(MyApp.Middleware.ChangesetErrors) # :D
end
end
object :post_queries do
@desc "Get posts"
field :posts, list_of(:post) do
middleware(Speakeasy.Authn)
middleware(Speakeasy.Resolve, fn(attrs, user) -> MyApp.Posts.search(attrs, user) end)
end
@desc "Get post"
field :post, type: :post do
arg(:id, non_null(:string))
middleware(Speakeasy.Authn)
middleware(Speakeasy.LoadResourceByID, &Posts.get_post/1)
middleware(Speakeasy.Authz, {Posts, :get_post})
middleware(Speakeasy.Resolve)
end
end
end
Middleware
Speakeasy is a collection of Absinthe middlewares:
Speakeasy.Plug
Speakeasy includes a Plug for loading the current user into the Absinthe context. It isn't required if you already have a method for loading the user into your Absinthe context.
defmodule MyAppWeb.Router do
use MyAppWeb, :router
pipeline :graphql do
plug(Speakeasy.Plug, load_user: &MyApp.Users.whoami/1, user_key: :current_user)
end
end