【问题标题】:Google OAuth, handle a revoked authorizationGoogle OAuth,处理撤销的授权
【发布时间】:2019-12-03 20:36:16
【问题描述】:

我一直在使用 Google OAuth 让用户授权访问我的 Web 应用程序的日历服务。在成功3-legged auth flow 之后,我将所有用户的凭据存储在应用服务器上的一个公共文件中。下次应用需要使用该服务时,它会检查凭据是否存在,如果存在,它将假定它们是有效的

代码就是这样工作的

@Override
public void _authorize(String userId) throws IOException {

    // Check if user has already authorised the service.
    Credential credents = flow.loadCredential(userId);

    // Checking if the given user is not authorized
    if (credents == null) {

        //Create credentials now. user will be redirected to authorise 

        try {
            //Creating a LocalServer Receiver
            // Getting the redirect URI
            // Creating a new authorization URL
            // Setting the redirect URI
            // Building the authorization URL
            // Receiving authorization code
            // Exchanging it for an access token

            // Storing the credentials for later access
            credents = flow.createAndStoreCredential(response, id);

        } finally {
            // Releasing resources
        }
    } else {
        // Assume the credentials are valid. so there's nothing left to do here, let's get that client
        //Update: Nooooooot! the user might have revoked the authorization, so credents != null BUT they are invalid
        //TODO: handle an Exception here, and manage the revoked credentials 
    }

    // Setting up the calendar service client
    client = new com.google.api.services.calendar.Calendar.Builder(httpTransport, jsonFactory, credents).setApplicationName(APPLICATION_NAME)
            .build();

}

只要用户从不改变主意,它就可以正常工作。但如果用户决定使用 Google 帐户安全选项manually revoke授权,则 com.google.api.services.calendar.Calendar 检索将失败。

我的问题是

  1. 有没有办法检查凭证是否仍然有效,在尝试使用它们之前? 否则,我只能猜测无法获取客户端对象,这是让我的门户网站意识到凭据不再有效的唯一方法吗?
  2. 我应该如何处理无效/撤销的凭据?我应该只打电话给flow.createAndStoreCredential 他们会被覆盖吗?还是我必须先删除旧的? (怎么做?)

【问题讨论】:

  • 如果凭据删除访问权限,他们不会返回 null 吗?试图找到删除访问权限的页面需要对此进行测试:)
  • @DaImTo no,[flow.loadCredential(id)] 仍然返回一个不会通过 CalendarService 初始化的凭证对象。据我所知,这很正常,因为我的门户没有通过任何其他方式通知用户已撤销身份验证
  • @DaImTo 我添加了指向 Google 帐户安全选项的链接。选择“撤销访问”按钮。说实话,这是一个隐藏得很好的功能,我怀疑大多数谷歌用户都知道这个选项:)
  • @yannicuLar 你找到解决方案了吗?我也遇到了同样的问题。
  • @DanielMarín 对不起,不!我已经做了一个解决方法,如果我有一个失败的服务调用,我将删除存储的通行证,因此将启动一个新的身份验证流程

标签: java google-calendar-api google-oauth


【解决方案1】:

您可以为此使用 refreshToken() 方法。见例子:

// Fetch credential using the GoogleAuthorizationCodeFlow
GoogleAuthorizationCodeFlow authorizationCodeFlow;
Credential credential = authorizationCodeFlow.loadCredential(userId);
if (credential != null) {
    try {
        // refresh the credential to see if the refresh token is still valid
        credential.refreshToken();
        System.out.println("Refreshed: expires in: " + credential.getExpiresInSeconds());
    } catch (TokenResponseException e) {
        // process exception here.
        // This will catch the Exception.
        // This Exception contains the HTTP status and reason etc.
        // In case of a revoke, this will throw something like a 401 - "invalid_grant"
        return;
    }
} else {
    // No credential yet known.
    // Flow for creating a new credential here
}

编辑 如果您确实有一个无效的刷新令牌并且您想要更新它,那么您需要重复您首先执行的步骤以获取凭据。所以:

  • 生成新的授权 URL
  • 将用户重定向到它
  • 用户接受同意屏幕
  • 从重定向中获取授权码返回到您的应用程序
  • 使用授权码向 Google 请求新令牌
  • 使用来自 Google 的响应创建并存储新凭据

无需删除旧凭据。但如果你想明确地这样做,这是可能的。 比如:

// This userId is obviously the same as you used to create the credential
String userId = "john.doe";
authorizationCodeFlow.getDataStore().delete(userId);

【讨论】:

  • 但是你如何获得另一个 RedirectUri 用户可以重新授权应用程序?
【解决方案2】:

您可以使用端点 https://www.googleapis.com/oauth2/v1/tokeninfo 来确定 OAuth2 令牌是否仍然有效。更多信息请访问OAuth2 guide

【讨论】:

  • 我认为如果您使用刷新令牌,此解决方案将不起作用,因为tokeninfo 端点不会验证刷新令牌的有效性。我认为您应该处理库将抛出的未经授权的错误并相应地响应客户端,以便它可以重新提示用户授予授权
  • 没错,tokeninfo 端点仅适用于访问令牌。如果您要从刷新令牌中获取新的访问令牌,那么您需要捕获错误,以防用户删除了对您的应用的授权。
【解决方案3】:

第一个问题的答案:

当使用服务对象从 Google 日历中检索日历项目时,会自动验证令牌。当它们无效时,它们将被自动刷新,并存储在您提供给流的数据存储中。

这也可以手动完成。令牌的有效期为 3600 秒(一小时)。检索令牌时,您会得到这个值以及它发出时的时间戳。您可以手动确定令牌是否有效。如果它无效,请调用以下异步方法。

await credents.RefreshtokenAsync(CancellationToken.None);

此函数为您获取新的令牌,并将它们存储在您提供的数据存储中。

【讨论】:

  • 很抱歉这么说,但我无法理解您的回答,而且缺乏有关处理已撤销令牌的文档也无济于事。另外,我知道 refreshToken 不是用来替换 revoked 令牌,而是 expired 令牌(尽管用户的 auth grand 仍然有效)。通过向我指出该用例的文档,您会真正帮助我
  • 感谢您的链接,但我仍然不明白令牌刷新将如何帮助我。我需要知道什么时候撤销访问,所以我会提示用户从头开始重复授权过程。
  • 我遇到了同样的问题。必须有一种方法来检查授权服务器并获取一个新的 RedirectUri,用户可以在其中再次重新授权。
  • @DanielMarín 对不起,不。我以为我已经在某个时候处理过它,但我还没有找到一个具体的解决方案来测试被撤销的令牌。目前,我已经做了一个解决方法,所以如果身份验证失败(撤销或其他任何事情),我将删除存储的令牌,强制执行新的授权流程
【解决方案4】:

您可以使用 tokeninfo 检查令牌,如果令牌无效: - 从数据存储中删除凭据 - 调用新的身份验证

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException 
{
    UserService userService = UserServiceFactory.getUserService();

    if (userService.isUserLoggedIn()) {
        User user = userService.getCurrentUser();
        log.info(String.format("LoggedUser: %s %s", user.getEmail(), user.getUserId()));
        Credential credential = this.getCredential();
        Tokeninfo tokenInfo = OAuth2Utils.getTokenInfo(credential, null);
        if (tokenInfo != null)
            log.info(String.format("Token expires in: %d", tokenInfo.getExpiresIn()));
        else {
            OAuth2Utils.deleteCredential(user.getUserId());
            response.sendRedirect(request.getRequestURI()); // recall this servlet to require new user authorization
            return;
        }
}

public static Tokeninfo getTokenInfo(Credential credential, String accessToken) {
    Oauth2 service = new Oauth2.Builder(new NetHttpTransport(), Constant.JSON_FACTORY, credential).setApplicationName(Constant.APP_NAME).build();
    Tokeninfo tokenInfo = null;
    try {
        tokenInfo = service.tokeninfo().setAccessToken( accessToken == null ? credential.getAccessToken() : accessToken ).execute();
    } catch (IOException e) {
        log.warning("An error occurred: " + e);
    }
    return tokenInfo;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-13
    • 1970-01-01
    • 2017-06-20
    • 1970-01-01
    • 2012-01-23
    • 2016-10-07
    • 2015-01-13
    相关资源
    最近更新 更多