Home Authentication
Authentication
Cancel

Authentication

Learning Outcomes

  • implement local and OAuth authentication in an application

Resources

Lab

Video walk through of this lab.

Setup

Sign into GitLab. Fork this project: https://gitlab.com/langarabrian/auth2. Remove the fork relationship. Make the project private. Clone YOUR fork of the project in the Google Cloud Shell.

Change to the frontend directory and install the dependencies and build the frontend as follows:

cd auth2/frontend
yarn install
yarn build

Start a new terminal. Start and run the Feathers backend as follows:

cd auth2/backend
export MONGODBURI='your mongodb connection string goes here'
npm install
PORT=8080 npm run dev

Test the backend by visiting the following URL:

https://8080-dot-...

You should see the shopping list form.

Add a few items to the shopping list. They don’t show on the frontend, but you can see them at the following URL:

http://8080-dot-.../items

Adding Authentication

We are going to modify the application so that it requires the user to authenticate before adding items to the list.

Initial Changes to Backend

In the terminal where the backend is runnig, stop it (Ctrl-C).

Add a new service to the application:

feathers generate service

Answer the prompts as follows:

1
2
3
4
What kind of service is it? Mongoose
What is the name of the service? moreitems
Which path should the service be registered on? /moreitems
Does the service require authentication? Yes

Update backend/src/models/moreitems.model.ts so that it has “description” and “quantity” fields just like the “items” model.

Examine backend/src/services/moreitems/moreitems.hooks.ts

Restart the backend (e.g. PORT=8080 npm run dev).

Initial Changes to Frontend

Convince yourself that the authentication hook is working by replacing items with moreitems in the frontend. On or about line 35 in frontend/src/shoppinglist.js replace client.service('items') with client.service('moreitems'). EVERY TIME YOU MAKE A CHANGE TO THE FRONTEND CODE, you will have to rebuild it by running the following command in the terminal where you last built the frontend:

1
yarn build

Refresh the application in the browser. Open the web console. Try to add a new item. Note the error in the web console.

Create frontend/src/loading.js as follows:

import React, { Component } from 'react';

class Loading extends Component {
  render() {
    return(<h1>Loading...</h1>);
  }
}

export default Loading;

Create frontend/src/login.js as follows:

import React, { Component } from 'react';

class Login extends Component {
  render() {
    return(<h1>(login page goes here)</h1>);
  }
}

export default Login;

Update frontend/src/application.js as follows:

import React, { Component } from 'react';
import Shoppinglist from './shoppinglist';
import Loading from './loading';
import Login from './login';
import client from './feathers';

class Application extends Component {
  constructor(props) {
    super(props);

    this.state = {};
  }

  render() {
    if ( this.state.login === undefined) {
      return(<Loading />);
    } else if ( this.state.login ) {
      return(<Shoppinglist />);
    }
    return(<Login />);
  }
}

export default Application;

Rebuild the frontend. Now if you refresh the application page, you should see the “Loading” message. The next step is to test to see if the user is already logged in during the “Loading” phase. Add this method to the Application class:

  componentDidMount() {
    // Try to authenticate with the JWT stored in localStorage
    client.authenticate().catch(() => this.setState({ login: null }));
  }

Now if you rebuild and refresh the application page, you should see the placeholder for the login page. The next step is to create a button to login with Google. Update the render method of the Login class as follows:

  render() {
    return(
      <a className="btn btn-primary"
         href="/oauth/google"
         role="button">Login with Google</a>);
  }

Now if you rebuild and refresh the application page, you should see a “Login with Google” button. If you click it, you will get an error page. The next step is to register our application with Google.

Final Changes to Backend

Go to the GCP Credentials Dashboard.

Click on “CREATE CREDENTIALS | OAuth client ID”. If you haven’t done so already, you will have to “CONFIGURE CONSENT SCREEN”. For “User Type” choose “External” and click on the “CREATE” button. Fill out the “OAuth consent screen” form as follows:

1
2
3
App name: CPSC 2650 Test
User support email: your email
Developer contact information: your email

Click on the “SAVE AND CONTINUE” button.

For “Scopes”, just click on “SAVE AND CONTINUE”.

For “Test users”, add a Google account that you have access to and then click on “SAVE AND CONTINUE”.

On the “Summary” page, click “BACK TO DASHBOARD”.

Now go back to the “Credentials” page and click on Click on “CREATE CREDENTIALS | OAuth client ID”.

Choose “Web application” for application type. For name, choose whatever you want. For authorised redirect URIs, add:

1
https://8080-dot-...appspot.com/oauth/google/callback

(Obviously here, you are going to use your actual web preview domain). Finally click on the “CREATE” button. Enter the “Client ID” and “Client secret” in backend/config/default.json for google key and secret respectively. You probably don’t want to put the secret directly in the file. Use an environment variable like GOOGLE_OAUTH_SECRET instead.

You will have to make some other changes to this file. The value for oauth.redirect needs to be https://8080-dot-...appspot.com. In the “google” section you also need to set:

1
2
3
"redirect_uri": "https://8080-dot-...appspot.com/oauth/google/callback",
"host": "8080-dot-...appspot.com",
"callback": "/oauth/google/authenticate",

Lastly, we need to make sure the email associated with the Google account is collected when the user logs in. Basically, you can update backend/src/authentication.ts exactly as for GitLab in the tutorial. Just rename “GitHubStrategy” with “GoogleStrategy” and call authentication.register for “google” instead of “gitlab”.

Final Changes to Frontend

If you restart the backend and refresh the application page, you should be able to log in with your Google account, but will end up back on the application page, but stuck in the “Loading…” state. The last thing we need to do is detect that the user has authenticated and update the application state.

Add the following in the componentDidMount method of the Application class right after the call to client.authenticate():

    // On successfull login ...
    client.on('authenticated', login => {

      // ... update the state
      this.setState({ login });
    });

At this point, if you refresh the application page, you should be taken to the shopping list and able to add items.

Assignment

  1. Implement CI/CD for this project and deploy to Google Cloud Run using the custom domain shopping.4949NN.xyz. To get this to work, you will have to add another URI for the Google OAuth Client. Also, modify the backend/config/production.json file and update with all the settings configured to the production domain. If you set the NODE_ENV environment variable to production, the app will pick up the settings from the production.json file. Finally you will have to ensure the other environment variables are available to the running container as well, like MONGODBURI and GOOGLE_OAUTH_SECRET.