迁移到 People API 时出现 ACCESS_TOKEN_SCOPE_INSUFFICIENT 错误

Get ACCESS_TOKEN_SCOPE_INSUFFICIENT error migrating to People API

我有一个桌面 Java 应用程序,我正在从 Google 联系人 API 迁移到人员 API。我有一些工作。例如,我可以检索联系信息。但是当我尝试创建新联系人时,出现以下错误:

com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden
POST https://people.googleapis.com/v1/people:createContact
{
  "code" : 403,
  "details" : [ {
   "@type" : "type.googleapis.com/google.rpc.ErrorInfo",
   "reason" : "ACCESS_TOKEN_SCOPE_INSUFFICIENT"
  } ],
  "errors" : [ {
    "domain" : "global",
    "message" : "Insufficient Permission",
    "reason" : "insufficientPermissions"
  } ],
  "message" : "Request had insufficient authentication scopes.",
  "status" : "PERMISSION_DENIED"
}

相关代码如下:

protected void createContact() throws Exception {
    
    Credential credential = authorize(PeopleServiceScopes.CONTACTS, "people");
    
    PeopleService service = new PeopleService.Builder(
              httpTransport, JSON_FACTORY, credential).setApplicationName(APPLICATION_NAME).build();

    
    Person contactToCreate = new Person();
    List<Name> names = new ArrayList<Name>();
    names.add(new Name().setGivenName("John").setFamilyName("Doe"));
    contactToCreate.setNames(names);

    Person createdContact = service.people().createContact(contactToCreate).execute();
    System.out.println("CREATED Contact: " + createdContact.getNames().get(0).getDisplayName());
}

protected Credential authorize(String scope, String subDir) throws Exception {
    
    File dataStoreDir = new File(System.getProperty("user.home"), ".store/myapp/" + cfg.dataStore + "/" + subDir);
    
    // initialize the transport
    httpTransport = GoogleNetHttpTransport.newTrustedTransport();

    // initialize the data store factory
    dataStoreFactory = new FileDataStoreFactory(dataStoreDir);
    
    // load client secrets
    GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY,
        new InputStreamReader(SyncMgr.class.getResourceAsStream("/client_secrets.json")));
    if (clientSecrets.getDetails().getClientId().startsWith("Enter")
            || clientSecrets.getDetails().getClientSecret().startsWith("Enter ")) {
        System.out.println(
                "Enter Client ID and Secret from https://code.google.com/apis/console/?api=calendar "
                + "into /client_secrets.json");
        System.exit(1);
    }
    // set up authorization code flow
    GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
            httpTransport, JSON_FACTORY, clientSecrets,
            Collections.singleton(scope)).setDataStoreFactory(dataStoreFactory).build();
    // authorize
    return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize(cfg.gUser);
}

当我第一次 运行 它时,我将范围设置为 CONTACTS_READONLY。我得到了同意屏幕。但是当我添加代码以创建新联系人时,我将范围更改为 CONTACTS。那是我收到 ACCESS_TOKEN_SCOPE_INSUFFICIENT 错误的时候。

我在另一个 post 中看到,当您更改范围时,我需要强制您的应用重新授权用户,以便您再次获得同意屏幕。但我不确定该怎么做。有什么建议吗?

谢谢。

22 年 1 月 4 日更新

我尝试了 Gabriel 关于删除对应用程序的访问权限的建议。删除访问权限后,我再次 运行 应用程序。这次我在 execute() 调用中遇到了这个错误:

com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request
POST https://oauth2.googleapis.com/token
{
  "error" : "invalid_grant",
  "error_description" : "Token has been expired or revoked."
}

甚至以前用于检索联系人的 execute() 语句现在也出现同样的错误。

我的应用程序还使用了日历 API。我没有碰那个代码。但是当我尝试使用它时,我得到了同样的“invalid_grant”错误。我现在该怎么办?

如果您希望再次检索同意屏幕,您可以按照此 documentation and then try to authorize the app again. As you mentioned, the error received is due to the scope that was granted with authorization was CONTACTS_READONLY instead of CONTACTS when checking the authorization scope for this specific create contacts method 中的步骤从您的帐户设置中删除对您的应用程序的访问权限。

您似乎在使用 People.createContact 方法。如果我们查看文档,我们会发现此方法需要用户同意以下权限范围

现在,如果我们检查您的代码,您似乎正在使用

Credential credential = authorize(PeopleServiceScopes.CONTACTS, "people");

这正是需要的范围。但是您最初只读了那里。因此,当您的代码 运行 第一次授权用户访问只读范围而不是完整的联系人范围时,您就卡住了。

这里的关键是这段代码

// set up authorization code flow
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
        httpTransport, JSON_FACTORY, clientSecrets,
        Collections.singleton(scope)).setDataStoreFactory(dataStoreFactory).build();
// authorize
return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize(cfg.gUser);

请注意,我不是 Java 开发人员,我是 .net 开发人员。图书馆非常接近,多年来我一直在用两种语言帮助解决这个问题。

dataStoreFactory 是存储用户同意的地方。在您的目录结构中的某个位置应该有一个 json 文件,其中包含与之关联的用户名,这就是您的系统重新加载它的方式。当您的代码运行时,它将在该目录中查找名称为 cfg.gUser 的文件。

Java 客户端库中应该有一种方法可以强制它重新请求用户授权。提示类型强制。但我将不得不环顾四周,看看如何在 java 中做到这一点。

现在最简单的解决方案是找到该目录并删除该用户的文件,或者只是将用户名 cfg.gUser 更改为 cfg.gUser +"test" 或其他会导致该名称的名称更改和文件名。强制它再次提示用户授权。

这次当它请求同意时,请注意它请求的权限范围。

令牌已过期或撤销。

这可能是因为您的刷新令牌即将过期。当您的应用程序处于测试阶段时,刷新令牌会在 7 天后过期或由 google 自动撤销。

这是 Google 去年左右添加的新内容。不幸的是,如果刷新令牌以这种方式过期,则客户端库未设计为再次请求访问。