1

我正在尝试在我的应用程序中使用 Google Oauth2实现增量授权。

起初,当用户登录时,应用程序正在请求一些范围和离线访问。我正在生成并存储 refreshToken 在我的数据库中,以便我可以刷新访问令牌以在需要时进行 API 调用。

现在我要实现一个新功能,它会请求一个新的范围,但我只想在用户尝试使用这个新功能时才请求这个范围。

我面临的问题是,当应用程序提示用户同意新范围时,Google 正在请求访问所有以前接受的范围。

我尝试过不进行离线访问(使用“授予”方法),它似乎正在工作(正如描述中所说的“向用户请求额外的范围。”)。这样做,谷歌只请求新的范围,并且生成的访问令牌似乎工作正常。但是,如果我尝试离线访问(使用'grantOfflineAccess' 方法),Google 会要求提供所有范围。如果我再次接受所有范围,它会返回一个授权码,我可以再次生成 refreshToken,但我只想接受新范围。

我想知道在请求脱机访问时可能无法做到这一点,因为您需要生成一个具有所有范围的新 refreshToken,但我找不到任何有关此的信息。

我做错了什么还是无法通过离线访问来实现这一点?

更新:还生成 URL(而不是使用 JS 库),如此所述,Google 要求提供所有范围。这将是生成的 URL 的示例:

https://accounts.google.com/o/oauth2/v2/auth?
  scope=my_new_scope&
  redirect_uri=https://myapp.example.com/callback&
  response_type=code&
  client_id=my_client_id&
  prompt=consent&
  include_granted_scopes=true

UPDATE2:我在这里发现了一个报告的问题,有人评论说不可能对已安装的应用程序执行此操作。但是有一条评论解释了如何使用网络服务器流来做到这一点,这些是使用 OAuth2 操场(https://developers.google.com/oauthplayground)的步骤:

1)选择谷歌日历范围。

2) 授权访问

3) 代币兑换码。

4) 向令牌信息端点发出请求: https ://www.googleapis.com/oauth2/v2/tokeninfo?access_token= ...

5) 验证令牌是否仅包含 Google 日历范围。

6) 取消选择 Google Calendar 范围并选择 Google Drive 范围。

7) 在授权窗口中编辑 URL 并附加 &include_granted_scopes=true。

8) 授权驱动范围。

9) 将代码换成代币。

10) 向令牌信息端点发出请求。

11) 验证它是否包含日历和驱动器范围。

做同样的事情,我在第一次验证中获得日历范围,在第二次验证中获得驱动范围。即使使用刷新令牌,我也无法获得具有两个范围的访问令牌。在那之后,这很奇怪,我检查了可以访问我的帐户的应用程序,并且 OAuth2 游乐场有两个范围:

在此处输入图像描述

先感谢您!

4

2 回答 2

4

我终于完成了使用 JS 库实现增量授权。使用grantOfflineAccess 功能,您可以发送您想要请求的新范围。默认情况下,当您使用gapi.auth2.authorize时 include_granted_scopes 为 true 。

所以你只需要发送新的范围,例如:

GoogleUser.grantOfflineAccess({scope: 'https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/calendar'}

然后你会收到一个授权码来换取一个refreshToken。您可以通过使用新的 refreshToken 生成 access_token 并调用令牌信息端点来检查一切是否正常:https ://www.googleapis.com/oauth2/v2/tokeninfo?access_token= ...

我确定这是我一开始尝试过的,所以我想我做错了什么。

无论如何,由于库的问题,我决定不使用增量授权。基本上问题在于,为了获得更好的用户体验并避免出现问题,您应该能够使用prompt: 'consent',因此用户无需选择帐户即可接受新范围。但这是不可能的,如果用户选择不同的帐户来接受来自您的应用程序的不同范围,您可能会遇到问题。

于 2018-08-02T17:38:28.313 回答
1

谢谢,我也遇到了同样的问题!你的回答帮助我弄清楚了。这是我们需要用两个库完成的最终工作流程。希望这些代码片段可能会有所帮助。

  1. 使用 Google 登录 ( google-auth-library ) 进行身份验证以获取刷新令牌。
  2. 实施增量授权:授权您的应用程序使用他们的信息将其与新的 Google API 集成。例如,谷歌日历(google-auth-librarygoogleapis

使用 Google 登录进行身份验证以获取刷新令牌

前端:

$('#signinButton').click(function() {
   auth2.grantOfflineAccess().then(signInCallback);
 });

后端:

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

使用此刷新令牌(您应该将其持久化),您可以随时使用googleapis库创建 Google API 的客户端。

实施增量授权

前端:

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

后端:

const { google } = require('googleapis'); 

// Create an oAuth client to authorize the API call. Secrets should be 
// downloaded from the Google Developers Console.
const oauth2Client = new google.auth.OAuth2(
  YOUR_CLIENT_ID,
  YOUR_CLIENT_SECRET,
  YOUR_REDIRECT_URL
);

client.setCredentials({ refresh_token: refreshToken });

就是这样!您可以使用此客户端将任何 Google API 与您的应用程序集成。有关 Node.js 后端的详细工作流程,您可能会发现我的要点很有帮助。

于 2021-06-20T19:18:13.880 回答