Sending an Authenticated Request from JavaScript

This page describes how to send an authenticated request from a JavaScript app running locally to a REST API created using Endpoints Frameworks. The JavaScript app shows how to use Google Sign-In and how to send a Google ID token in the request to authenticate the user. When the JavaScript app sends the request, Endpoints Frameworks authenticates the user before passing the request to the backend code that is running on the App Engine standard environment.

Prerequisites

To run the sample JavaScript app:

  • Locate the project ID that you created for the sample API because you will need to add it to the sample JavaScript code. If you need help finding your project ID, see Listing projects.

  • You need a web server on your local computer to serve the sample index.html file that contains the JavaScript code. This page includes steps for running SimpleHTTPServer, which comes with Python 2.7, but you can use any web server.

Download the sample JavaScript client code

  1. Clone the sample to your local machine:

    git clone https://github.com/GoogleCloudPlatform/web-docs-samples
    
  2. Change to the directory that contains the JavaScript client:

    cd web-docs-samples/endpoints-frameworks
    

Creating OAuth 2.0 client IDs

To set up the sample for authentication, you need to configure an OAuth 2.0 client ID in the sample JavaScript code and in the backend code. The JavaScript app uses the client ID to obtain a Google ID token from Google's OAuth 2.0 server and sends the Google ID token in the request. Endpoints Frameworks uses the client ID to authenticate the ID token that the JavaScript app sent in the request.

To create a client ID:

  1. Go to the Credentials page in the API Console.

    Go to the Credentials page

  2. From the projects list, select the project that you created for the sample API.

  3. Click the Create credentials button, then select OAuth client ID. If this is your first time creating a client ID in this project, use the sub-steps to set a product name on the consent screen; otherwise, skip to step 4.

    1. Click the Configure consent screen button.
    2. Enter a name in the Application name field.
    3. Click Save.
  4. Under Application type, click Web application.

  5. In the Authorized JavaScript origins field, enter the following:

    http://localhost:8080
    
  6. Click Create.

  7. Copy your client ID to the clipboard. Your complete client ID looks like the following, but it is unique to the web application in your project:

    271473851874-mtll5dk2vultovbtilt31hakqbinpuvd.apps.googleusercontent.com

For more information about creating client IDs, see Setting up OAuth 2.0.

Configuring the backend code and redeploying

For Endpoints Frameworks to authenticate the request sent from the JavaScript app, you have to add the client ID you just created to the sample code and redeploy an updated OpenAPI document and the application backend code.

The following procedure assumes that you have already deployed the sample API from Getting Started with Endpoints Frameworks on App Engine. Make sure that you get a successful response when you send a request to the API as described in Sending a request to the API before starting the following procedure.

To configure the backend code and redeploy:

  1. In the directory where you cloned the java-docs-samples repository, change to the directory that contains the Java sample:

    cd [YOUR_WORKING_DIRECTORY]/java-docs-samples/appengine-java8/endpoints-v2-backend
    
  2. Open src/main/java/com/example/echo/Echo.java in a text editor.

  3. In the @ApiMethod annotation for the method getUserEmail, replace YOUR_OAUTH_CLIENT_ID in both the audiences and clientIds attributes with the client ID that you just created.

    @ApiMethod(
        httpMethod = ApiMethod.HttpMethod.GET,
        authenticators = {EspAuthenticator.class},
        audiences = {"YOUR_OAUTH_CLIENT_ID"},
        clientIds = {"YOUR_OAUTH_CLIENT_ID"}
    )
    public Email getUserEmail(User user) throws UnauthorizedException {
      if (user == null) {
        throw new UnauthorizedException("Invalid credentials");
      }
    
      Email response = new Email();
      response.setEmail(user.getEmail());
      return response;
    }

  4. Save Echo.java.

  5. Clean your project and then build your API:

    Maven

    mvn clean package

    Gradle

          gradle clean
          gradle build

  6. Regenerate the OpenAPI document, openapi.json, so that it contains the client ID.

    Maven

    mvn endpoints-framework:openApiDocs

    Gradle

    gradle endpointsOpenApiDocs

  7. Make sure that Cloud SDK (gcloud) is authorized to access your data and services on Google Cloud Platform:

    gcloud auth login
    
  8. Set the default project for the gcloud command line tool. Run the following command and replace [YOUR_PROJECT_ID] with the project that you created for the sample API:

    gcloud config set project [YOUR_PROJECT_ID]
    
  9. Deploy the updated OpenAPI document:

    gcloud endpoints services deploy target/openapi-docs/openapi.json
    
  10. Wait for the command to finish and then redeploy your application.

    Maven

    mvn appengine:deploy

    Gradle

    gradle appengineDeploy

Configuring the JavaScript code

To configure the JavaScript code:

  1. In the web-docs-samples/endpoints-frameworks directory, open main.js in a text editor.
  2. In the function initGoogleAuth, replace YOUR_CLIENT_ID with the client ID that you just created.

    function initGoogleAuth (clientId = 'YOUR_CLIENT_ID') {
      gapi.auth2.init({
        client_id: clientId,
        scope: 'https://www.googleapis.com/auth/userinfo.email'
      }).then(() => {
        document.getElementById('sign-in-btn').disabled = false;
      }).catch(err => {
        console.log(err);
      });
    }

  3. In the function sendSampleRequest, replace YOUR_PROJECT_ID with the project ID that you created for the sample API.

    function sendSampleRequest (projectId = 'YOUR_PROJECT_ID') {
      var user = gapi.auth2.getAuthInstance().currentUser.get();
      var idToken = user.getAuthResponse().id_token;
      var endpoint = `https://${projectId}.appspot.com/_ah/api/echo/v1/email`;
      var xhr = new XMLHttpRequest();
      xhr.open('GET', endpoint + '?access_token=' + encodeURIComponent(idToken));
      xhr.onreadystatechange = function () {
        if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
          window.alert(xhr.responseText);
        }
      };
      xhr.send();
    }

Sending an authenticated request

  1. In the directory where you cloned the web-docs-samples repository, change to the directory that contains the JavaScript sample:

    cd [YOUR_WORKING_DIRECTORY]/web-docs-samples/endpoints-frameworks
    
  2. Start your web server to serve index.html on port 8080. The following example uses Python 2.7's SimpleHTTPServer:

    python -m SimpleHTTPServer 8080
    
  3. Open your browser and in the address bar enter: localhost:8080

    The JavaScript app displays two buttons.

    Sign In

  4. Click the Sign In button. The Sign in with Google page appears.

  5. After you've signed in, click the Send sample request button. The first time you send a request, you might experience a delay of about 20 seconds as App Engine starts up. Endpoints Frameworks intercepts the requests and uses the client ID that you configured in the backend code to authenticate the request. If the authentication is successful:

    1. Endpoints Frameworks passes the request to the sample backend running on App Engine.

    2. In the backend code, the getUserEmail method returns the email address of the user account that you used when you signed in.

    3. The JavaScript client displays an alert dialog box with the email address.

Overview of the JavaScript client

The JavaScript client uses Google Sign-In, which manages the OAuth 2.0 flow. This section provides a brief overview of the JavaScript client code.

Auth setup

Load the Google APIs Platform Library to create the gapi object:

  <script src="https://apis.google.com/js/platform.js?onload=loadAuthClient" async defer></script>
</head>

After the platform library loads, load the auth2 library:

function loadAuthClient () {
  gapi.load('auth2', initGoogleAuth);
}

Initialize the GoogleAuth object:

function initGoogleAuth (clientId = 'YOUR_CLIENT_ID') {
  gapi.auth2.init({
    client_id: clientId,
    scope: 'https://www.googleapis.com/auth/userinfo.email'
  }).then(() => {
    document.getElementById('sign-in-btn').disabled = false;
  }).catch(err => {
    console.log(err);
  });
}

When you initialize the GoogleAuth object, you configure the object with your OAuth 2.0 client ID and any additional options you want to specify. Typically, you specify the access scope. Scopes enable your application to only request access to the resources that it needs while also enabling users to control the amount of access that they grant to your application. Before you start implementing OAuth 2.0 authorization, we recommend that you identify the scopes that your app will need permission to access. This example requests access to the https://www.googleapis.com/auth/userinfo.email scope, which grants access to view the user's email address.

Sign in

After you've initialized the GoogleAuth object, you can prompt the user to sign in by calling the signIn function of the GoogleAuth object:

function signIn () {
  gapi.auth2.getAuthInstance().signIn().then(() => {
    document.getElementById('sign-in-btn').hidden = true;
    document.getElementById('sign-out-btn').hidden = false;
    document.getElementById('send-request-btn').disabled = false;
  }).catch(err => {
    console.log(err);
  });
}

Make a request with the ID token

When the user has finished signing in, send a request with an Authorization header with the user's ID token:

function sendSampleRequest (projectId = 'YOUR_PROJECT_ID') {
  var user = gapi.auth2.getAuthInstance().currentUser.get();
  var idToken = user.getAuthResponse().id_token;
  var endpoint = `https://${projectId}.appspot.com/_ah/api/echo/v1/email`;
  var xhr = new XMLHttpRequest();
  xhr.open('GET', endpoint + '?access_token=' + encodeURIComponent(idToken));
  xhr.onreadystatechange = function () {
    if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
      window.alert(xhr.responseText);
    }
  };
  xhr.send();
}

What's next

Was this page helpful? Let us know how we did:

Send feedback about...

Cloud Endpoints Frameworks for App Engine
Need help? Visit our support page.