Authentication

In this section, you’ll learn how you can implement authentication functionality with Relay and Graphcool to provide a login to the user.

Prepare the React components

As in the sections before, you’ll set the stage for the login functionality by preparing the React components that are needed for this feature. You’ll start by implementing the Login component.

Let’s quickly understand the structure of this Login new component. It can have two major states based on the boolean flag that’s represented by state.login.

One state is for users that already have an account and only need to login, here the component will only render two input fields for the user provide email and password. Notice that state.login will be true in this case.

The second state is for users that haven’t created an account yet and thus still need to sign up. Here, you also render a third input field where users can provide their name. In this case, state.login will be false.

The method _confirm will be used to implement the mutations that we need to send for the login functionality.

Next you also need to provide the constants.js file that we use to define keys for the credentials that we’re storing in the browser’s localStorage.

With that component in place, you can go and add a new route to your react-router-dom setup.

Finally, go ahead and add Link to the Header that allows the users to navigate to the Login page.

You first retrieve the userId from local storage. If the userId is not available, the submit-button won’t be rendered any more. That way you make sure only authenticated users can create new links.

You’re also adding a second button to the right of the Header that users can use to login and logout.

Here is what the ready component looks like:

Before you can implement the authentication functionality in Login.js, you need to prepare the Graphcool project and enable authentication on the server-side.

Enabling Email-and-Password Authentication & Updating the Schema

This will open up the Graphcool Console - the web UI that allows you to configure your Graphcool project.

This will open the popup that allows you to enable the Graphcool’s email-based authentication mechanism.

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
}

Next, you have to make sure that the changes introduced by the authentication provider are reflected in your local project file. You can use the graphcool pull to update your local schema file with changes that happened remotely.

Note: 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.

This will bump the schema version to 2 and update the User type to look also include the email and password fields:

type User implements Node {
  createdAt: DateTime!
  email: String @isUnique
  id: ID! @isUnique
  password: String
  updatedAt: DateTime!
}

Perfect, you’re all set now to actually implement the authentication functionality inside your app.

Implementing the Login Mutations

createUser and signinUser are two regular GraphQL mutations that you can use in the same way as you did with the createLink mutation from before.

You’ll start with the createUser mutation.

Let’s quickly understand what’s going in the code that you just added.

You’re again defining a mutation by using the graphql function. The template string that you’re tagging with graphql actually contains two mutations at once!

The first mutation is used to create a new User. It takes the SignupUserInput as an argument, which is essentially a wrapper object for the user’s name, email and password.

The second mutation is used to log in the user and will return a token that you can attach to all subsequent requests and thus authenticate the user against the API.

When these two mutations are sent to the server, it will execute them synchronously from top to bottom. This means that the server will first create the user and then directly log them in so that you don’t have to send an additional request to obtain the user’s authentication token. Neat!

To send the mutation, you need to use the commitMutation function again and pass it the mutation, the environment and the right user input.

Let’s quickly walk through what’s going on here!

  1. Here you prepare the first input object that you’re passing as an argument into the createUser mutation
  2. Then, directly after that you’re creating the second input object for the signinUser mutation
  3. Once the arguments are ready and stored in variables, you’re calling commitMutation and pass the required data
  4. Finally, you’re implementing onCompleted again where you retrieve the id of the user and their authentication token and pass it into a callback

Go ahead and add the single signinUser mutation right away so that users can also login without having to create an account.

This code is very similar to the mutation you just implemented, just a bit simpler. In fact, it’s very much the same setup except that the createUser mutation is removed - so the sole purpose of this mutation will be to authenticate an existing User and get a token for them from the server.

Calling the Login Mutations

Finally, you need to make sure that the two mutations can be called from within the Login component.

The code is pretty straightforward. If the user wants to only login, you’re calling the SigninUserMutation and pass the provided email and password as arguments. Otherwise you’re using the CreateUserMutation where you also pass the user’s name. The last argument in both cases is the callback that receives the id and token which you’re then storing in localStorage using the _saveUserData method and navigate back to the root route.

Before you’re running the app, you need to import the mutations and run the Relay Compiler again.

Now invoke the Relay Compiler.

You can now create an account by providing a name, email and password. Once you did that, the submit-button will be rendered again:

Updating the createLink-mutation

Since you’re now able to authenticate users and also added a new relation between the Link and User type, you can also make sure that every new link that gets created in the app can store information about the user that posted it. That’s what the postedBy field on Link will be used for.

All you do is include a new argument that represents the id of the user who is posting the link.

Secondly, you should also include the information about the user in the mutation’s payload so Relay can put it into the cache.

Now you need to make sure that the id of the posting user is included when you’re calling the mutation in _createLink.

For this to work, you also need to import the GC_USER_ID key.

Perfect! Before sending the mutation, you’re now also retrieving the corresponding user id from localStorage. If that succeeds, you’ll pass it to the call to createLinkMutation so that every new Link will from now on store information about the User who created it.

If you haven’t done so before, go ahead and test the login functionality. Run yarn start and open http://localhost:3000/login. Then click the need to create an account?-button and provide some user data for the user you’re creating. Finally, click the create Account-button. If all went well, the app navigates back to the root route and your user was created. You can verify that the new user is there by checking the data browser or sending the allUsers query in a Playground.

Configuring Relay with the Auth Token

Now that users are able to login and obtain a token that authenticates them against the Graphcool backend, you actually need to make sure that the token gets attached to all requests that are sent to the API.

Since all the API request are actually created and sent by Relay in your app, you need to make sure it knows about the user’s token.

All you need to do for that is reconfigure the fetchQuery function that you’re currently using to instantiate the Relay Environment and attach the token to a header.

That’s it - now all your API requests will be authenticated if a token is available.

Note: In a real application you would now configure the authorization rules (permissions) of your project to define what kind of operations authenticated and non-authenticated users should be allowed to perform.

Unlock the next chapter
What are the names of the two mutations that are added to the Graphcool project after the Email+Password Auth Provider was enabled?
loginUser & logoutUser
signinUser & createUser
createUser & loginUser
signinUser & logoutUser