Google OAuth 2.0 - 离线访问的增量授权

Google OAuth 2.0 - Incremental authorization with offline access

我正在尝试在我的应用程序中使用 Google Oauth2 实现 incremental authorization

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

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

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

我试过没有离线访问(使用'grant' method) and it seems to be working (as the description says 'Request additional scopes to the user.'). Doing that, Google is only requesting new scope and the access token generated seems to work fine. But if I try with offline access (using 'grantOfflineAccess' method),Google要求所有范围。如果我再次接受所有范围,它 returns 一个授权码,我可以再次生成 refreshToken,但我只想接受新范围。

我想知道在请求离线访问时可能无法执行此操作,因为您需要生成一个包含所有范围的新 refreshToken,但我找不到任何相关信息。

我是不是做错了什么,或者无法通过离线访问实现这一点?

UPDATE:还生成 URL(而不是使用 JS 库),如 here 所述,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:我找到了 reported issue here, where someone commented that it's not possible to do it for installed applications. But there is a comment explaining how to do it with web server flow, these are the steps using OAuth2 playground (https://developers.google.com/oauthplayground):

1) Select the Google Calendar scope.

2) Authorize access

3) Exchange code for token.

4) Make request to the token info endpoint: https://www.googleapis.com/oauth2/v2/tokeninfo?access_token=...

5) Verify that the token only contains the Google Calendar scope.

6) De-select the Google Calendar scope and select the Google Drive scope.

7) Edit the URL in the authorization window and append &include_granted_scopes=true.

8) Authorize the Drive scope.

9) Exchange the code for a token.

10) Make request to the token info endpoint.

11) Verify that it contains both the Calendar and Drive scopes.

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

提前致谢!

我终于完成了使用JS库实现增量授权。使用 grantOfflineAccess function you can send the new scopes you want to request. By default include_granted_scopes is true when you use gapi.auth2.authorize.

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

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

我确定这是我最初尝试过的方法,所以我想我做错了什么。

无论如何,由于an issue with the library,我决定不使用增量授权。基本上问题是,为了更好的用户体验和避免问题,您应该能够使用 prompt: 'consent',这样用户就不需要 select 一个帐户来接受新的范围。但这是不可能的,如果用户 select 不同的帐户接受来自您的应用程序的不同范围,您可能会遇到问题。

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

  1. 使用 Google 登录 (google-auth-library) 进行身份验证以获取刷新令牌。
  2. 实施增量授权:授权您的应用程序使用他们的信息将其与新的GoogleAPI集成。例如,Google 日历(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 后端的详细工作流程,您可能会发现 my gist 有帮助。