客户端登录后,在服务器端对G +用户进行身份验证

问题描述:

我正在尝试设置一个使用Google登录按钮,允许人们在我的网站上购买商品.

I'm trying to set up a Sign in with Google button that will allow people to purchase things on my website.

客户端身份验证看起来非常简单,但是我正在努力了解服务器端身份验证的工作方式.在示例中代码,它们将客户端的代码"参数传递给服务器,在该服务器中可以将其交换为访问令牌,然后可以使用该令牌来查看用户朋友的列表.

Client-side authentication looks pretty straightforward, but I'm struggling to understand how the server-side authentication works. In the example code they pass the client-side "code" parameter to the server, where it can be traded in for an access token, which can then be used to see the list of the user's friends.

但是我不想看到用户的朋友列表.我只想确定客户实际上就是他们声称的那个人.

But I don't want to see the list of the user's friends. I just want to be certain that the client is actually who they claim to be.

在检索令牌之后,示例代码将令牌放入会话中,并且似乎使用令牌的存在来验证用户是否已通过身份验证.那是正确/安全吗?我的服务器是否应该在进行购买时以某种方式(如何?)重新验证令牌?我是否应该在每次请求时不断与Google一起重新验证令牌? (希望不是吗?)

After retrieving the token, the sample code puts the token in the session, and seems to use the presence of the token to verify that the user is authenticated. Is that correct/safe? Should(n't) my server re-verify the token somehow (how?) when it's time to do a purchase? Should I constantly re-verify the token with Google on every request? (Hopefully not?)

在执行购买之前,您可能想通过将用户ID从客户端安全地传递给您,来验证用户是否符合您的期望服务器并根据存储的凭据的用户ID对其进行验证.如果攻击者通过劫持会话伪装成您的站点用户,并且在接受用户付款之前进行的最相关的检查,则可以提供额外的保护,以防止重放攻击.

What you might want to do before performing a purchase is to verify the user is who you expect them to be by securely passing the user id from the client to your server and verifying it against the user id for the stored credentials. This offers additional protection against replay attacks where an attacker is pretending to be your site's user by hijacking their session and is the most relevant check to make before accepting payment from the user.

我不会仅仅依靠用户验证来防止欺诈.您应该使用安全的付款系统,例如 Google Commerce平台,并遵循

I would not rely on user validation alone as a mechanism for protection against fraud. You should use a secure payment system such as the Google Commerce platform and follow the best practices for commerce.

提醒一下,每次初始化缓存的凭据时,都应使用OAuth2 v2端点检查您的令牌.检查每个请求似乎有点多余,因为您应该使用已经过验证并存储在服务器端的缓存凭据.最多可以在更新访问令牌时执行检查,但是如果您信任刷新令牌,那么在创建帐户并设置刷新令牌时执行检查应该足够安全.

As a reminder, the OAuth2 v2 endpoint should be used to check your token every time the cached credentials are initialized. Checking on every request seems a bit excessive because you should be using cached credentials that have already been validated and stored server-side. At most, you could perform the checks when you update an access token but if you trust your refresh token, you should be sufficiently safe if you perform the checks when you create the account and set that refresh token.

除了在创建帐户时验证用户ID之外,还执行以下步骤:

The following steps are taken in addition to the user id validation upon account creation:

  • 验证客户的身份.这样可以防止您将伪造的访问令牌传递给您的应用,从而使用您的配额代表攻击者有效地发出请求.
  • 验证该帐户是由您的应用程序创建的,此如您在链接文章中所述, Google+快速入门中的示例代码应充分展示如何执行这些检查以多种编程语言进行帐户授权.

    As mentioned in your linked post, the sample code in the Google+ quickstarts should sufficiently demonstrate how to perform these checks in a variety of programming languages for account authorization.

    在HTML/JS客户端中,以下代码显示在何处检索userId(值,而不是特殊字符串"me"),以传递给connect方法以验证Google+ userId:

    Within the HTML/JS client, the following code shows where the userId (value, as opposed to the special string "me") is retrieved for passing to the connect method to verify the Google+ userId:

      var request = gapi.client.plus.people.get( {'userId' : 'me'} );
      request.execute( function(profile) {
          $('#profile').empty();
          if (profile.error) {
            $('#profile').append(profile.error);
            return;
          }
          helper.connectServer(profile.id);
          $('#profile').append(
              $('<p><img src=\"' + profile.image.url + '\"></p>'));
          $('#profile').append(
              $('<p>Hello ' + profile.displayName + '!<br />Tagline: ' +
              profile.tagline + '<br />About: ' + profile.aboutMe + '</p>'));
          if (profile.cover && profile.coverPhoto) {
            $('#profile').append(
                $('<p><img src=\"' + profile.cover.coverPhoto.url + '\"></p>'));
          }
        });
    

    ...,下面的代码显示了正在传递的Google+ ID.

    ... and the following code shows the Google+ id being passed.

    connectServer: function(gplusId) {
      console.log(this.authResult.code);
      $.ajax({
        type: 'POST',
        url: window.location.href + '/connect?state={{ STATE }}&gplus_id=' +
            gplusId,
        contentType: 'application/octet-stream; charset=utf-8',
        success: function(result) {
          console.log(result);
          helper.people();
        },
        processData: false,
        data: this.authResult.code
      });
    }
    

    在Java示例中执行这些检查的相关代码如下:

    The relevant code performing these checks in the Java sample is as follows:

          // Check that the token is valid.
          Oauth2 oauth2 = new Oauth2.Builder(
              TRANSPORT, JSON_FACTORY, credential).build();
          Tokeninfo tokenInfo = oauth2.tokeninfo()
              .setAccessToken(credential.getAccessToken()).execute();
          // If there was an error in the token info, abort.
          if (tokenInfo.containsKey("error")) {
            response.status(401);
            return GSON.toJson(tokenInfo.get("error").toString());
          }
          // Make sure the token we got is for the intended user.
          if (!tokenInfo.getUserId().equals(gPlusId)) {
            response.status(401);
            return GSON.toJson("Token's user ID doesn't match given user ID.");
          }
          // Make sure the token we got is for our app.
          if (!tokenInfo.getIssuedTo().equals(CLIENT_ID)) {
            response.status(401);
            return GSON.toJson("Token's client ID does not match app's.");
          }
          // Store the token in the session for later use.
          request.session().attribute("token", tokenResponse.toString());
          return GSON.toJson("Successfully connected user.");
        } catch (TokenResponseException e) {
          response.status(500);
          return GSON.toJson("Failed to upgrade the authorization code.");
        } catch (IOException e) {
          response.status(500);
          return GSON.toJson("Failed to read token data from Google. " +
              e.getMessage());
        }
    

    在示例中,ClientID来自Google API控制台,对于您的应用程序而言是唯一的.

    In the samples, the ClientID came from the Google API console and will be distinct for your application.