使用Golang在Google Admin SDK SDK上获取400 invalid_grant。 有什么建议么?

I am trying to work out a golang script that uses my service account to manage my google domain. I get an error when I try to do a simple user list: 400 invalid_grant. It appears that I am using my service account correctly(?), and my service account is a super admin. I am using credentials in java code; so I know that it is valid. Any thoughts?

package main

import (
    "fmt"
    "io/ioutil"
    "log"

    "golang.org/x/net/context"
    "golang.org/x/oauth2/google"
    directory "google.golang.org/api/admin/directory/v1"
)

func main() {
    serviceAccountFile := "/credentials.json"
    serviceAccountJSON, err := ioutil.ReadFile(serviceAccountFile)
    if err != nil {
        log.Fatalf("Could not read service account credentials file, %s => {%s}", serviceAccountFile, err)
    }

    config, err := google.JWTConfigFromJSON(serviceAccountJSON,
        directory.AdminDirectoryUserScope,
        directory.AdminDirectoryUserReadonlyScope,
    )

    // Add the service account.
    config.Email = "serviceaccount@domain.com"

    srv, err := directory.New(config.Client(context.Background()))
    if err != nil {
        log.Fatalf("Could not create directory service client => {%s}", err)
    }

    // The next step fails with:
    //2019/03/25 10:38:43 Unable to retrieve users in domain: Get https://www.googleapis.com/admin/directory/v1/users?alt=json&maxResults=10&orderBy=email&prettyPrint=false: oauth2: cannot fetch token: 400 Bad Request
    //Response: {
    //  "error": "invalid_grant",
    //  "error_description": "Robot is missing a project number."
    //}
    //exit status 1
    usersReport, err := srv.Users.List().MaxResults(10).OrderBy("email").Do()
    if err != nil {
        log.Fatalf("Unable to retrieve users in domain: %v", err)
    }

    if len(usersReport.Users) == 0 {
        fmt.Print("No users found.
")
    } else {
        fmt.Print("Users:
")
        for _, u := range usersReport.Users {
            fmt.Printf("%s (%s)
", u.PrimaryEmail, u.Name.FullName)
        }
    }
}

I got this working. It seems like it may be a combination of things. DazWilkin, yes I get the 401 unauthorized error when I switch around how I pass in my service account. I made the following changes.

  1. Used Subject instead of Email in the configuration.
  2. Only used the AdminDirectoryUserScope scope, instead of the AdminDirectoryUserReadonlyScope scope (or the combination of both).
  3. Included the Domain in the request. I get a 400 Bad Request without it.
  4. I verified that Directory APIs were on from this link: https://developers.google.com/admin-sdk/directory/v1/quickstart/go . When I clicked on this link, it said that apis were already working. I am using the same json credentials here that I am using in some production scripts written in other languages. Meaning, I thought that this was already in place. So I don't think I needed to do this step, but I will include it in case it is useful for others.

Here is what my script looks like now:

package main

import (
    "fmt"
    "io/ioutil"
    "log"

    "golang.org/x/net/context"
    "golang.org/x/oauth2/google"
    directory "google.golang.org/api/admin/directory/v1"
)

func main() {
    serviceAccountFile := "/credentials.json"

    serviceAccountJSON, err := ioutil.ReadFile(serviceAccountFile)
    if err != nil {
        log.Fatalf("Could not read service account credentials file, %s => {%s}", serviceAccountFile, err)
    }

    // I want to use these options, but these cause a 401 unauthorized error
    //  config, err := google.JWTConfigFromJSON(serviceAccountJSON,
    //      directory.AdminDirectoryUserScope,
    //      directory.AdminDirectoryUserReadonlyScope,
    //  )
    config, err := google.JWTConfigFromJSON(serviceAccountJSON,
        directory.AdminDirectoryUserScope,
    )

    // Add the service account.
    //config.Email = "serviceaccount@domain.com" // Don't use Email, use Subject.
    config.Subject = "serviceaccount@domain.com"

    srv, err := directory.New(config.Client(context.Background()))

    if err != nil {
        log.Fatalf("Could not create directory service client => {%s}", err)
    }

    // Get the results.
    usersReport, err := srv.Users.List().Domain("domain.com").MaxResults(100).Do()
    if err != nil {
        log.Fatalf("Unable to retrieve users in domain: %v", err)
    }

    // Report results.
    if len(usersReport.Users) == 0 {
        fmt.Print("No users found.
")
    } else {
        fmt.Print("Users:
")
        for _, u := range usersReport.Users {
            fmt.Printf("%s (%s)
", u.PrimaryEmail, u.Name.FullName)
        }
    }
}

Fixed!

Thanks to Sal.

Here's a working Golang example:

https://gist.github.com/DazWilkin/afb0413a25272dc7d855ebec5fcadcb6

NB

  • Line 24 --config.Subject
  • Line 31 --You'll need to include the CustomerId (IDPID) using this link

Here's a working Python example:

https://gist.github.com/DazWilkin/dca8c3db8879765632d4c4be8d662074