Created
December 3, 2019 21:37
-
-
Save nuxlli/4445d8dc916d69bfb04102b82678e86c to your computer and use it in GitHub Desktop.
Absinthe middleware for auth with Guardian
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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