GraphQL Authentication with Elixir and Absinthe

Written by Pete Corey on May 8, 2017.

You’ve assembled your superhero stack. Your React front-end is communicating with an Elixir/Phoenix back-end through an Apollo/Absinthe GraphQL data layer.

You feel invincible.

But that feeling of invincibility quickly turns to panic as your first real development task comes down the pipe. You need to add user authentication to your system.

How do we even do authentication in this stack?

Our application will need to handle both publicly accessible and private queries and mutations through its GraphQL API. How do we set up these queries on the server, and how do we manage users’ sessions on the client?

Great questions! Let the panic pass over you and let’s dive in.

Two Layers of Authentication

Every request made against our Absinthe-based GraphQL server is done through an HTTP request. This request layer provides a fantastic opportunity to lay the groundwork for our authentication system.

Every GraphQL request that’s made against our system will come with an optional auth_token. A valid auth_token will map to a single user in our system. This auth_token is assigned to a user when they sign in.

On each request we’ll look up the user associated with the given auth_token and attach them to the context of our GraphQL resolvers.

If we can’t find a user associated with a given auth_token, we’ll return an authorization error (403) at the HTTP level. Otherwise, if no auth_token was provided, we simply won’t set the user_id in our GraphQL context and we’ll move onto processing our query and mutation resolvers.

The key to our authentication (and authorization) system is that the currently signed in user can be pulled from the GraphQL context. This context can be accessed by all of our resolvers and can be used to make decisions about what data to return, which mutations to allow, etc…

Writing Our Context Plug

The first step of building our authentication solution is to write a piece of Plug middleware that populates our GraphQL context with the currently signed in user.

To make things more real, let’s consider the context middleware I’m using for the security-focused SaaS application I’m building (Inject Detect). The middleware is based on the middleware provided by the Absinthe guide.

With that in mind, let’s build out our Plug in a module called InjectDetect.Web.Context:


defmodule InjectDetect.Web.Context do
  @behaviour Plug

  import Plug.Conn

  def init(opts)
  def call(conn, _)

end

To start, we’ll want our plug to implement the Plug behavior, and to import Plug.Conn. Implementing the Plug behavior means that we’ll need to define an init/1 function, and a call/2 function.

The call function is the entry point into our Plug middleware. Let’s flesh it out a bit:


def call(conn, _) do
  case build_context(conn) do
    {:ok, context} ->
      put_private(conn, :absinthe, %{context: context})
    {:error, reason} ->
      conn
      |> send_resp(403, reason)
      |> halt()
    _ ->
      conn
      |> send_resp(400, "Bad Request")
      |> halt()
  end
end

Here’s the meat of our context middleware. We call out to a function called build_context which builds our GraphQL context, as the name suggests.

If build_context returns an :ok tuple, we stuff the resulting context into our conn as is expected by Absinthe.

Otherwise, we return either a 403 error or a 400 error in the case of either a bad authentication token, or any other unexpected error.

Now we need to flesh out the build_context function:


def build_context(conn) do
  with ["Bearer " <> auth_token] <- get_req_header(conn, "authorization"),
       {:ok, user_id}            <- authorize(auth_token)
  do
    {:ok, %{user_id: user_id}}
  else
    []    -> {:ok, %{}}
    error -> error
  end
end

build_context pulls the auth_token out of the authorization header of the request and passes it into an authorize function. authorize either returns an :ok tuple with the current user_id, or an :error.

If authorize returns an error, we’ll pass that back up to our call function, which returns a 403 for us.

Otherwise, if the authorization header on the request is empty, we’ll return an empty map in the place of our GraphQL context. This empty context will allow our resolvers to let unauthenticated users access public queries and mutations.

Lastly, let’s take a look at authorize:


def authorize(auth_token) do
  InjectDetect.State.User.find(auth_token: auth_token)
  |> case do
       nil  -> {:error, "Invalid authorization token"}
       user -> {:ok, user.id}
     end
end

authorize is a relatively simple function.

It takes in an auth_token, looks up the user associated with that token, and either returns that user’s id, or an :error tuple if no associated user was found.


Armed with our new InjectDetect.Web.Context Plug, we can build a new :graphql pipeline in our router:


pipeline :graphql do
  plug :fetch_session
  plug :fetch_flash
  plug InjectDetect.Web.Context
end

And pipe our /graphql endpoint through it:


scope "/graphql" do
  pipe_through :graphql
  forward "/", Absinthe.Plug, schema: InjectDetect.Schema
end

Now all GraphQL requests made against our server will run through our authentication middleware, and the currently signed in user will be available to all of our GraphQL resolvers.

Contextual Authentication and Authorization

Now that the currently signed in user can be accessed through our GraphQL context, we can start to perform authentication and authorization checks in our resolvers.

But first, let’s take a look at how we would set up a public query as a point of comparison.

A Public Query

In our application the user query must be public. It will either return the currently signed in user (if a user is signed in), or nil if the current user is unauthenticated.


field :user, :user do
  resolve &resolve_user/2
end

The user query takes no parameters, and it directly calls a function called resolve_user:


def resolve_user(_args, %{context: %{user_id: user_id}}) do
  {:ok, User.find(user_id)}
end
def resolve_user(_args, _context), do: {:ok, nil}

We use pattern matching to pull the current user_id out of our GraphQL context, and then return the user with that user_id back to our client. If our context is empty, the current user is unauthenticated, so we’ll return nil back to our client.

Great, that makes sense. The query is returning data to both authenticated and unauthenticated users. It’s completely public and accessible by anyone with access to the GraphQL API.

But what about a private queries?

A Private Query

Similarly, our application has an application query that returns an object representing a user’s application registered with Inject Detect. This query should only return a specified application if it belongs to the currently signed in user.


field :application, :application do
  arg :id, non_null(:string)
  resolve &resolve_application/2
end

Once again, our application query calls out to a resolver function called resolve_application:


def resolve_application(%{id: id}, %{context: %{user_id: user_id}}) do
  case application = Application.find(id) do
    %{user_id: ^user_id} -> {:ok, application}
    _                    -> {:error, %{code: :not_found,
                                       error: "Not found",
                                       message: "Not found"}}
  end
end

def resolve_application(_args, _context), do:
  {:error, %{code: :not_found,
             error: "Not found",
             message: "Not found"}}

In this case, we’re once again pattern matching on our GraphQL context to grab the current user_id. Next, we look up the specified application. If the user_id set on the application matches the current user’s user_id, we return the application.

Otherwise, we return a :not_found error. We’ll also return a :not_found error if no user_id is found in our GraphQL context.

By making these checks, an authenticated user can only access their own applications. Anyone else trying to query against their application will receive a :not_found authorization error.

A Private Mutation with Absinthe Middleware

Let’s take a look at another way of enforcing authentication at the query level.

We have a sign_in mutation that should only be callable by a signed in user:


field :sign_out, type: :user do
  middleware InjectDetect.Middleware.Auth
  resolve &handle_sign_out/2
end

You’ll notice that we’ve added a call to an Absinthe middleware module before the call to our &handle_sign_out/2 resolver. As you might have guessed, the InjectDetect.Middleware.Auth module is where we’re enforcing an authentication check.


defmodule InjectDetect.Middleware.Auth do
  @behavior Absinthe.Middleware

  def call(resolution = %{context: %{user_id: _}}, _config) do
    resolution
  end

  def call(resolution, _config) do
    resolution
    |> Absinthe.Resolution.put_result({:error, %{code: :not_authenticated,
                                                 error: "Not authenticated",
                                                 message: "Not authenticated"}})
  end

end

The call function is our entry-point into our middleware module. It takes an Absinthe.Resolution struct as an argument, which contains the current GraphQL context.

If the context contains a user_id, we know that the user making the request is authorized. We can return the unmodified resolution from our middleware function, which lets it continue on to the &handle_sign_out/2 resolver function.

Otherwise, if no user_id is found in the context, we use Absinthe.Resolution.put_result to modify the resolution struct before returning it from our middleware. Giving the resolution a result, in this case a :not_authenticated :error tuple, will short circuit the query or mutation’s resolution and immediately return that result to the client.

This piece of middleware effectively prevents unauthenticated users from accessing the sign_out mutation.

Beautiful.

This middleware pattern is extremely powerful. It can easily be extended to check for specific user roles or other criteria, and can be easily added to an existing query or mutation.

Additionally, multiple middleware modules or functions can be chained together to create a very readable, declarative authentication and authorization scheme around your GraphQL API.

Final Thoughts

At first, all of the moving parts related to handling authentication and authorization in a GraphQL application can be overwhelming.

Thankfully, once you wrap your head around the basic strategies and building blocks involved, the end solution easily falls into place. Authorization and authentication in a GraphQL-based system isn’t much different than in any other system.

Next week, we’ll move on to answering the second set of questions raised in the beginning of this article. How do we manage user sessions on the front-end of our application?

Stay tuned!

Inject Detect Progress Report

Written by Pete Corey on May 1, 2017.

It’s been almost two months since I announced I was working on a security focused SaaS application called Inject Detect.

For those that haven’t been following along, Inject Detect is a service designed to detect NoSQL Injection attacks against your Meteor applications. It does this by monitoring queries made against your application, looking for unexpected queries that may be the result of an injection attack.

I had lots of ideas about how I wanted to build out Inject Detect. To keep myself accountable and in an effort to iterate in public, I’ve decided to put together a progress report.

Let’s dive into how my plans have played out, where I’m at in the project, and most importantly let’s talk about when Inject Detect will be ready to use!

Front-end Progress

Since it was announced, I’ve been making steady headway on both the front-end and back-end components of Inject Detect.

The front-end of Inject Detect is being built as a React application, backed by Create React App tooling. On top of vanilla React, I’m using Apollo client to integrate with my Absinthe-powered GraphQL back-end.

So far, this combination has been a dream to work with.

The productivity gains I’ve experienced working with React and Apollo is off the charts. After climbing over a few learning curves, I’ve found myself effortlessly cranking out feature after feature.

I’m addicted to the GraphQL workflow.


The front-end of Inject Detect is still heavily under construction. I’ll be the first to say that I’m no designer. I’m using Semantic UI as the base for most of my designs and tweaking from there.

Overall, I’m very happy with the progress I’ve made so far on the front-end.

Check out a few screen shots to see where I’m at:






Since NoSQL Injection is a relatively unknown (but unbelievably common) vulnerability, I’m trying to incorporate healthy doses of education into its user interface.

I’m discovering that balancing education with brevity and clarity is a difficult thing to do.

Back-end Progress

Most of the first month of development was spent building out the back-end infrastructure and business logic to handle all of the various use cases of Inject Detect.

In my post about the high level design of Inject Detect, I talked about implementing the core domain of the application using Event Sourcing.

So far, I’m very happy with this choice.

Let’s look a little closer at some of the design decisions I’ve made, and dive into what it means to be an “Event Sourced” system.


Interactions with the system are done through “commands”. A command is just an instruction for the system to do something. Based on the current state of the system, the processing of a command will either return a list of events representing the changes to the system, or an error.

Keep in mind that events don’t actually modify the system in any way (write to a database, etc…). They just return a list of events, side-effect free.

Diving into the code, a command is just an Elixir struct. For example, here’s a command to toggle “alerting” on an application in Inject Detect:


defmodule InjectDetect.Command.ToggleAlerting do
  defstruct application_id: nil
end

The struct holds all of the information we need to carry out the command. In this case, we just need the application_id of the application in question.

Each command implements a Command protocol, which means it defines a handle function. Command.handle takes in the command struct being handled and a “context” map, which in our case holds the currently signed in user.

Our handle implementation for the ToggleAlerting command looks like this:


defimpl InjectDetect.Command, for: InjectDetect.Command.ToggleAlerting do

  alias InjectDetect.Event.TurnedOffAlerting
  alias InjectDetect.Event.TurnedOnAlerting
  alias InjectDetect.State.Application

  def toggle_alerting(application = %{user_id: user_id}, command, %{user_id: user_id}) do
    case application.alerting do
      true ->
        {:ok, [%TurnedOffAlerting{application_id: command.application_id}]}
      false ->
        {:ok, [%TurnedOnAlerting{application_id: command.application_id}]}
    end
  end

  def toggle_alerting(_, _, _) do
    {:error, %{code: :not_authorized,
               error: "Not authorized",
               message: "Not authorized"}}
  end

  def handle(command, context) do
    Application.find(command.application_id)
    |> toggle_alerting(command, context)
  end

end

If the current user has permission to toggle alerting on the specified application, we return either a TurnedOffAlerting event, or a TurnedOnAlerting event.

Otherwise, we throw an authorization error.


Events, like commands, are just Elixir structs. The TurnedOffAlerting event we mentioned earlier looks something like this:


defmodule InjectDetect.Event.TurnedOffAlerting do
  defstruct application_id: nil
end

Again, the only information we need to represent this event is the application_id of the application in question.

Events implement a Reducer protocol, which defines a apply function. The apply function takes in the system’s current state as its first argument, and the event being applied as the second argument. It’s purpose is to transform the current state according to the event being applied.

The Reducer implementation for TurnedOffAlerting looks like this:


defimpl InjectDetect.State.Reducer,
   for: InjectDetect.Event.TurnedOffAlerting do

  def apply(event, state) do
    put_in(state, [Lens.key(:users),
                   Lens.all,
                   Lens.key(:applications),
                   Lens.filter(&(&1.id == event.application_id)),
                   Lens.key(:alerting)], false)
  end

end

We basically just dig through our application’s state structure, finding the application we care about and we set its alerting key to false.


You may have noticed that we’re passing the entire system’s state into each Reducer.apply function call. Does this mean that the entire system’s state needs to exist in memory at all times?

Yes!

Inject Detect is storing application state entirely in memory as a Memory Image.

This means that Inject Detect isn’t using a database to hold information about the application’s users, applications, and queries. The only thing being stored in the database is a stream of events.

Instead, Inject Detect stores this stateful information in a long-lived GenServer process. Every time we grab the application’s current state (InjectDetect.State.get), it queries the database for any events that have happened since the last get, applies them using Reducer.apply, and then returns the resulting state.

This was definitely the most radical decision I made while building Inject Detect, but in my experience so far, it has proven to be incredibly powerful.


I definitely made some adventurous design decisions when implementing Inject Detect’s back-end. So far, I’m incredibly satisfied with my decisions to date.

I’m eager to see how they perform under continued development, load testing, and real-world usage.

I’m planning on writing more articles outlining and detailing these design choices in the future. If you’re particularly interested in any one aspect of this system, reach out and let me know!

What’s Next?

While I didn’t mention it in this post, the Meteor plugin component of Inject Detect is essentially finished. The back-end portion of the projects is a few loose ends and a few missing features away from being completed. The front-end of the application represents the bulk of the remaining work, but it’s moving along at a steady pace.

I expect to be ready for beta testing for a small number of real-world users in the next one or two months.

If you haven’t yet, be sure to subscribe to the Inject Detect newsletter to get official news on the release as soon as it’s ready!

Passwordless Authentication with Phoenix Tokens

Written by Pete Corey on Apr 24, 2017.

Subtitled: I got 99 problems, but a password ain’t one.

I’m in the process of building a security-focused SaaS application (shameless plug: Inject Detect), and I’ve decided to use a passwordless authentication scheme. Since I’ve decided to stop using passwords, I feel like a great burden has been lifted from my shoulders.

In this post, let’s dig into how awesome passwordless authentication is, and just how easy it is to set up in your Elixir/Phoenix application.

Passwordless in a Passwordful World

Before we dig into the nuts and bolts of building out a passwordless system, we should probably talk about what exactly “passwordless” means.

How can we authentication users without passwords?

The general idea behind a passwordless authentication scheme is that instead of a user regurgitating a password to prove their identity, they’re emailed a “magic link” that, when clicked, will activate their session.


In many ways, a passwordless authentication scheme is very similar to traditional password-based authentication. The only difference is that we require the user makes a trip to their inbox.

So why bother? Shouldn’t we focus on creating less work for our users? Aren’t passwords fine?

It turns out that passwords aren’t fine. There are numerous problems with passwords as we know them. Not only do people often choose poor passwords and have deplorable password habits (full disclosure: I’m one of these people), but they fundamentally don’t do the job they’re designed to do.

How so?

Authentication ultimately boils down to proving you are who you say you are. This is often done by presenting the system with a fact that can only be known by you. If you can produce this fact, the system assumes that you are who you say you are.

Unfortunately, your passwords aren’t secrets only known by you. Every time you use (or reuse) a password in a system, you’re giving that system knowledge of your password. You’re trusting the ethics and technical competency of that system with the defining factor of your online identity.


How is passwordless better?

I believe that passwordless authentication is a better alternative over password-based authentication for the simple reason that turns authentication into a process of active consent, rather than the passive transfer of a piece of information.

Your active consent cannot be given to another system, or stolen by an attacker. As long as you control the channel through which consent is granted (email, SMS, etc…), you control your identity.

Take the power back!

Going Passwordless with Phoenix Tokens

Now that I’ve spent all that time waxing poetic about the beauties of passwordless authentication, let’s talk about how can actually implement it in a Elixir/Phoenix based application.

I debated going into great detail in this section discussing how to implement passwordless authentication in a few different stack permutations (Vanilla Phoenix, React, Apollo, Absinthe, etc…), but instead, let’s talk about the common theme in all of these implementations: Phoenix Tokens.

Phoenix Tokens do two things that turn out to be invaluable for building out a passwordless authentication scheme:

  • They generate cryptographically strong bearer tokens.
  • They let you make assertions about the age of a token.

There are three major workflows of a passwordless system. Let’s run through each of them and see how easy they are to implement using Phoenix Tokens.

Signing Up

In a passwordless system, all we need to create an account for a new user is their email address.

If we don’t care to verify the email address they provided, we can immediately sign them in by using Phoenix.Token.sign to generate the user an auth_token:


Phoenix.Token.sign(Endpoint, user.id, :crypto.strong_rand_bytes(32))

This auth_token will be saved and passed down to the client and stored in localStorage. Any subsequent requests to the server will send along this token to identify the currently logged in user.

When the server receives a request with an attached auth_token, it can look up the associated user.

If we want to limit the maximum age of a user’s session, we can verify the token using Phoenix.Token.verify and pass in a :max_age in seconds:


Phoenix.Token.verify(Endpoint, user.id, auth_token, max_age: 1209600)

In this example, we’re limiting sessions to two weeks (or 1,209,600 seconds). If a user tries to use an expired token, or a token not associated with any users, we return an error.

Signing Out

Once our new user has signed up, signing out is as simple as deleting their associated auth_token.

Once the auth_token is removed, and cleared from their browser’s localStorage, all subsequent requests they make will be unauthenticated until they sign back in.

Signing In

Now we’re getting to the interesting part.

Once our user has signed out, how do they sign back into our passwordless application?

On the “sign in” page, the user will enter their email address and click a “Send me a magic link” button. Next, our server will use Phoenix.Token.sign to generate a new requested_token, which will be saved and emailed to the provided email address.


Phoenix.Token.sign(Endpoint, user.id, :crypto.strong_rand_bytes(32))

The email will contain a link to a “verify requested token” route in our application which takes the requested_token as a parameter.

That route looks up the user with the provided requested_token, verifies that the requested_token isn’t expired, generates a new auth_token for that user, and finally removes the verified requested_token from the user:


Phoenix.Token.verify(Endpoint, user.id, requested_token, max_age: 600)

Phoenix.Token.sign(Endpoint, user.id, :crypto.strong_rand_bytes(32))

In this example, our “magic link” emails only last for ten minutes.

Once a user clicks the link, their new auth_token will be sent down to the client and they’ll be automatically signed in!

Final Thoughts

Passwordless authentication is definitely new territory for me, and I suspect, a lot of other software developers.

I strongly believe that it’s important to explore other options for user authentication. The status quo simply isn’t working. Whether you look at it from a user experience perspective, or from a perspective of security, traditional passwords in practice are ineffective and wrought with problems.

Passwordless authentication may not be the ideal solution, but I believe that it’s a step in the right direction.

If you want to see passwordless authentication in action, sign up for the Inject Detect newsletter to receive the latest news on its upcoming release!