I am struggling to understand how service accounts and authorisation operate in the context of Google APIs.
I have a Google Account, let's call it Fred that created a service account: FredsGDriveService. I shared a folder in Fred's Drive with FredsGDriveService and managed to write some .Net code using the Google.Apis.Drivev3 API to upload and download files from this folder.
Now trying to do similar with Google Forms using the recently released Google.Apis.Formsv1 API.
I have a form, located in the shared Drive folder. The form is also shared with FredsGDriveService. In the code I'm using the same credentials as before, and I believe I've given the service account permission to use GoogleForms API. But I get the following error when I attempt to get this form.
{ "error": { "code": 401, "message": "Request had invalid authentication credentials.
Expected OAuth 2 access token, login cookie or other valid authentication credential.
See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"errors": [ { "message": "Invalid Credentials", "domain": "global",
"reason": "authError", "location": "Authorization", "locationType":
"header" } ], "status": "UNAUTHENTICATED" } }
My code is as follows:
private readonly FormsService _formsService;
private string _credentialsFileName = "emailmanager-416312-8e1ba22c772b.json";
public MyFormsService()
{
var _credentialsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, _credentialsFileName);
_formsService = GetFormsService(_credentialsPath);
}
private FormsService GetFormsService(string serviceAccountKeyPath)
{
var credential = GoogleCredential.FromFile(serviceAccountKeyPath)
.CreateScoped(DriveService.Scope.DriveFile + FormsService.Scope.DriveFile + FormsService.Scope.FormsBody);
return new FormsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Dance Addiction Emails",
});
}
public Form GetForm(string formId)
{
// Create a request to get the form with the specified form ID
FormsResource.GetRequest getRequest = _formsService.Forms.Get(formId);
try
{
// Execute the request and retrieve the form
return getRequest.Execute();
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred while getting the form: {ex.Message}");
return null;
}
}
var google = new MyFormsService();
Console.Write(google.GetForm("xxxxFormIdxxxx"));
I have successfully used similar code to create a separate form and then retrieve it, although I've been unable to locate that form through Fred's account, so it's been created in the context of the serviceaccount.
Resolving this is not helped by the fact I don't understand the requirements here, relating to serviceaccounts use and/or OAuth2.My use case is that I have a program that I will be using to manage my forthcoming calendar and everytime I create a new event in this calendar, I need to add it to this google form, so is my choice of a serviceaccount correct for this, or should i be using OAuth2 or BOTH!?
Edit - this is different from the other questions on S/O I have come across because the issue is NOT about HOW to configure serviceauthorisation. I have successfully done that with both drive and forms APIs as described above. It's about why my forms code cannot access a specifically shared resource in the same way the Drive API can.
Edit2 - My fallback was to create the form using the serviceaccount and then share that with my user account, but I've now come across this question which suggests there is no way of doing this currently.
How do I authorise an app (web or installed) without user intervention?
Edit3 - If I change the Form security from restricted access to "anyone with the link", then the service account can access it successfully.