Prisma Example Series: Authentication

Authentication with Github OAuth2

Matic Zavadlal
5 min readJan 3, 2018

This is the second article in my GraphQL examples series. If you want to learn about handling files with Prisma and Amazon S3, check out this example, where I guide you through simple project implementing basic File API.

For the second post I wanted to give a better overview of basic authentication concepts with Prisma. We are going to build a simple notes app which will allow users to create, edit and delete their notes. To make things even easier, we are going to use Github OAuth API, which will allow users to login using their Github account.

I recommend you clone the repository (repository) and follow along with the snippets to truly understand their role.

1.0 Setting up Github

To kick things of, we are first going to obtain the GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET. Head over to your Github profile page and open Settings. Search for Developer settings at the bottom of left-hand side navigation, and click New OAuth App in the upper right corner.

To register your app pick a catchy name you are definitely going to remember. Use http://localhost:3000 as your Homepage URL and Authorization callback URL, and click Register application.

Once your app is created copy Client ID and Client Secret somewhere you can access them later.

1.1 Connecting Github API

To authenticate the user and obtain its profile, we are using Github Auth API. Github requires us to first generate a “super” authentication token using our Client ID, Client Secret — we have generated before, and User Token — provided by user during authorisation.

Using obtained “super” token we then fetch basic user data. We will use this data later on. For now, acknowledge what this two functions do.

2.0 Setting up Prisma

As you might have guessed, we are going to use Prisma to store our data. Prisma generates all Queries and Mutations we might use based on our datamodel. Our datamodel consists of two types; User, which defines each user in our system, and Note, which defines any note our user might write. We use GraphQL SDL to define our datamodel.

Prisma relies on our prisma.yml and .graphqlconfig.yml config files. The first one is used to tell where, what and how to deploy our database, and the second is used to tell GraphQL about structure of our app.

Deploy your app using prisma deploy command and wait for the endpoint to be returned.

2.1 Prisma-binding and GraphQL-Yoga

Once our database is deployed, a generated schema will be built for us inside src/generated/database.graphql. The following schema will be used by graphql prepare to create database bindings for our app. (You can also run yarn bind, in case you haven’t installed the global dependency.)

We use GraphQL-Yoga which implements GraphQL Server for us. Yoga requires type definitions of our application schema, resolvers for the provided schema, and can optionally be given a context, which is passed down the resolvers stream.

For now let’s focus on context part; we use Prisma-binding, imported from the previously generated file, to create a connection between our server and our database.

This creates a whole set of functions, which match the structure of the generated schema. We will use this functions later on to query and mutate our data.

2.2 Schema and Resolvers

Application Schema is the vital part of our server. It tells the world what clients can Query and Mutate using our GraphQL API. In our application, we want to be able to authorise the user, access their notes and basic user profile, create new notes, edit existing ones and delete the old ones.

Query and Mutation represent the bottom branch of the app. They are a must in any GraphQL application, which queries and mutates any kind of data.

Once we have our schema defined we’ve got define resolvers, matching their schema definitions. Hence, createNote mutation must return Note shape like object from its resolver, and me mutation must return User shaped object, if such user exists. Let me focus on authentication part for now, as I will explain Notes a bit further down the post.

3.0 Authentication

Our server exposes authenticate endpoint to handle logins. It accepts githubCode as an argument and returns a User if the provided code was correct. We also return a JWT token which stores the value of our userId hashed behind our JWT_SECRET. We include this code in every authenticated request as a Http “Authorization” header with “Bearer {token}” value.

All the autorisation magic happens in this two parts. The first one signs the token using our secret, and the second one tries to decode it, returning userId if it were successful. We can use getUserId function everywhere we want to make sure user is signed in, and use its ID.

3.1 Permitting Note access

Because our Notes are very, very personal, we don’t want them to be visible to anyone but the user currently signed in. In order to achieve that, we must first take a look at the principle of permissions and how .exists function works.

Permissions tell our system which data can and which data cannot be accessed by out users. In our case, we want to make sure user owns the note he or she wants to view, edit or delete.

Looking at the note resolver, we can see that it first obtains the userId. If no user is signed in an AuthError is returned. AuthError is also returned if current user has no permissions to access this note. We use db.exists.Note function to find out whether such note exists. Exists function will return true only if there’s an object matching the filter — in our case, there must be an object which ID matches the ID of the requested Note, and its Owner’s ID must match the ID of currently signed in user.

The very same logic is used in all other Mutations, used to create, edit or delete a Note.

4.0 Testing the app

To test our app we first need to obtain our githubCode. In theory, we can do this by visiting https://github.com/login/oauth/authorize?client_id={GITHUB_CLIENT_ID}, where we change {GITHUB_CLIENT_ID} with ID of our Github app. Once we authorise the app, we will be redirected to http://localhost:3000?code={OUR_GITHUB_CODE}. We can use this code for testing purposes, but I would highly recommend implementing a real client which takes care of code processing — the logic of obtaining the code is still the same.

Once we have obtained the code, head over to the Playground and try Authenticating. You’ll need to set your Authorization: Bearer <token> header to test the permissions, by using the token obtained by authenticate Mutation.

To conclude, we use GraphQL-Yoga as the central unit of our application. Using Prisma-binding we connect our server with our database, and use generated functions to define our resolvers. Using query and mutation we can Query and Mutate data on our database, and using exists function we can implement permissions logic to secure our users’ personal data.

--

--