This video is available to students only

Executing Login

Having the UI of the Login page built in our client, we'll investigate how we can make the query for Google Sign In's authentication URL when a user attempts to sign in.

With the UI of our <Login /> component mostly prepared, we'll now handle how we can make the authUrl query to direct the user to the Google consent form. We'll then look to handle the response when the consent form redirects a signed-in user back to our client application.

Viewer state object#

Just like how we have the concept of a Viewer in our server, we'll create the same Viewer concept in our client (i.e. the person viewing the app). This Viewer object on the client will determine whether the user has been logged in, and the id and avatar of this user is available, as well whether the user has connected to their Stripe account.

We'll create the Viewer interface type in a types.ts file within the src/lib/ folder which is to be the file we'll keep type definitions that is to be accessed in multiple parts of the client app.

The Viewer interface we'll create will have the same properties as we've seen before - id, token, avatar, hasWallet, and didRequest.

For most of the Viewer fields, null means the fields requested is not available. In our logIn mutation, we're requesting all the fields within the Viewer object type and if they're not available, they will come back as null values.

In the main starting point of our app, the src/index.tsx file, we'll import the Viewer interface and we'll use the useState Hook to create a viewer state object that the child components of <App /> will be able to access and use. We'll initialize this viewer state object with null values for all the fields except for the didRequest field which will be given a false boolean value.

In the useState Hook, we'll also destruct a setViewer() function which will be used to update the state viewer object.

We'll want to have the setViewer() function be available in the <Login /> component so the client viewer object can be updated after the logIn mutation runs. To have the setViewer() function be available in the Login component we can take advantage of React Router's render props pattern and pass in the setViewer() function.

React Router has now introduced Hooks! Be sure to check out the React Router Hooks video in Module 15 of the course to highlight how the above can be done with Hooks.

<Login />#

The <Login /> component now expects a setViewer prop with which we should check for. In the <Login /> component file, we'll create a Props interface and state that setViewer is a prop it will receive. The setViewer prop is to be a function that receives a viewer object with which it uses to update the viewer state property and won't return anything (i.e. void). We have the interface for what represents a viewer in our client so we'll import this Viewer interface and assign it as the type of the viewer argument within the setViewer function prop type. We'll then destruct the setViewer prop from the props argument of the <Login /> component function.

Manual query of authUrl#

We'll continue to work on the <Login /> component. The first thing we'll look to tackle is how we can make the query for the authentication URL from Google Sign-in/OAuth and direct our user to this URL when they attempt to sign-in.

We've created the authUrl field in our GraphQL API that will provide this authentication URL. We know that the useQuery Hook from React Apollo runs a query upon component mount. That isn't what we want to do here. In this context, we'll rather have the authUrl query fired on the click event of the Sign in with Google Button.

To manually fire a query upon an event other than component rendering, React Apollo gives us two options to do so:

  1. Use the useLazyQuery Hook

  2. Run the query() function from the client object obtained from the useApolloClient Hook.

The useQuery and useLazyQuery Hooks leverage and use the client object that can be accessed directly from the useApolloClient Hook.

We'll import and use the useApolloClient Hook. At the top of the <Login /> component, we'll run useApolloClient() to get the client object.

With this client object, we have access to a query() function that will allow us to run the authUrl query manually. With that said, we'll create a handleAuthorize() component function that will fire when the user clicks the Sign in with Google button in our <Login> component template. We'll attach this function as a click listener to the button.

In the handleAuthorize() function, we'll want to use the client object from our useApolloClient Hook to request the authUrl query. First, we'll need to import the authUrl query document from the src/lib/graphql/ folder.

We'll also import the corresponding type definitions for the authUrl query data.

In the handleAuthorize() function, we'll have a try...catch statement. The try statement will use the client object to make our query. The query() function from client will appear similar to how the useQuery Hook behaves. It accepts a type variable for the data (and variables) of the query and an options argument where we can pass in the query document. It returns a result of options where in this case, we're only interested in returning the data from the query.

With our client application running, if we were to head over the login page and click the Sign in with Google button, we'll notice the API call being made in our browsers developer console.

We'll need to have our app be redirected to the url that's returned from the authUrl query. To simply redirect our app to the new URL, we'll use the window.location.href property and set it to the data.authUrl value.

Now, when we click the Sign In With Google Button, we'll be taken to the Google Consent page!

The consent page we see here refers to the project we've set up in our Google cloud developer console with which we've given the name of TinyHouse. If we were to provide a valid email and password for a Google account and sign-in, we'll be redirected back to the /login route of our app.

Why are we being redirected to /login? This is because we've stated in the Google Developer Console for our TinyHouse project, the redirect URI of the OAuth client credentials we've generated (and are using) is going to be http://localhost:3000/login, which is the login page for our development environment.

When we're redirected to the /login page, Google returns a code as part of the URL query parameter. The redirected URL will look something like this.

At this point, we've finished Step 1 of our Google OAuth flow. Our React client must now pass this authorization code to our Node server where our server will use the code to communicate with Google's People API and determine if this is an existing user logging to our app or a new user.

logIn#

We've set up the logIn mutation in our GraphQL API that when triggered will accept the code returned from Google and essentially "log in" our user. To run this mutation, we'll use the useMutation Hook React Apollo provides.

In our <Login /> component, we'll first import the LOG_IN mutation document.

We'll import the autogenerated type definitions for the data to be returned from the logIn mutation and the variables it accepts.

Lastly, we'll also import the useMutation Hook from React Apollo.

At the top of our <Login /> component, we'll use the useMutation Hook to destruct the logIn mutation request as well the results we're interested in. We'll be interested in the data, loading, and error properties with which we'll rename to logInData, logInLoading, and logInError.

We'll want to run the logIn mutation request in a certain condition. We want to run the request the moment our <LogIn /> component is being rendered and the code is available as a query parameter. To run an effect in a certain condition like this, we can use React's useEffect Hook.

We'll import the useEffect Hook and declare it near the top of our component function.

We'll place an empty dependencies list since we don't want the effect we'll create to ever run after the component has been mounted. We'll access the code from the URL query parameter with the URL() constructor and the capability to use the searchParams.get() function. This will allow us to access the value of a certain parameter in our URL. In this case, we're interested in accessing the value of the code parameter.

Start a new discussion. All notification go to the author.