Google OAuth 2.0 - 离线访问的增量授权
Posted
技术标签:
【中文标题】Google OAuth 2.0 - 离线访问的增量授权【英文标题】:Google OAuth 2.0 - Incremental authorization with offline access 【发布时间】:2018-12-31 20:46:33 【问题描述】:我正在尝试在我的应用程序中使用 Google Oauth2 实现 incremental authorization。
起初,当用户登录时,应用程序正在请求一些范围和离线访问。我正在生成 refreshToken 并将其存储在我的数据库中,以便在需要时刷新访问令牌以进行 API 调用。
现在我要实现一个新功能,它会请求一个新的范围,但我只想在用户尝试使用这个新功能时才请求这个范围。
我面临的问题是,当应用程序提示用户同意新范围时,Google 正在请求访问所有以前接受的范围。
我尝试过不进行离线访问(使用'grant' method),它似乎可以正常工作(正如描述中所说的“向用户请求额外的范围。”)。这样做,谷歌只请求新的范围,并且生成的访问令牌似乎工作正常。但如果我尝试离线访问(使用'grantOfflineAccess' method),Google 会要求提供所有范围。如果我再次接受所有范围,它会返回一个授权码,我可以再次生成 refreshToken,但我想只接受新范围。
我想知道在请求离线访问时可能无法执行此操作,因为您需要生成一个包含所有范围的新 refreshToken,但我找不到有关此的任何信息。
我做错了什么还是无法通过离线访问实现这一点?
更新:同时生成 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,有人评论说无法对已安装的应用程序执行此操作。但是有一条评论解释了如何使用 Web 服务器流来执行此操作,这些是使用 OAuth2 操场 (https://developers.google.com/oauthplayground) 的步骤:
1) 选择 Google 日历范围。
2) 授权访问
3) 代币兑换码。
4) 向令牌信息端点发出请求: https://www.googleapis.com/oauth2/v2/tokeninfo?access_token=...
5) 验证令牌是否仅包含 Google 日历范围。
6) 取消选择 Google 日历范围并选择 Google Drive 范围。
7) 在授权窗口中编辑 URL 并追加 &include_granted_scopes=true。
8) 授权 Drive 范围。
9) 用代码交换令牌。
10) 向令牌信息端点发出请求。
11) 验证它是否同时包含 Calendar 和 Drive 范围。
做同样的事情,我在第一次验证中获得日历范围,在第二次验证中获得云端硬盘范围。即使使用刷新令牌,我也无法获得具有两个范围的访问令牌。之后,这很奇怪,我检查了可以访问我的帐户的应用程序,并且 OAuth2 游乐场具有两个范围:
提前谢谢你!
【问题讨论】:
【参考方案1】:我终于完成了使用 JS 库实现增量授权。使用grantOfflineAccess function,您可以发送您想要请求的新范围。默认情况下,当您使用 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=...
我确定这是我一开始尝试过的,所以我想我做错了什么。
无论如何,由于an issue with the library,我决定不使用增量授权。基本上问题是,为了更好的用户体验和避免问题,您应该能够使用prompt: 'consent'
,这样用户就不需要选择一个帐户来接受新的范围。但这是不可能的,如果用户选择不同的帐户来接受来自您的应用程序的不同范围,您可能会遇到问题。
【讨论】:
【参考方案2】:谢谢,我也遇到了同样的问题! Your answer 帮助我弄清楚了。这是我们需要用两个库完成的最终工作流程。希望代码 sn-ps 可能会有所帮助。
-
使用 Google 登录 (google-auth-library) 进行身份验证以获取刷新令牌。
实施增量授权:授权您的应用程序使用他们的信息将其与新的 Google API 集成。例如,Google 日历(google-auth-library 和 googleapis)
使用 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 很有帮助。
【讨论】:
以上是关于Google OAuth 2.0 - 离线访问的增量授权的主要内容,如果未能解决你的问题,请参考以下文章
需要Google OAuth 2.0架构建议通过Java邮件Api发送Smtp邮件
单流:通过 Google oAuth 登录用户并授予离线/服务器访问权限?
通过 OAuth 2.0 和私钥(即服务帐户)访问 Google Contacts Api
使用 OAuth 2.0 Google App Engine 刷新访问令牌