Created
June 10, 2016 11:18
-
-
Save ericstumper/412a17321e25eab524a2bbbd57a033d4 to your computer and use it in GitHub Desktop.
Guardian Authentication with Absinthe GraphQL in Elixir
This file contains 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 Languafy.Web.Context do | |
@behaviour Plug | |
import Plug.Conn | |
alias Languafy.User | |
def init(opts), do: opts | |
def call(conn, _) do | |
case build_context(conn) do | |
{:ok, context} -> | |
put_private(conn, :absinthe, %{context: context}) | |
{:error, reason} -> | |
conn | |
_ -> | |
conn | |
end | |
end | |
@doc """ | |
Return the current user context based on the authorization header | |
""" | |
defp build_context(conn) do | |
with ["Bearer " <> token] <- get_req_header(conn, "authorization"), | |
{:ok, current_user} <- authorize(token) do | |
{:ok, %{current_user: current_user}} | |
end | |
end | |
defp authorize(token) do | |
case Guardian.decode_and_verify(token) do | |
{:ok, claims} -> return_user(claims) | |
{:error, reason} -> {:error, reason} | |
end | |
end | |
defp return_user(claims) do | |
case Guardian.serializer.from_token(Map.get(claims, "sub")) do | |
{:ok, resource} -> {:ok, resource} | |
{:error, reason} -> {:error, reason} | |
end | |
end | |
end |
This file contains 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 Languafy.Router do | |
use Languafy.Web, :router | |
pipeline :browser do | |
plug :accepts, ["html"] | |
plug :fetch_session | |
plug :fetch_flash | |
plug :protect_from_forgery | |
plug :put_secure_browser_headers | |
end | |
pipeline :graphql do | |
plug Plug.Parsers, | |
parsers: [:urlencoded, :multipart, :json], | |
pass: ["*/*"], | |
json_decoder: Poison | |
plug Languafy.Web.Context | |
end | |
if Mix.env == :dev do | |
scope "/graphiql" do | |
forward "/", Absinthe.Plug.GraphiQL, schema: Languafy.Schema | |
end | |
end | |
scope "/graphql" do | |
pipe_through :graphql | |
forward "/", Absinthe.Plug, schema: Languafy.Schema | |
end | |
scope "/", Languafy do | |
pipe_through :browser # Use the default browser stack | |
get "*path", PageController, :index | |
end | |
end |
This file contains 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 Languafy.Schema do | |
use Absinthe.Schema | |
import_types Languafy.Schema.Types | |
query do | |
@desc "Get an App User by ID" | |
field :user, type: :user do | |
arg :id, non_null(:id) | |
resolve &Languafy.UserResolver.find/2 | |
end | |
@desc "Get current App User" | |
field :current_user, type: :user do | |
resolve &Languafy.Resolver.User.current/2 | |
end | |
@desc "Get all courses" | |
field :courses, list_of(:course) do | |
resolve &Languafy.Resolver.Course.all/2 | |
end | |
end | |
mutation do | |
@desc "Create an App User" | |
field :user, type: :user do | |
arg :first_name, non_null(:string) | |
arg :last_name, non_null(:string) | |
arg :email, non_null(:string) | |
arg :password, non_null(:string) | |
resolve &Languafy.Resolver.User.create/2 | |
end | |
@desc "Login User" | |
field :session, :token do | |
arg :email, non_null(:string) | |
arg :password, non_null(:string) | |
resolve &Languafy.Resolver.Session.create/2 | |
end | |
end | |
end |
This file contains 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 Languafy.Resolver.Session do | |
alias Languafy.Repo | |
alias Languafy.User | |
def create(args, _info) do | |
user = Repo.get_by(User, email: args[:email]) | |
case authenticate(user, args[:password]) do | |
true -> create_token(user) | |
_ -> {:error, "User could not be authenticated."} | |
end | |
end | |
defp authenticate(user, password) do | |
case user do | |
nil -> false | |
_ -> Comeonin.Bcrypt.checkpw(password, user.password_hash) | |
end | |
end | |
defp create_token(user) do | |
case Guardian.encode_and_sign(user, :token) do | |
nil -> {:error, "An Error occured creating the token"} | |
{:ok, token, full_claims} -> {:ok, %{token: token}} | |
end | |
end | |
end |
This file contains 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 Languafy.Schema.Types do | |
use Absinthe.Schema.Notation | |
@desc "An App User" | |
object :user do | |
field :id, :id | |
field :first_name, :string | |
field :last_name, :string | |
field :email, :string | |
field :courses, list_of(:course) | |
end | |
@desc "A Languafy Course" | |
object :course do | |
field :id, :id | |
field :title, :string | |
field :description, :string | |
field :users, list_of(:user) | |
end | |
@desc "A JWT Token" | |
object :token do | |
field :token, :string | |
end | |
end |
This file contains 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 Languafy.Resolver.User do | |
alias Languafy.User | |
def find(%{id: id}, _info) do | |
case Languafy.Repo.get(User, id) do | |
nil -> {:error, "User id #{id} not found"} | |
user -> {:ok, user} | |
end | |
end | |
def current(_, %{context: %{current_user: current_user}}) do | |
{:ok, current_user} | |
end | |
def current(_, _) do | |
{:error, "Access denied"} | |
end | |
def create(args, _info) do | |
%User{} | |
|> User.registration_changeset(args) | |
|> Languafy.Repo.insert | |
end | |
end |
That would definitely make sense!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://gist.github.com/ericstumper/412a17321e25eab524a2bbbd57a033d4#file-schema-ex-L29 you may want a password confirmation too.