In this section, you’ll learn how you can implement authentication functionality with Relay and Graphcool to provide a login to the user.
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.
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 @model {
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.
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!
createUser
mutationsigninUser
mutationvariables
, you’re calling commitMutation
and pass the required dataonCompleted
again where you retrieve the id
of the user and their authentication token
and pass it into a callbackGo 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.
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:
createLink
-mutationSince 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.
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.