Photo by Mabel Amber Table of Contents Part I: Setup and displaying short URLs using GraphQL Part II: Creating short URLs Part III: Creating serverless function for hashing Part IV: Short URL stats (number of clicks) Part V: User authentication (login, sign-up) (this post) Happy New Year and welcome back! In this part which is last part of the series, we are going to implement user authentication (login and sign up) as well as modify our queries, mutations and subscriptions to support this. At the moment, anyone can go to the site and create as many short links as they want. What we want want to do is to require users to sign-up first, then they can login to the site and can create their own links there without seeing anyone else’s links. If you’re interested in looking at the final version of the project, visit the GitHub repo . here Login and Sign-up React components Let’s start with a two simple components that we are going to use to sign-up and login to the site. Login component Create the file with the following contents (we will come back in a bit to implement the login function and wrap the component with necessary containers): src/components/Login.js This will give us an awesome looking login component (seriously, I need to do some CSS and get better with design — recommendations more than welcome!): Awesome login component Similarly, let’s do the sign up component. Sign-up component Sign-up looks pretty much the same as login: Hooking up the components In order to render these two new components, we need to slightly modify the to say that whenever someone requests or path, we are going to render the corresponding components. After you’ve imported both and components in , add the following two routes below the component route: AppRouter /login /signup Login Signup AppRouter.js Home <Route exact path="/login" component={Login} /><Route exact path="/signup" component={Signup} /> First up yarn ( ) and navigate to and to see the components render. yarn start /login /signup Now that we have the components in place, let’s go to the backend and bring in the email/password auth. Add auth support to Graphcool service Graphcool has support for templates that allow you to pull in various functionality to your projects — there’s a whole of officially supported templates that include, for example, Twilio integration, Mailgun integration (send text messages and emails as part of a subscription, in a resolver function, etc.) and (good news for us) email/password authentication. Other interesting auth related templates are Facebook auth, Google auth, Github, etc. GitHub repo We are going to use the that consists of 3 functions that are described in more details below. email-password authentication template Signup For signup we define a new mutation called that has this signature: signupUser signupUser(email: String!, password: String!): SignupUserPayload This is the mutation we are calling when user tries to sign up for an account. What happens as part of this mutation is defined by our function that we define (similarly as we did in previous post where we defined a function that handled a subscription). Here’s what the signup function does: Validates the email address Checks if the user with that email exists or not Hashes the password Creates a new user Generates a token we use to identify the user In the end, it returns an ID of a newly created user and an a token we will store locally. In addition to the things above, this would be the place where we could add things such as sending a welcome email, or making user confirm their email address etc. Authenticate Authenticate is another new mutation we define and it’s very similar in signature to the signup mutation: authenticateUser(email: String!, password: String!): AuthenticateUserPayload This is the mutation that gets called when user is trying to log in to our website. As part of this mutation we are going to check if the user with the provided email exists, and if it does, we ensure the passwords match then finally generate token and return it. Logged-in user The last part of the template is a query that we can execute and it’s going to return us user ID if the request itself contains a valid token. The idea is that we are going to execute this query on components that require authentication and then before rendering the component we can check if we got any user data back or not — if we didn’t we can redirect the user to the login page, and if we did, we can continue and render the component. Let’s add the template to our service by running this command from our subfolder: graphcool/ graphcool add-template graphcool/templates/auth/email-password Above command will pull down the .graphql and .ts files that represent the mutations and the functions we explained above. We still need to manually update the and files to include these in our service. If you open any of those two files, you will see that some commented out declarations were added to it — this is Graphcool CLI helping us to define the functions and/or models. types.graphql graphcool.yml Go ahead and uncomment the functions in as well as the User model in the file. graphcool.yml types.graphql Note: we will come back to the _types.graphql_ file shortly and add the relation between User and Link models, so don’t worry about that at the moment. Let’s deploy the changes and make sure all is good — run and after a couple of seconds you should get the list of resolver functions and types that were created. graphcool deploy Next, let’s implement sign up and login functionality and then as a last step we will hook up the relation between Links and Users. Sign me up! As explained above we need to call the signupUser mutation to sign the user up and store the token we get back from the mutation. This is the mutation we are going to use in Signup.js component: const SIGNUP_USER_MUTATION = gql`mutation SignupUser($email: String!, $password: String!) {signupUser(email: $email, password: $password) {idtoken}}`; We also need to wrap the component with : graphql export default graphql(SIGNUP_USER_MUTATION, { name: 'signupUserMutation' })(Signup,); Finally, we need to implement the function. Here’s the full implementation: signup There’s nothing to complicated in the code above — we are calling the mutation then taking the ID and token and storing it in the local storage. Finally, we redirect the user to home page. Note : you probably noticed the lack of proper error handling — writing errors blindly to the console is not good — make sure you always parse the errors and show the sanitized error message , instead of full errors that could include a call stack and possibly information that shouldn’t be ever shared. To fix this, you could add an error property to the state and update it with a generic error such as ‘Sign up failed — try again’. I’ve added this as an issue in the GitHub repo . We can try out the signup now and see if it works — navigate to and try entering an email and a sample password — if all is good, you should get redirected to the home page. You can also check that the user information was stored in the Graphcool service (tip: run and click on Data). /signup graphcool playground Log me in Now that you have your account created, let’s modify the login component in a similar way we updated the Signup component. Here’s the mutation we are using: const AUTHENTICATE_USER_MUTATION = gql`mutation AuthUser($email: String!, $password: String!) {authenticateUser(email: $email, password: $password) {idtoken}}`; Wrap the component with graphql container: export default graphql(AUTHENTICATE_USER_MUTATION, {name: 'authenticateUserMutation',})(Login); And implement the login function that’s pretty much identical with the sign up function (difference being the mutation we call): Try this out by going to the /login page and using the same email/password you used to sign up. If you get redirected to the home page, it worked! Issue we have to solve next is the fact that regardless if you’re logged in or not, when you navigate to the home page, you’ll always get the list of links and ability to create links. We should change that, so if you’re not logged in, we render something that gives you links to login and sign up page, and if you are logged in, we will show you the links. No links for you! Remember that third query we added earlier, the query? This is what we are going to use. But first, we need to make a change that’s going to attach the user token from the local storage with each GraphQL request we make. loggedInUser There’s a pattern in Apollo that allows us to do exactly that — with we can inject the authorization token to each request. Install it first: ApolloLink $ yarn add apollo-link After you’ve imported from , we can create a new instance of it in the file: ApolloLink apollo-link index.js We are defining an in the code above (lines 1–10), then reading our from the local storage, creating the auth header with the token (or null if token is not there) and setting it as an authorization header. Finally, we call the function and pass in the updated operation (with the authorization header) — this continues with the chain of calls and eventually the request goes out to the server. Lastly, we need to update the link we are using in line 20 to be the we defined in line 12. apolloLinkWithToken SHORTLY_TOKEN forward httpLinkWithToken At this point, if we are logged in and we try to call the query we will get back the ID of the logged in user. Let’s add this functionality to the component. First, this is the query we are going to use: loggedInUser Home.js const LOGGED_IN_USER_QUERY = gql`query CurrentUser {loggedInUser {id}}`; Notice that we don’t need to provide any parameters to the above query, but when the function behind the query runs, it will check if there was an authorization header attached to it and figure out which user ID corresponds to that token and return us the ID of that user (or nothing, if there was no or invalid auth header provided). Let’s wrap the component: export default graphql(LOGGED_IN_USER_QUERY, { name: 'currentUser' })(Home); And update the render method to render different UI based on the fact if user is logged in or not as well as implement the logout functionality. Here’s the full Home.js file with all these things implemented: In the render method we wait for the query to execute and then try to get the from the query (line 27). Finally, we use a simple if statement to check if we got the user id, then render the component with the logout button and the list of links, otherwise we render a with links to login and sign up page. Check the screenshots below for both: userId div View when user is not logged in Logged-in view with the user Id and a logout button Yay, this looks much better (from the functional standpoint, not the design standpoint :)). Last thing left is to actually add the relation between User and Link models and modify the queries a bit, so we only display links from the logged in users. Linking things together Let’s open the types.graphql file and connect the Link and User models together by adding the bolded lines: type Link @model {...createdBy: User @relation(name: "UserLinks")...} type User @model {...links: [Link!]! @relation(name: "UserLinks")...} The above changes are adding a field to each link that points to the User that created the link, and a field in User model that gives us an array of all links that the user created. createdBy Since schema changes involve new relations with required values, I am suggesting you delete all links and users you’ve created so far testing this, then run to deploy the updated schema. graphcool deploy Hopefully deploy worked without issues (if it didn’t, make sure to check what the error is and usually, deleting the old data fixes any issues). We can start updating the queries now. Let’s start with and by including the createdBy field in the query: CreateShortLink.js CREATE_SHORT_LINK_MUTATION const CREATE_SHORT_LINK_MUTATION = gql`mutation CreateLinkMutation($url: String!$description: String!** $createdById: ID!**) {createLink(url: $urldescription: $description ) {id}}`; createdById: $createdById And passing the user ID we read from the local storage to the mutation in createShortLink function: await this.props.createShortLinkMutation({variables: {url,description, },}); createdById : localStorage.getItem('SHORTLY_ID') Note : we could have also added the _loggedInUser_ query to the component and use the user ID from that query to create a link. An even better solution would probably be to create a container called _LoggedInContainer_ and use that to wrap all components that we want to use when user is logged in. That way, we do the _loggedInUser_ query on the container level and not separately in each component. Next up are the queries and in file. ALL_LINKS_QUERY LINKS_SUBSRIPTION LinkList.js In we need to filter all links to only those that belong to the logged in user (changes in bold and rest of the query is omitted): ALL_LINKS_QUERY const ALL_LINKS_QUERY = gql`query AllLinksQuery( ) {allLinks**(filter: { createdBy: { id: $createdById } })** {...}}`; $createdById: ID! Since we added the field to the filter, we need to update the export statement to pass the id from the local storage to the like this: createdById ALL_LINKS_QUERY export default graphql(ALL_LINKS_QUERY, {name: 'allLinksQuery', })(LinkList); options: props => ({variables: {createdById: localStorage.getItem('SHORTLY_ID'),},}), Also, we need to update the filter on our subscription, so it only fires when the user currently logged-in creates or updates a link. Here’s the variable with updates in bold: LINKS_SUBSCRIPTION subscription NewLinkCreatedSubscription( ) {Link(filter: {mutation_in: [CREATED, UPDATED] })... $createdById: ID! node: { createdBy: { id: $createdById } } We also need to pass the variable to the subscription when we call function in the . Here’s the line to add after the document property: createdById subscribeToMore componentDidMount variables: { createdById: localStorage.getItem('SHORTLY_ID') } Similarly as with previous queries, we read the user ID from the local storage and pass it as a createdById variable to the document (GraphQL query). AND WE ARE DONE At this point you can create multiple users, create links for each of them and verify that you only see the links from the logged in user. Final demo! Conclusion If you got this far — ! I hope I was able to provide you with some useful information throughout these series. I’d love to hear your feedback — you can comment on the story below or ping me on Twitter or on GitHub. thank you If you’re interested in the final code, check the GitHub repo here — feel free to fork the repo and play with the code. I’ll also log some issues on the repo that I’d like to fix in the coming weeks, but if something catches your eye, I’ll be more than happy to look at any pull requests!