Authorization in GraphQL
Tags: graphql, apollo
Published: 2020-04-05 10:11:47

---
title: Authorization in GraphQL
published: true
description: Authorization in an Apollo GraphQL Server by separating your authorization layer.
tags: graphql, apollo
canonical_url: https://blog.robertbroersma.com/authorization-in-graphql
---

**Authentication** is figuring out who the user is.
**Authorization** is figuring out what the user is allowed to do.

For this article I'll be focusing on the latter.

# Authenticate
Before we _can_ focus on authorization, we do need to be authenticated. So without diving in too deep, this is how we'll let our resolvers (and other parts of our app, a you will see) know who we are.

```javascript
export const handler = new ApolloServer({
  typeDefs,
  resolvers,
  context: async ({ req }) => {
    const user = await getUser(req.header.authorization)

    return { user }
  },
})
``` 

# Basic Authorization
Now that we know if the user is authenticated or not, the most basic thing we can do is allow or deny them to do anything.

```javascript
export const handler = new ApolloServer({
  typeDefs,
  resolvers,
  context: async ({ req }) => {
    const user = await getUser(req.header.authorization)

    if (!user) throw new AuthorizationError('You are not logged-in!');	

    return { user }
  },
})
```
The context function always runs before any requested query. IF there's no user found that matches the `authorization` header, the entire request is blocked!

# Group Authorization
The next thing we want to do is allow the user access to _parts_ of the schema if they are allowed. Let's say we only allow the doctors to access patient info. It would now be very tempting to go into the resolver for the `patients` query and do this:

```javascript
patients: (root, args, context) => {
  // We could also throw an error again. That's up to you!
  if (context.user.role !== 'doctor') return [];

  return ['Carl', 'Hank'];
}
```

However, this could quickly become a problem when there's multiple to ways to query for patient data. If our full schema looked like this:

```graphql
type Patient {
  name: String
}

type Hospital {
  name: String
  patients: [Patient!]!
}

type Query {
  patients: [Patient!]!
  hospitals: [Hospital!]!
}
```

Now we need to duplicate the `doctor` check inside the `hospitals` resolver!

## Models
A better way to do this is by splitting your authorization logic into a separate layer, separated from your resolver logic. This is described in more detail in [Thinking in Graphs](https://graphql.org/learn/thinking-in-graphs/)

![Business Layer](https://dev-to-uploads.s3.amazonaws.com/i/pdcxpgor2w01lhd5bezm.png)

We can for instance create a model for the `Patient` type that looks like this:

```javascript
const Patient = {
  getAll() {...}
  getById(id) {...}
  getByHospitalId(hospitalId) {...}
}
```

Now our `Patient` model is the single source of truth for business logic concerning our Patient type! We just need a way to tell our models about our authenticated user.

```javascript
// models/Patient.js
export const createPatientModel = ({ user }) => ({
  getAll() {...}
  getById(id) {...}
  getByHospitalId(hospitalId) {...}
})

// graphl handler
export const handler = new ApolloServer({
  typeDefs,
  resolvers,
  context: async ({ req }) => {
    const user = await getUser(req.header.authorization)

    if (!user) throw new AuthorizationError('You are not logged-in!');

    const Patient = createPatientModel({ user })

    return { user, models: { Patient } }
  },
})
```

There we go! We pass the Patient model and any other models on the context as well, so they're accessible in our resolvers:

```javascript
// Query.patients resolver
patients: (root, args, context) => {

  // The doctor authorization is handled in the Patient model
  return context.models.Patient.getAll()
}
```

# Ownership Authorization

Let's say we want even more fine-grained authorization for our doctors. Doctors should only be able to view information about their own patients. We can now go into our Patient model and change the required permissions, as opposed to editing all the different resolvers. Here's an example of filtering a doctor's patients if you're using `Prisma 2`:

```javascript
export const createPatientModel = ({ user }) => ({
  getAll() {
    return db.patient.findMany({ where: { doctor: { id: user.id } } })
  },
  getByHospitalId(hospitalId) {
    return db.patient.findMany({
      where: { doctor: { id: user.id }, hospital: { id: hospitalId } }
    })
  }
})
```

I'll leave it up to you to generalize the ownership part of the `where` clause!

# Conclusion
There's many different ways to handle authorization in a GraphQL server. This is my favourite one. Separating your business/authorization logic from your resolver logic makes your code more readable and maintainable!