javascript - 单流: sign user in via Google oAuth AND grant offline/server access?

标签 javascript node.js google-oauth google-signin google-api-nodejs-client

我正在尝试为具有 Node.js 后端的 Web 应用程序实现 Google 登录和 API 访问。 Google 的文档使用 platform.js 的组合提供了两个选项客户端和google-auth-library服务器端:

  • Google Sign-Inback-end auth ,用户可以通过它使用他们的 Google 帐户登录我的应用程序。 (auth2.signIn() 在客户端和 verifyIdToken() 在服务器上。)
  • Google Sign-in for server-side apps ,通过它我可以授权服务器代表我的用户直接连接到 Google。 (客户端上的 auth2.grantOfflineAccess(),它返回一个我可以传递给服务器上的 getToken() 的代码。)

  • 我需要两者:我想通过 Google 登录对用户进行身份验证;而且,我想设置服务器身份验证,以便它也可以代表用户工作。
    我无法弄清楚如何使用单个身份验证流程来做到这一点。我能得到的最接近的方法是依次执行两个操作:首先使用 signIn() 验证用户身份,然后(根据需要)通过 grantOfflineAccess() 进行第二次传递.这是有问题的:
  • 用户现在必须背靠背地通过两次身份验证,这很尴尬,并且看起来我的应用程序出现了问题。
  • 为了避免与弹出窗口阻止程序发生冲突,我不能将这两个流程叠加在一起。我必须进行第一次身份验证,然后提供一个按钮来启动第二次身份验证。这非常尴尬,因为现在我必须解释为什么第一个还不够。

  • 理想情况下,signIn() 有一些变体它将离线访问添加到初始身份验证流程中并返回代码以及通常的 token ,但我什么也没看到。帮助?
    (编辑:我在其他地方收到的一些建议是仅实现流程 #2,然后使用安全 cookie 存储某种用户标识符,我会在每个请求中检查用户帐户。我可以看到这在功能上可以正常工作,但基本上意味着我正在滚动自己的登录系统,这似乎会增加我在关键系统中引入错误的机会。)

    最佳答案

    要将 API 添加到现有的 Google 登录集成,最好的选择是实现增量授权。为此,您需要同时使用 google-auth-librarygoogleapis ,以便用户可以拥有这个工作流程:

  • 使用 Google 登录进行身份验证。
  • 授权您的应用程序使用他们的信息将其与 Google API 集成。例如,谷歌日历。

  • 为此,您用于身份验证的客户端 JavaScript 可能需要对请求进行一些更改
    离线访问:
    $('#signinButton').click(function() {
       auth2.grantOfflineAccess().then(signInCallback);
     });
    
    在响应中,您将拥有一个带有授权码的 JSON 对象:
    {"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}
    
    在此之后,您可以使用一次性代码将其交换为访问 token 和刷新 token 。
    Here are一些工作流程细节:

    The code is your one-time code that your server can exchange for its own access token and refresh token. You can only obtain a refresh token after the user has been presented an authorization dialog requesting offline access. If you've specified the select-account prompt in the OfflineAccessOptions [...], you must store the refresh token that you retrieve for later use because subsequent exchanges will return null for the refresh token


    因此,您应该使用 google-auth-library在后端完成此工作流程。为了这,
    您将使用身份验证代码来获取刷新 token 。但是,由于这是一个离线工作流程,
    您还需要验证所提供代码的完整性,如 documentation explains :

    If you use Google Sign-In with an app or site that communicates with a backend server, you might need to identify the currently signed-in user on the server. To do so securely, after a user successfully signs in, send the user's ID token to your server using HTTPS. Then, on the server, verify the integrity of the ID token and use the user information contained in the token


    获取应该保留在数据库中的刷新 token 的最终函数可能如下所示
    这个:
    const { OAuth2Client } = require('google-auth-library');
    
    /**
    * Create a new OAuth2Client, and go through the OAuth2 content
    * workflow. Return the refresh token.
    */
    function getRefreshToken(code, scope) {
      return new Promise((resolve, reject) => {
        // Create an oAuth client to authorize the API call. Secrets should be 
        // downloaded from the Google Developers Console.
        const oAuth2Client = new OAuth2Client(
          YOUR_CLIENT_ID,
          YOUR_CLIENT_SECRET,
          YOUR_REDIRECT_URL
        );
    
        // Generate the url that will be used for the consent dialog.
        await oAuth2Client.generateAuthUrl({
          access_type: 'offline',
          scope,
        });
        
        // Verify the integrity of the idToken through the authentication 
        // code and use the user information contained in the token
        const { tokens } = await client.getToken(code);
        const ticket = await client.verifyIdToken({
          idToken: tokens.id_token!,
          audience: keys.web.client_secret,
        });
        idInfo = ticket.getPayload();
        return tokens.refresh_token;
      })
    }
    
    此时,我们已经重构了身份验证工作流程以支持 Google API。但是,您尚未要求用户对其进行授权。由于您还需要授予离线访问权限,因此您应该通过客户端应用程序请求额外的权限。请记住,您已经需要一个事件 session 。
    const googleOauth = gapi.auth2.getAuthInstance();
    const newScope = "https://www.googleapis.com/auth/calendar"
    
    googleOauth = auth2.currentUser.get();
    googleOauth.grantOfflineAccess({ scope: newScope }).then(
        function(success){
          console.log(JSON.stringify({ message: "success", value: success }));
        },
        function(fail){
          alert(JSON.stringify({message: "fail", value: fail}));
        });
    
    您已经完成了前端更改,并且您只缺少一个步骤。使用 googleapis 在后端创建 Google API 的客户端库,您需要使用上一步中的刷新 token 。
    对于带有 Node.js 后端的完整工作流程,您可能会发现 my gist有帮助。

    关于javascript - 单流: sign user in via Google oAuth AND grant offline/server access?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66339977/

    相关文章:

    java - 访问用户 Google 日历 Activity

    在 Java 网络浏览器中工作的 Javascript?

    javascript - 显示/隐藏 jQuery 不适用于 Ajax

    node.js - 在 leveldb 上按值查找

    javascript - 为什么我们不能通过将长度分配为零来清空 Float32Array() 数组,就像其他普通数组一样?在Javascript中

    javascript - 如何使用 JavaScript 获取 Gmail 联系人?

    javascript - 在本地主机上运行 VueJS 应用程序

    javascript - "delete"- 恢复 native 函数不适用于已更改的原型(prototype),那怎么办?

    javascript - 从 Graph 获取单个消息

    java - 在 Android 中使用 Gmail API 和 OAuth2 检查来自 Gmail 的新电子邮件