Reputation: 21
I'm trying to make a summary on responses for a Google Form. For that I use Google Forms API in Golang. I didn't find the Question text in the responses, just the QuestionId. So I decided to get the list of questions from the form itself. Surprisingly, I don't have issues with getting the responses, but I can't get the form.
I wrote the code based on Google Docs API Go Quickstart and modified it based on the Google Forms API doc.
Each time I run the program I get the Authentication error:
Unable to retrieve data from document: googleapi: Error 403: Request had insufficient authentication scopes.
Details:
[
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"domain": "googleapis.com",
"metadata": {
"method": "google.apps.forms.v1.FormsService.GetForm",
"service": "forms.googleapis.com"
},
"reason": "ACCESS_TOKEN_SCOPE_INSUFFICIENT"
}
]
Code is below:
// Retrieves a token, saves the token, then returns the generated client.
func getClient(config *oauth2.Config) *http.Client {
tokFile := "token.json"
tok, err := tokenFromFile(tokFile)
if err != nil {
tok = getTokenFromWeb(config)
saveToken(tokFile, tok)
}
return config.Client(context.Background(), tok)
}
// Requests a token from the web, then returns the retrieved token.
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
fmt.Printf("Go to the following link in your browser then type the "+
"authorization code: \n%v\n", authURL)
var authCode string
if _, err := fmt.Scan(&authCode); err != nil {
log.Fatalf("Unable to read authorization code: %v", err)
}
tok, err := config.Exchange(oauth2.NoContext, authCode)
if err != nil {
log.Fatalf("Unable to retrieve token from web: %v", err)
}
return tok
}
// Retrieves a token from a local file.
func tokenFromFile(file string) (*oauth2.Token, error) {
f, err := os.Open(file)
if err != nil {
return nil, err
}
defer f.Close()
tok := &oauth2.Token{}
err = json.NewDecoder(f).Decode(tok)
return tok, err
}
// Saves a token to a file path.
func saveToken(path string, token *oauth2.Token) {
fmt.Printf("Saving credential file to: %s\n", path)
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
log.Fatalf("Unable to cache OAuth token: %v", err)
}
defer f.Close()
json.NewEncoder(f).Encode(token)
}
var srv *forms.Service
var srvForm *forms.FormsService
func main() {
ctx := context.Background()
b, err := os.ReadFile("credentials.json")
if err != nil {
log.Fatalf("Unable to read client secret file: %v", err)
}
config, err := google.ConfigFromJSON(b, forms.FormsBodyReadonlyScope, forms.FormsResponsesReadonlyScope)
if err != nil {
log.Fatalf("Unable to parse client secret file to config: %v", err)
}
client := getClient(config)
srv, err = forms.NewService(ctx, option.WithHTTPClient(client), option.WithScopes(forms.DriveScope, forms.FormsResponsesReadonlyScope))
if err != nil {
log.Fatalf("Unable to retrieve Docs client: %v", err)
}
srvForm = forms.NewFormsService(srv)
if srvForm != nil {
docId := "HERE GOES ID OF A FORM"
doc, err := srvForm.Get(docId).Do()
if err != nil {
log.Fatalf("Unable to retrieve data from document: %v", err)
}
fmt.Printf("The title of the doc is: %s\n", doc.Items[0].Description)
for _, v := range doc.Items {
fmt.Printf("The description is: %s\n", v.Description)
fmt.Printf("The title is: %s\n", v.Title)
fmt.Printf("The title is: %s\n", v.QuestionItem.Question.QuestionId)
}
}
}
As shown in the examples here, it should be enough to provide "https://www.googleapis.com/auth/forms.body.readonly" as a Scope, but apparently it is not enough and even "https://www.googleapis.com/auth/drive" is not enough.
As you can see from the code, I've tried to provide the Authentication Scopes to google.ConfigFromJSON and to forms.NewService, but it doesn't help.
Upvotes: 1
Views: 161
Reputation: 21
Apparently you can't just add the new scope to a program when token was created, it is necessary to delete the token and obtain new permission via the consent screen. After that code works fine.
Upvotes: 1