【问题标题】:OAuth 2.0 Access Tokens and Refresh TokensOAuth 2.0 访问令牌和刷新令牌
【发布时间】:2023-05-04 09:56:01
【问题描述】:

我很难理解刷新和访问令牌的正确用法。我知道刷新令牌与授权有关,访问令牌与身份验证有关。我想更好地解释我的用例,以便有人可以在这里帮助我。我在 Google Merchant Center 中有一个多帐户中心。我想在我的代码中集成最新的 OAuth 2.0 身份验证机制。我做到了并且可以成功进行身份验证。我使用 Google Credential 机制来构建凭据对象,并在向 google 发送 httprequest 期间使用 httprequestinitializer 机制注入。创建 google credential 对象时,我看到执行 googleCredential.getAccessToken() 时没有访问令牌,但是当我执行 googleCredential.refreshToken() 和 googleCredential.getAccessToken() 时,我得到一个 accessToken .但是,我正在测试令牌是如何创建的,并且我没有在请求中明确地将这些令牌传递给谷歌。我传递的只是带有客户端机密和其他私钥的 googleCredential 对象。我正在做的任务只是通过 cron 脚本将子帐户产品提要上传到谷歌。

我的问题是,

  1. 在此处传递 googleCredential 对象时,我是否必须在此处处理刷新令牌? (假设脚本运行超过一天)
  2. 什么时候应该使用刷新令牌和访问令牌,在上述用例中对我来说什么是合适的选择? (虽然现在我没有明确传递任何东西,除了 googleCredential 对象)
  3. 访问令牌和刷新令牌的有效期是多少(与上述用例无关,只是要知道,有人说刷新令牌为 14 天,有人说无限期直到用户撤销访问权限等)

如果有人澄清我并把我拉出来,我会很高兴。我知道这个平台主要是为了澄清代码问题,但我的谷歌论坛也没有帮助。所以在这里发帖。

抱歉,我写的太冗长了。

提前致谢。

【问题讨论】:

    标签: google-oauth access-token


    【解决方案1】:

    所谓的 OfflineCredentials 需要 刷新令牌。这些凭据可供不在浏览器中运行的应用程序使用(例如桌面应用程序或一些没有 UI 的批处理),因此无法执行 OAuth2 流程。

    请看Using OAuth 2.0 to Access Google APIs

    1. 如有必要,请刷新访问令牌。

    访问令牌的生命周期有限。如果您的应用程序需要在单个访问令牌的生命周期之外访问 Google API,它可以获得刷新令牌。刷新令牌允许您的应用程序获取新的访问令牌。

    注意:将刷新令牌保存在安全的长期存储中,只要它们仍然有效,就可以继续使用它们。限制适用于每个客户端-用户组合以及所有客户端的每个用户发出的刷新令牌的数量,这些限制是不同的。如果您的应用程序请求足够的刷新令牌以超过其中一个限制,则旧的刷新令牌将停止工作。

    更多信息Offline Access

    在 Java 中,它看起来像这样:

    import com.google.api.ads.common.lib.auth.OfflineCredentials;
    import com.google.api.ads.common.lib.auth.OfflineCredentials.Api;
    import com.google.api.ads.common.lib.auth.OfflineCredentials.ForApiBuilder;
    import com.google.api.ads.common.lib.exception.OAuthException;
    import com.google.api.ads.common.lib.exception.ValidationException;
    import com.google.api.client.auth.oauth2.Credential;
    
    // ...
    
    // Generate offline credentials
    // With a previously created OAuth2 refresh token (see API examples)
    ForApiBuilder forApiBuilder = new OfflineCredentials.Builder().forApi(Api.ADWORDS);
    forApiBuilder.withClientSecrets(clientId, clientSecret);
    forApiBuilder.withRefreshToken(refreshToken);
    
    Credential credential = null;
    try {
      credential = forApiBuilder.build().generateCredential();
    } catch (OAuthException e) {
      throw new Exception("The given credential could not be refreshed: " + e.getMessage());
    } catch (ValidationException e) {
      throw new Exception("Client ID, client secret or refresh token are not valid: " + e.getMessage());
    }
    
    // Build session
    // ...
    

    除了客户端 ID 和客户端密码之外,还需要将刷新令牌传递给凭证生成器。使用有效的 OfflineCredentials,您现在可以为特定的 Google API 构建新会话。

    关于你的第三个问题:查看以下question的接受答案

    这里是源代码,它显示了如何通过命令行获取一次Google AdWords(见范围)的刷新令牌。客户端 ID 和客户端密码必须作为命令行参数传递。

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    
    import org.apache.commons.configuration.Configuration;
    import org.apache.commons.configuration.PropertiesConfiguration;
    
    import com.google.api.ads.common.lib.auth.GoogleClientSecretsBuilder;
    import com.google.api.ads.common.lib.auth.GoogleClientSecretsBuilder.Api;
    import com.google.api.ads.common.lib.auth.GoogleClientSecretsBuilder.GoogleClientSecretsForApiBuilder;
    import com.google.api.ads.common.lib.exception.ValidationException;
    import com.google.api.client.auth.oauth2.Credential;
    import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
    import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest;
    import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
    import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
    import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
    import com.google.api.client.http.javanet.NetHttpTransport;
    import com.google.api.client.json.jackson2.JacksonFactory;
    import com.google.common.collect.Lists;
    
    // ...
    
      private static final String SCOPE = "https://adwords.google.com/api/adwords";
    
      // This callback URL will allow you to copy the token from the success screen
      private static final String CALLBACK_URL = "urn:ietf:wg:oauth:2.0:oob";
    
      public static void main(String[] args) throws Exception {
        if (args.length != 2) {
          System.err.println("Please provide client ID and secret as commandline arguments!");
          System.err.println("If you do not have a client ID or secret, please create one in the API console: https://code.google.com/apis/console#access");
          System.exit(1);
        }
    
        GoogleClientSecrets clientSecrets = null;
        try {
          Configuration configuration = new PropertiesConfiguration();
          configuration.setProperty("api.adwords.clientId", args[0]);
          configuration.setProperty("api.adwords.clientSecret", args[1]);
    
          GoogleClientSecretsForApiBuilder googleClientSecretsForApiBuilder = new GoogleClientSecretsBuilder().forApi(Api.ADWORDS);
          googleClientSecretsForApiBuilder.from(configuration);
    
          clientSecrets = googleClientSecretsForApiBuilder.build();
        } catch (ValidationException e) {
          System.err.println("Invalid client ID or secret!");
          System.exit(1);
        }
    
        // Get the OAuth2 credential
        Credential credential = getOAuth2Credential(clientSecrets);
    
        System.out.printf("Your refresh token is: %s\n", credential.getRefreshToken());
        }
      }
    
      private static Credential getOAuth2Credential(GoogleClientSecrets clientSecrets) throws Exception {
        /*
         * Set the access type to offline so that the token can be refreshed. By
         * default, the library will automatically refresh tokens when it can, but
         * this can be turned off by setting api.adwords.refreshOAuth2Token=false
         */
        GoogleAuthorizationCodeFlow authorizationFlow = new GoogleAuthorizationCodeFlow.Builder(new NetHttpTransport(), new JacksonFactory(), clientSecrets, Lists.newArrayList(SCOPE)).setAccessType("offline").build();
    
        String authorizeUrl = authorizationFlow.newAuthorizationUrl().setRedirectUri(CALLBACK_URL).build();
        System.out.println("Paste this url in your browser: \n" + authorizeUrl + '\n');
    
        // Wait for the authorization code
        System.out.println("Type the code you received here: ");
        String authorizationCode = new BufferedReader(new InputStreamReader(System.in)).readLine();
    
        // Authorize the OAuth2 token
        GoogleAuthorizationCodeTokenRequest tokenRequest = authorizationFlow.newTokenRequest(authorizationCode);
        tokenRequest.setRedirectUri(CALLBACK_URL);
        GoogleTokenResponse tokenResponse = tokenRequest.execute();
    
        // Create the OAuth2 credential
        GoogleCredential credential = new GoogleCredential.Builder().setTransport(new NetHttpTransport()).setJsonFactory(new JacksonFactory()).setClientSecrets(clientSecrets).build();
    
        // Set authorized credentials
        credential.setFromTokenResponse(tokenResponse);
    
        return credential;
      }
    

    代码最初来自Goolge AdWords API example。我的版本不是从配置文件中读取的,因为我不想将客户端 ID 和机密存储在某个资源文件中(稍后我忘了删除)。这就是为什么将值作为参数传递给程序的原因。

    【讨论】:

    • 感谢您的回答。是的,请提供用于访问刷新令牌的 java 代码。
    • @gclaussn 我想知道谷歌在哪一步从刷新令牌生成访问令牌。是在创建凭据时吗?还是在使用凭据加载会话时?是否建议将凭据存储在某个地图中,然后使用该凭据加载每个请求的会话?
    • 必须在您的程序每次想要构建和使用会话来访问 Google API 之前创建凭据。凭据是使用客户端 ID、客户端密码和刷新令牌创建的。当您启用 Google 帐户以使用 AdWords 等特定服务时,会通过 Google Cloud UI 生成客户端 ID 和客户端密码。使用这两个,您必须创建一次刷新令牌(直到它过期或被撤销)。只有应用程序需要离线凭据的刷新令牌,因为它们不在浏览器中运行,所以无法执行 OAuth2 流程。