Authentication

In this section, you’ll learn how you can implement authentication functionality with Apollo and Graphcool to provide a login to the user. Hang in there, because this will be a lengthy section. Once it’s all over you will understand how to handle authentication with Apollo and Ember though!

One Service To Rule Them All

Let’s breeze through what is happening in this piece of code as it’s vital to the application in the long run.

  1. You are defining two constants, GC_USER_ID and GC_AUTH_TOKEN which hold the key values that localStorage will need.
  2. Right off the bat in the service initialization, you are executing two methods to get the user’s ID and auth token, if they are set in localStorage.
  3. You are also defining those two methods getUserId and getAuthToken which query the localStorage for the value and pass it to each method to be set on your service.
  4. Two methods, removeUserId and removeAuthToken, are created to remove the value from localStorage and reset the local value, on the service, to null.
  5. Finally, you have the two methods that are setting the value in localStorage and on the service.

What’s missing from this long block of code is methods to login, signup, and logout. You will add those in a moment, but first let’s talk about how Graphcool handles authentication.

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 command 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 and update the User type to now also include the email and password fields:

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

Next you need to make one more modification to the schema. Generally, when updating the schema of a Graphcool project, you’ve got two ways of doing so:

  1. Use the web-based Graphcool Console and change the schema directly
  2. Use the Graphcool project file and the CLI to update the schema from your local machine

You added two things to the schema:

  • A new field on the User type to store the name of the user.
  • A new relation between the User and the Link type that represents a one-to-many relationship and expresses that one User can be associated with multiple links. The relation manifests itself in the two fields postedBy and links.

Here is the Terminal output after you execute the command:

$ graphcool push
 ✔ Your schema was successfully updated. Here are the changes: 

  | (*)  The type `User` is updated.
  ├── (+)  A new field with the name `name` and type `String!` is created.
  |
  | (+)  The relation `UsersLinks` is created. It connects the type `Link` with the type `User`.

Your project file project.graphcool was updated. Reload it in your editor if needed.

Note: You can also use the graphcool status command after having made changes to the schema to preview the potential changes that would be performed with graphcool push.

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

Authentication with Graphcool

Time to quickly add the remaning methods to your auth service that would allow us to login, sign up, and logout.

First you need to add the signInUserMutation and createUser mutations to your mutations folder.

Let’s look through what these new methods are doing:

  1. isLoggedIn is a simple computed property utility you will use in templates regularly.
  2. loginOrSignUp is a method that will, depending on the value of state, either create a new user or sign in an existing user. In both cases the user will end up signed in and their id and token is passed to saveUserData.
  3. logout is simple method that removes the user’s ID and token on the service and localStorage, and resolves a Promise.
  4. Finally saveUserData executes the methods to set the user ID and token on the service and in localStorage.

With that, you have a working authentication service and necessary mutations. Next you will create your login route and template and wire it all up to work.

Implementing your Login route

This controller is:

  1. Defining an action to change the value of loginState.
  2. Adding an action to handle signing up and logging in.
  3. Maintaining a state called loginState.

Now add the template for your login route.

It’s a pretty simple template that brings us full circle with the login/signup functionality.

Go ahead and test the login functionality. Run yarn start and open http://localhost:4200/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.

Integrate authentication with site-header

You need to update your site-header component to add a link to the login route, and to show auth status.

You are accessing your auth service so you can use the isLoggedIn helper and logout method. You also added a link to the login route.

Redirecting user on logout

You need to now redirect a user when they click the logout link. For this you will need to add a new add-on and an application level route file.

This add-on allows you to specify a route level action and fire that via the route-action helper.

This action is defined on the application route so when a user clicks the “logout” button it will be fired no matter where they are in the application.

The last piece to all of this is that you need to pass in that route-action to your site-header component so it can be called. Earlier you added an action to the site-header component that calls the navigateHome action. This is simply hooking it all up to work now!

Now if you run the app, with yarn start, login, navigate to /create, then click the logout link in the header, you should be redirected back to the home page!

Updating the createLink-mutation

I promise you are nearly done with authentication; only this section and one more!

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.

There are two major changes. You first added another argument to the mutation that represents the id of the user that is posting the link. Secondly, you also include the postedBy information in the payload of the mutation.

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

Perfect! Now, before you send the mutation you are gathering the user’s ID and setting that into the variables to be sent!

Configuring Apollo 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 of the API requests are actually created and sent by the ApolloClient in your app, you need to make sure it knows about the user’s token. Luckily, Apollo provides a nice way for authenticating all request by using middleware.

You need to override the default ApolloService that ember-apollo-client is exposing so you can add your own middleware.

Here you are importing the service that ember-apollo-client uses, and a utility that you will use to add your authorization middleware.

Let’s walk through this code:

  1. You are not extending the typical Ember service, but are instead extending that ApolloService you imported.
  2. You are using that middlewares utility you imported to add an new middleware
  3. That new middleware checks that there are no headers. If there are none, then the user’s authToken is retrieved, and attached to the authorization 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.

At this point you should have a working login route to authenticate/signup with, and a working site-header component that updates based on the user’s login status. You should also have a link creation process that attaches the user ID to that created link.

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