In this section, you’ll learn how you can implement authentication functionality with Apollo 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 Angular components that are needed for this feature. You’ll start by implementing the Login
component.
Let’s quickly gain an understanding of the structure of this new component which can have two important states.
input
fields for the user to provide their email
and password
. Notice that login
will be true
in this case.input
field where users can provide their name
. In this case, 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 src/app/constants.ts
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 ahead and add a new route to your src/app/app.routing.module
file.
Finally, go ahead and add the Login
link to the Header
component that allows users to navigate to the Login
page.
You first subscribe to the isAuthenticated
stream from authService
and update the logged
property. If the logged
is false, the submit-button won’t be rendered anymore. That way you make sure only authenticated users can create new links.
You’re also adding a second button on the right side of the HeaderComponent
that users can use to login and logout.
Here is what the LoginComponent
and HeaderComponent
components now look like:
Before you can implement the authentication functionality in src/app/login/login.component.ts
, you need to prepare the Graphcool project and enable authentication on the server-side.
Authentication in the Graphcool Framework is based on resolver functions that deal with login-functionality by issuing and returning node tokens which are used to authenticate requests.
Graphcool has a lightweight and flexible template system that allows to conventiently pull in predefined functionality into a service. You’ll be using the email-password
template for authentication.
You can use the CLI’s add-template
command to use a template in your Graphcool service. This command will perform two major tasks:
templates
repository that are required for the email-password
template.graphcool.yml
and types.graphql
that allow you to “activate” the template’s functionality by uncommenting them and then invoking graphcool-framework deploy
again.This now downloaded six new files and placed them in the src/email-password
directory. It also added comments to graphcool.yml
and types.graphql
.
Next, you have to actually “activate” the templates functionality by uncommenting these lines.
If you take a look at the code for these functions, you’ll notice that they’re referencing a User
type that still needs to be added to your data model. In fact, the add-template
command already wrote this User
type to types.graphql
- except that it still has comments.
Before you apply the changes to the running service, you’ll make another modification to your data model by adding the relation between the Link
and the newly added User
type as well as a new field name
for the User
.
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
.Now it’s time to apply the changes by deploying your service again.
Your GraphQL API now includes three additional operations, as specified in graphcool.yml
:
signup
: Create a new user based on email
and password
.authenticate
: Log in existing user with email
and password
.loggedInUser
: Checks whether a user is currently logged in.signup
MutationYou can see the GraphQL interface for the newly added operations in the corresponding .graphql
-files inside the server/src/email-password
directory. Let’s take a look at the interface of the signup
function:
type SignupUserPayload {
id: ID!
token: String!
}
extend type Mutation {
signupUser(email: String!, password: String!): SignupUserPayload
}
The signupUser
-mutation is used to create a new User
in the database. The problem right now is that our schema requires every User
instance to have a name
. However, the above signupUser
-mutation only accepts email
and password
as arguments. You now need to adjust the signup
resolver so it also accepts the name
for the new User
as an input argument and make sure it’s saved when the User
is created.
For now you only adjusted the interface of the signup
resolver. Next, you also need to make sure to update the implementation.
Note: The
signup
resolver is implemented as a serverless function which will be deployed for you by the Graphcool Framework. The input arguments for that function are determined by the input arguments of the corresponding GraphQL operation. In this case, this is thesignupUser
-mutation, so the function will received three string as input arguments:password
andname
. (Notice that these are wrapped in a single object calledevent
though.)
The goal in the new implementation is to retrieve the name
argument from the input event
and send it along when creating the new User
.
All you do is also retrieve the name
from the input event
and then pass it to the createGraphcoolUser
function a bit later.
All that’s left for you now is deploying these changes to make sure your running Graphcool service gets updated and exposes the new functionality in its API.
Perfect, you’re all set now to actually implement the authentication functionality in the frontend as well.
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.
Now, let’s gain a better understanding what’s going on in the two mutations that you just added to the src/app/graphql.ts
file.
The SIGNIN_USER_MUTATION
looks very similar to the mutations we saw before. It simply takes the email
and password
as arguments and returns info about the user
as well as a token
that you can attach to subsequent requests to authenticate the user. You’ll learn a bit how to do so.
The CREATE_USER_MUTATION
however is a bit different! Here, we define two mutations at once! When you’re doing that, the execution order is always from top to bottom. So, in your case, the createUser
mutation will be executed before the signinUser
mutation. Bundling two mutations like this allow you to sign up and login in a single request!
All right, all that’s left to do is to call the two mutations inside the Login
component!
The code is pretty straightforward. If the user wants to only login, you’re calling the SIGNIN_USER_MUTATION
and pass the provided email
and password
as arguments. Otherwise, you’re using the CREATE_USER_MUTATION
where you also pass the user’s name
. After the mutation is performed, you are calling the authService
that will take care of storing the data and navigating back to the root route.
You now need to make a couple more changes to src/app/app.component.ts
to get things working.
authService
userId
on the $root
$data
objectYou can now create an account by providing a name
, email
and password
. Once you do so, 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.
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 CreateLinkComponent
.
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 CREATE_LINK_MUTATION
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. 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.
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 requests are actually created and sent by the HttpLink
in your app, you need to make sure it knows about the user’s token. Luckily, Apollo provides a nice way for authenticating all requests by using headers.
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.