Authentication & Permissions with Graphcool

Authentication and data access permissions are critical features for every production application. In this chapter, you’ll learn how you can enable authentication in your project and leverage the powerful Graphcool permission system to specify data access rules.

Here’s a list of the requirements that we have for the app:

  • Only authenticated should be able to create new links
  • Only the user who posted a link should be able to update or delete it; in the case of updating, only the description of a link can be changed - not the url
  • Only authenticated users can vote on links
  • Only a user who submitted a vote can revoke (i.e. delete) that vote

Let’s see how you can implement these!

Enabling Email-and-Password authentication

To enable Email-and-Password authentication in your Graphcool project you have to use the Graphcool Console.

All this does is open up the Console for the project that’s represented by project.graphcool.

Having the Email-and-Password auth provider enabled adds two new mutations to the project’s API:

# 1. Create new user
createUser(authProvider: { email: { email, password } }): User

# 2. Login existing user
signinUser(email: { email, password }): SignInUserPayload

# SignInUserPayload bundles information about the `user` and `token`
type SignInUserPayload {
  user: User
  token: String
}

Furthermore, this auth provider also extended your project’s schema and now added two new fields to the User type: email and password. You can verify this by checking the selecting the Schema tab in the left side-menu.

Updating the Project File

You’re now in a situation again where your local Project File and the remote schema are out of sync. However, in contrast to before, this time that’s because the remote schema contains changes that are not yet reflect in your local version of it.

This can also happen when you’re working with multiple developers on the same project and one developer changes the remote schema.

In these situations, you need to update your local Project File with the changes that have been performed remotely. This can be done using the graphcool pull command.

Before the remote schema gets fetched, you will be asked to confirm that you want to override the current project file. You can confirm by typing y.

After you confirmed, the terminal output will look as follows:

 ✔ Your project file (project.graphcool) was successfully updated. Reload it in your editor if needed. 
 The new schema version is 3.

This schema version was bumped to 3 and the User type was updated to now also include the email and password fields:

type User implements Node {
  createdAt: DateTime!
  id: ID! @isUnique
  updatedAt: DateTime!
  name: String!
  links: [Link!]! @relation(name: "UsersLinks")
  votes: [Vote!]! @relation(name: "UsersVotes")
  email: String @isUnique
  password: String
}

Note: The graphcool pull command can also be used to initially download the Project File for one of your projects instead of updating an existing one. In these cases, you need to pass the --project (or short -p) option and specify the project ID as an argument. For example: graphcool pull -p cj4n6xatsgiv50118b7d6nyre.

Configuring Permissions

The Grapghcool permission system follows a whitelist approach. This means that all operations that can be performed on a type need to be explicitly allowed (i.e. whitelisted).

There are four different kinds of operations per type:

  • reading items of that type
  • creating items of that type
  • updating items of that type
  • deleting items of that type

When you’re creating a new type in a Graphcool project, there will be permissions generated for you that allow everyone to perform all of these operations! So, with every app that you want to put into production, it’s your responsibility to properly update these permissions to make sure only the right users can actually perform them. Otherwise, everyone who knows the endpoint for your API (or simply the project ID) will have full access to the data in your database!

As mentioned before, there are permissions for all of your types that currently allow everyone to perform all four possible operations.

Go ahead and change that and update the permissions as required. You’ll start with the easy ones to configure that only authenticated users can create links and cast votes.

In the popup that appears, you can now configure the rules to specify which users should be able to perform this operation.

The first tab of the popup requires you to select a number of fields of that type. That is so that you can configure the rules even on a field- not only on a type-level. This will allow you, for example, to specify certain users can only read the description and url fields of a Link - but not the id and the other system fields.

Awesome! You just prevented attackers from being able to randomly create links in your database. In fact, you can verify the actually works in a Playground!

To do so, you need to grab the endpoint for the Simple API and paste it into the address bar of a browser.

This will open up a Playground.

The server will return the following response in the right pane:

{
  "data": {
    "createLink": null
  },
  "errors": [
    {
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "createLink"
      ],
      "code": 3008,
      "message": "Insufficient permissions for this mutation",
      "requestId": "cj4nagibjb1l20164tsjececr"
    }
  ]
}

The server tells you that you can’t perform this operation since you’re not authenticated and would thus violate the permission rule that you just configured.

If you want to learn more about the error format the server uses, you can read up on it here.

All right, go ahead and repeat the same process for the Vote type.

That’s it - you just protected your voting system. 💪

Now on to the more complicated requirements, only the User who created a link should be able to update it later on. Plus, only the description can actually be changed.

All right, what’s going on here?

You just wrote your first permission query. Permission queries are a mechanism that allow to describe data access rules by means of a GraphQL query.

A permission query always returns true or false. It’s executed right before the actual operation is performed on the database. But where do the $user_id and $node_id arguments come from?

The $user_id represents the User that wants to perform an operation. The $node_id on the other hand identifies the element (in this case a Link) that the operation is to be performed on!

So, in effect, what you’re expressing with this query is that the createLink operation can only be executed if:

  • there is a Link element in the database that is identified by link_id
  • the User who is trying to perform the mutation has initially posted that Link, since the id of the postedBy field needs to be the same as the User that’s identified by user_id

Easy as pie! 🍰

The permission query that you can use for the Delete operation is identical to the one from the Update operation.

At this point, we’ll leave the permission query for the createVote and deleteVote mutations as an exercise to you.

Next Chapter

Realtime with GraphQL subscriptions

Learn how to setup server-side GraphQL subscriptions with Graphcool to add realtime functionality to an app. Subscriptions can be tested in a Playground too.

Go to next chapter