刷新/使用React应用中的Google OAuth2的刷新令牌后保持用户登录

问题描述:

我正在构建一个React应用,该功能的关键部分是用户可以登录其Google帐户,然后访问其最新的Google云端硬盘/文档提及和通知的供稿.一位用户到达我的网站,在该网站上,我的 client_id apiKey scope discoveryDocs 加载了Google OAuth2客户端,他们可以单击按钮进行登录.为方便起见,我希望用户不必在每次使用该应用程序或刷新应用程序时都重新登录并使用其Google帐户进行身份验证.跨会话保存的登录信息.为此,我将使用localStorage进行启动,但最终会集成Firebase之类的数据库.

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.

浏览JavaScript客户端Google OAuth2文档后,我了解了大多数事情的工作原理-了解了存储在GoogleUser,GoogleAuth等对象中的数据和方法.我在访问和刷新令牌时遇到了一些麻烦.我知道您可以通过 gapi.auth2.getAuthInstance().currentUser.get() gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse获取经过身份验证的用户的信息()返回一个具有很多我想我需要的对象的对象,例如 id_token access_token 和元数据(例如 expires_at token_type .我还看到了从中提取 response.code grantOfflineAccess()方法,但是我不确定这些标记化字符串中的哪一个是正确的,以及如何使用我需要使用它.

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.

此常见问题解答来自Google( https://developers.google.com/api-client-library/javascript/help/faq )有点帮助,但建议通过调用带有客户端ID,范围和Instant:true作为参数的gapi.auth.authorize 刷新令牌.,但是Google在客户端JS OAuth2库中指出 gapi.auth.authorize 与更广泛使用和记录广泛的 api.auth2.init 不兼容.code>和 signIn .

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.

我在 Google OAuth2 API刷新令牌之类的帖子中也有一个模糊的想法,我需要遵循服务器端OAuth2的说明,我只能通过服务器端调用获取此 refresh_token ,但我仍然有些茫然.我会警告说,我更像是前端开发人员/设计人员,所以我对节点和服务器端技能不满意.

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;博士:我不知道如何让刷新后通过Google OAuth2登录的用户保持登录状态.我知道这是由于 refresh_token access_token 造成的,我可以访问它们,但是在将数据发送到Google服务器方面,我不知道该怎么办,获取信息并为给定用户返回时设置令牌信息.

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.

这是我的方法,它调用componentDidMount(基本上是在我的应用首次加载时):

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); });
}

如果您使用的是客户端库(gapi API),则无需刷新令牌...登录后,它应可在会话和刷新中持续存在...问题是代码...

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)将其包括在 head 部分的 index.html 中:

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

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

2)这是一个将使用 gapi lib处理auth并有条件地呈现按钮的组件(该代码是不言自明的,但是如果您有任何疑问,只问...)>

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;

现在,您可以在应用程序中的任何位置简单地使用此组件/按钮.这意味着如果您有Navigation组件,只需将其导入并用作按钮登录/注销...

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...