Authentication

In this section, you’re going to implement signup and login functionality that allows your users to authenticate against your GraphQL server.

Adding a User model

The first thing you need is a way to represent user data in the database. To do so, you can add a User type to your Prisma data model.

You’ll also want to add a relation between the User and the existing Link type to express that Links are posted by Users.

Now you see even more how Prisma helps you to reason about your data in a way that is more aligned with how it is represented in the underlying database.

Understanding relation fields

Notice how you’re adding a new relation field called postedBy to the Link model that points to a User instance. The User model then has a links field that’s a list of Links.

To do this, you need to also define the relation by annotating the postedBy field with the @relation attribute. This is required for every relation field in your Prisma schema, and all you’re doing is defining what the foreign key of the related table will be. So in this case, we’re adding an extra field to store the id of the User who posts a Link, and then telling Prisma that postedById will be equal to the id field in the User table (if you are familiar with SQL, this kind of relation is being represented as one-to-many).

If this is quite new to you, don’t worry! We’re going to be adding a few of these relational fields and you’ll get the hang of it as you go! For a deeper dive on relations with Prisma, check out these docs.

Updating Prisma Client

This is a great time to refresh your memory on the workflow we described for your project at the end of chapter 4!

After every change you make to the data model, you need to migrate your database and then re-generate Prisma Client.

This command has now generated your second migration inside of prisma/migrations, and you can start to see how this becomes a historical record of how your database evolves over time. This script also run the Prisma migration, so your new models and types are ready-to-use.

That might feel like a lot of steps, but the workflow will become automatic by the end of this tutorial!

Your database is ready and Prisma Client is now updated to expose all the CRUD queries for the newly added User model – woohoo! 🎉

Extending the GraphQL schema

Remember back when we were setting up your GraphQL server and discussed the process of schema-driven development? It all starts with extending your schema definition with the new operations that you want to add to the API - in this case a signup and login mutation.

The signup and login mutations behave very similarly: both return information about the User who’s signing up (or logging in) as well as a token which can be used to authenticate subsequent requests against your GraphQL API. This information is bundled in the AuthPayload type.

Implementing the resolver functions

After extending the schema definition with the new operations, you need to implement resolver functions for them.

Setup for authentication

In this tutorial, you will implement simple, naive implementation of a JWT (Json Web Token) implementation. This is a simple solution for creating token-based authentication.

You’ll also use bcryptjs for simple encryption for the user’s password.

Implementing signup resolvers

Let’s use the good ol’ numbered comments again to understand what’s going on here – starting with signup.

  1. In the signup mutation, the first thing to do is encrypt the User’s password using the bcryptjs library which you’ll install soon.
  2. The next step is to use your PrismaClient instance (via prisma as we covered in the steps about context) to store the new User record in the database.
  3. You’re then generating a JSON Web Token which is signed with an APP_SECRET. You still need to create this APP_SECRET and also install the jwt library that’s used here.
  4. Finally, you return the token and the user in an object that adheres to the shape of an AuthPayload object from your GraphQL schema.

You can now open GraphiQL and play around with your new schema and resolvers, try to run the following mutations:

mutation {
  signup(email: "test@mail.com", name: "Dotan Simha", password: "123456") {
    token
    user {
      id
      name
      email
    }
  }
}

signup mutation

Implementing login resolvers

Now, the login mutation, add it under the signup resolvers.

And if you’ll open Playground, you should be able to login with the user you previously created:

mutation {
  login(email: "test@mail.com", password: "123456") {
    token
    user {
      id
      name
      email
    }
  }
}

login mutation

You should be able to get the information of the user. Please save the authentication token you get, we’ll need that on the next step!

Now on the login mutation!

  1. Instead of creating a new User object, you’re now using your PrismaClient instance to retrieve an existing User record by the email address that was sent along as an argument in the login mutation. If no User with that email address was found, you’re returning a corresponding error.
  2. The next step is to compare the provided password with the one that is stored in the database. If the two don’t match, you’re returning an error as well.
  3. In the end, you’re returning token and user again.

Detecting the current user

Now, you have our users database ready to use, and our next step is to be able to detect who’s the current user that queries the server.

To do that, you’ll need to add the option to pass the authentication token along with our GraphQL operation.

You are not going to use the GraphQL schema in this case, since you don’t want to mix the authentication flow with the GraphQL contract that you have. So you’ll use HTTP headers.

The authentication token will be passed as a HTTP header, in the following form:

Authorization: "Bearer MY_TOKEN_HERE"

To add support for this kind of authentication in our server, you’ll need to be able to access the raw incoming HTTP request, then verify the token and identify the current user.

You also want to be able to tell who’s the current authenticated user during our resolvers, so you’ll inject the current user into the GraphQL context.

So let’s do that:

So what happened here?

  1. Take the Authorization for the incoming HTTP request headers.
  2. Use verify of jsonwebtoken to check that the token is valid, and extract the userId from the token payload.
  3. Use Prisma API to fetch the user from the database.
  4. Return the current user, or null in case of missing/invalid token.

Now, every incoming GraphQL request that has a valid token and a user, will also have the context.currentUser available with the authenticated user details. If an incoming request doesn’t have that, the context.currentUser will be set to null.

So that’s really cool, and to test that, you can add a new GraphQL field under type Query called me that just exposes the current user information.

You can now try it in GraphiQL with the following query:

query {
  me {
    id
    name
  }
}

And under the HEADERS section of GraphiQL, add your authentication token in the following structure:

{
  "Authorization": "Bearer YOUR_TOKEN_HERE"
}

And if you’ll run it, you’ll see that the GraphQL server now being able to authenticate you based on the token!

me query

Connecting other resolvers

If you remember, you added more new fields to the GraphQL schema (such as Link.postedBy), so let’s implement the missing resolvers!

To make sure our server knows how to identify the creator of each Link, let’s modify the resolver of Mutation.post to ensure that only authenticated users can use it, and also add the current authenticated user id to the object created on our database.

Protecting resolvers

Let’s go and finish up the implementation, and connect everything together with the rest of the resolvers.

You can now try it from GraphiQL:

mutation {
  post(url: "www.graphqlconf.org", description: "An awesome GraphQL conference") {
    id
  }
}

carrying the authentication token

Resolving relations

There’s one more thing you need to do before you can launch the GraphQL server again and test the new functionality: ensuring the relation between User and Link gets properly resolved.

In the postedBy resolver, you’re first fetching the Link from the database using the prisma instance and then invoke postedBy on it. Notice that the resolver needs to be called postedBy because it resolves the postedBy field from the Link type in our type-definitions.

You can resolve the links relation in a similar way.

That’s all! Now you have resolvers for all fields, and you can signup, login, identify the user as part of our GraphQL server!

You should be able to run complex GraphQL queries, for example:

query {
  feed {
    id
    description
    url
    postedBy {
      id
      name
    }
  }
}

linked types example

Unlock the next chapter
Which is JWT?
Standard for creating web tokens
Standard for creating GraphQL servers
Standard for connecting with database
Standard to execute queries