3

I’m building a React app where a key part of the functionality is a user can sign into their Google account and then access a feed of their most recent Google Drive/Docs mentions and notifications. A user arrives at my site where I load the Google OAuth2 client with my client_id, apiKey, scope and discoveryDocs, and they can click a button to sign in. For convenience, I’d like the user to not have to re-login and re-auth with their Google account every time they use the app or the app refreshes, I’d like the login information to be saved across sessions. For this I’ll use localStorage to start but eventually integrate a database like Firebase.

After looking through the JavaScript client Google OAuth2 docs I understand how most things work - understand the data and methods stored in the GoogleUser, GoogleAuth, etc objects. I’m having a little trouble with access and refresh tokens. I recognize that you can get the authenticated user’s information through gapi.auth2.getAuthInstance().currentUser.get() and gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse() returns an object with a lot of what I think I need like id_token, access_token and metadata like expires_at and token_type. I also see the grantOfflineAccess() method from which I extract response.code, but I’m not quite sure which of these tokenized strings is the right one to use and how I need to use it.

This FAQ from Google (https://developers.google.com/api-client-library/javascript/help/faq) is somewhat helpful but advises to Refresh the token by calling gapi.auth.authorize with the client ID, the scope and immediate:true as parameters., but gapi.auth.authorize is noted by Google in the client JS OAuth2 library as being incompatible with the more widely used and heavily documented api.auth2.init and signIn.

I also have a vague idea from posts like Google OAuth2 API Refresh Tokens that I need to follow server-side OAuth2 instructions and I can only get this refresh_token through a server-side call, but I’m still at a bit of a loss. I’ll caveat and say I’m more of a front end developer/designer so I'm shaky on my node and server-side skills.

TL;dr: I don't know how to keep my users who signed in via Google OAuth2 signed in after a refresh. I have an idea it's due to refresh_token and access_token and I have access to them but I don't know what to do after that, in terms of sending data to Google servers, getting information back, and setting the token information for the given user when they return.

Here's my method that calls on componentDidMount (basically when my app first loads):

loadGoogleClient = () => {

    gapi.load("client:auth2", () => {
        gapi.auth2.init({
          'client_id': my-client-id,
          'apiKey': my-key,
          'scope': "https://www.googleapis.com/auth/drive.readonly",
          'discoveryDocs': ['https://content.googleapis.com/discovery/v1/apis/drive/v3/rest']
        })

            // Listen for sign-in state changes.
        console.log(`User is signed in: ${gapi.auth2.getAuthInstance().isSignedIn.get()}`);
        gapi.client.load("https://content.googleapis.com/discovery/v1/apis/drive/v3/rest")
            .then(() => { console.log("GAPI client loaded for API"); 
                }, (error) => { console.error("Error loading GAPI client for API", error); 
            });
    console.log('Init should have worked');
    });
}

And here's my code that's onClick on my Signin button:

authGoogle = () => {
    gapi.auth2.getAuthInstance()
            .signIn({scope: "https://www.googleapis.com/auth/drive.readonly"})
            .then(function() { console.log("Sign-in successful"); },
                  function(err) { console.error("Error signing in", err); });
}
Stephen
  • 35
  • 1
  • 8

1 Answers1

3

If you are using the client lib (the gapi api) there is no need for a refresh token... Once logged in it should persist across sessions and refreshes... The issue is the code...

1) Include this in your index.html in the head section:

<script src="https://apis.google.com/js/api.js"></script>

2) Here is a component that will handle auth using the gapi lib and render a button conditionally (The code is self-explanatory but if you have a question just ask...)

import React from 'react';

class GoogleAuth extends React.Component {
  state = { isSignedIn: null };

  componentDidMount() {
    window.gapi.load('client:auth2', () => {
      window.gapi.client
        .init({
          clientId: '<your client id here...>',
          scope: 'email', // and whatever else passed as a string...
        })
        .then(() => {
          this.auth = window.gapi.auth2.getAuthInstance();
          this.handleAuthChange();
          this.auth.isSignedIn.listen(this.handleAuthChange);
        });
    });
  }

  handleAuthChange = () => {
    this.setState({ isSignedIn: this.auth.isSignedIn.get() });
  };

  handleSignIn = () => {
    this.auth.signIn();
  };

  handleSignOut = () => {
    this.auth.signOut();
  };

  renderAuthButton() {
    if (this.state.isSignedIn === null) {
      return null;
    } else if (this.state.isSignedIn) {
      return <button onClick={this.handleSignOut}>Sign Out</button>;
    } else {
      return <button onClick={this.handleSignIn}>Sign in with Google</button>;
    }
  }
  render() {
    return <div>{this.renderAuthButton()}</div>;
  }
}

export default GoogleAuth;

Now you can simply use this component/button anywhere in your app... Meaning if you have a Navigation component simply import it there and use it as a button login / log out...

SakoBu
  • 3,972
  • 1
  • 16
  • 33
  • Ah thanks! This was clarifying and useful. I had the ` – Stephen Mar 21 '19 at 21:14
  • 1
    @Stephen, what if I have to use the Google API token on the back end to perform some actions. – Puneet Uppal May 24 '19 at 09:37
  • @PuneetUppal did ya manage to retrieve google's access token? – Itay Feldman Jun 21 '20 at 21:25
  • @ItayFeldman This is the client side lib... If you have a backend look into the passport strategy. Having said that, I do recall that the access token does get stored in local storage in the browser... You shouldn’t have any issues getting it.... it would be “hacky” but you could... – SakoBu Jun 21 '20 at 21:55
  • Thanks for your response, I managed getting it from the gapi auth object we get after sign in > window.gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse().id_token – Itay Feldman Jun 22 '20 at 06:45