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!
Let’s breeze through what is happening in this piece of code as it’s vital to the application in the long run.
GC_USER_ID
and GC_AUTH_TOKEN
which hold the key values that localStorage
will need.localStorage
.getUserId
and getAuthToken
which query the localStorage
for the value and pass it to each method to be set on your service.removeUserId
and removeAuthToken
, are created to remove the value from localStorage
and reset the local value, on the service, to null.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.
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:
You added two things to the schema:
User
type to store the name
of the user.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 withgraphcool push
.
Perfect, you’re all set now to actually implement the authentication functionality inside your app.
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:
isLoggedIn
is a simple computed property utility you will use in templates regularly.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
.logout
is simple method that removes the user’s ID and token on the service and localStorage
, and resolves a Promise.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.
This controller is:
loginState
.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.
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.
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!
createLink
-mutationI 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!
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:
ApolloService
you imported.middlewares
utility you imported to add an new middlewareauthToken
is retrieved, and attached to the authorization headerThat’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.