Skip to content

Instantly share code, notes, and snippets.

@nuxlli
Created December 3, 2019 21:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nuxlli/4445d8dc916d69bfb04102b82678e86c to your computer and use it in GitHub Desktop.
Save nuxlli/4445d8dc916d69bfb04102b82678e86c to your computer and use it in GitHub Desktop.
Absinthe middleware for auth with Guardian
# Adaptação de http://www.east5th.co/blog/2017/05/08/graphql-authentication-with-elixir-and-absinthe/
defmodule DemoWeb.AuthContext do
@moduledoc false
@behaviour Plug
import Plug.Conn
alias Guardian.Plug.Pipeline
def init(opts), do: opts
def call(conn, opts) do
case build_context(conn) do
{:ok, context} ->
put_private(conn, :absinthe, %{context: %{ auth_context: context }})
{:error, error} ->
conn
|> Pipeline.fetch_error_handler!(opts)
|> apply(:auth_error, [conn, error, opts])
|> halt()
end
end
@doc """
Return the current resource context based on the authorization header
"""
def build_context(conn) do
case get_req_header(conn, "authorization") do
["Bearer " <> jwt] ->
case Guardian.Plug.current_resource(conn) do
nil ->
{:error, {:invalid_token, "invalid_token"}}
resource ->
{:ok, %{token: jwt, guardian_resource: resource}}
end
_ ->
{:ok, %{}}
end
end
end
defmodule DemoWeb.Graphql.Middlewares.EnsureAuthenticated do
@behaviour Absinthe.Middleware
alias DemoWeb.Graphql.Errors
@moduledoc """
Check context.guardian_resource in Abstinhe.Resolution.
When User and Person are valid guardian resources, use:
When only User is valid guardian resource, use:
```elixir
middleware(EnsureAuthenticated, resources: [Demo.Accounts.User])
```
```elixir
middleware(EnsureAuthenticated, resources: [Demo.Accounts.User, Demo.Accounts.Person])
```
"""
def call(resolution, config) do
resources =
config
|> Keyword.get(:resources, [])
|> List.wrap()
with %{auth_context: %{guardian_resource: %module{id: id}}} <- resolution.context,
true <- is_binary(id),
true <- module in resources do
resolution
else
_ ->
error = Errors.unauthenticated(:user) |> Errors.convert_to_error()
%{resolution | state: :resolved, errors: [error]}
end
end
end
defmodule DemoWeb.Router do
use DemoWeb, :router
use Plug.ErrorHandler
use Sentry.Plug
pipeline :graphql do
plug(DemoWeb.AuthContext)
end
scope "/graphql" do
pipe_through :graphql
forward "/ui", Absinthe.Plug.GraphiQL, schema: DemoWeb.Graphql.Schema
# socket: BrokerWeb.UserSocket,
# interface: :playground
forward "/", Absinthe.Plug, schema: DemoWeb.Graphql.Schema
end
end
defmodule DemoWeb.Graphql.Middlewares.Transform do
@moduledoc false
@behaviour Absinthe.Middleware
# middleware DemoWeb.Graphql.Middlewares.Transform, &(&1 |> to_string |> String.upcase())
# middleware DemoWeb.Graphql.Middlewares.Transform, :upcase
# middleware DemoWeb.Graphql.Middlewares.Transform, [
# :compact,
# &Enum.map(&1, SubscriptionFrequencyMapper.convert/1),
# :upcase
# ]
def call(%{value: value, state: :resolved} = res, func) do
%{res | value: transform(value, func)}
end
def call(%{source: source, definition: %{schema_node: %{identifier: field}}} = res, func) do
call(%{res | state: :resolved, value: Map.get(source, field)}, func)
end
defp transform(value, funcs) when is_list(funcs) do
Enum.reduce(funcs, value, &transform(&2, &1))
end
defp transform(value, :compact) when is_list(value) do
value |> Enum.filter(&(&1 != nil and &1 != ""))
end
defp transform(value, :compact), do: value
defp transform(value, func) when is_list(value) and is_atom(func) do
Enum.map(value, &transform(&1, func))
end
defp transform("", :empty), do: nil
defp transform(value, :empty), do: value
defp transform(value, :upcase) do
value |> to_string |> String.upcase()
end
defp transform(value, :to_atom) do
value |> String.to_atom()
end
defp transform(value, :enum) do
value
|> transform(:upcase)
|> transform(:to_atom)
end
defp transform(value, func), do: func.(value)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment