3
votes

I'm using the Mirror API .NET Library with a Google Service Account to do 3rd Party Authentication via the MyGlass app. I'm following the Authentication for GDK Glassware documentation to authenticate my users server-side and then insert the account.

When I try to insert a new account using the Mirror API, I get an OAuth Error: "invalid_grant":

Stack Trace:

Google.Apis.Auth.OAuth2.Responses.TokenResponseException was unhandled by user code HResult=-2146233088 Message=Error:"invalid_grant", Description:"", Uri:"" Source=Google.Apis

at Google.Apis.Requests.ClientServiceRequest`1.Execute() in c:\code\google.com\google-api-dotnet-client\default_182\Tools\Google.Apis.Release\bin\Debug\output\default\Src\GoogleApis\Apis\Requests\ClientServiceRequest.cs:line 96\r\n

It's being unwrapped and thrown by the Execute() function in ClientServiceRequest.cs. Line 96.

Source Code:

const string password = "notasecret";
X509Certificate2 certificateToExport = new X509Certificate2(HostingEnvironment.MapPath("/Path/To/Certificate.p12"), password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
var rsa = (RSACryptoServiceProvider)certificateToExport.PrivateKey;

// Have to export the provider or you get an "Invalid Algorithm" error when 
// trying to sign the request.
RSACryptoServiceProvider cryptoProvider = new RSACryptoServiceProvider();
cryptoProvider.ImportParameters(rsa.ExportParameters(true));

var serviceAccountCredential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(id: "MYCLIENTID.apps.googleusercontent.com")
{
Key = cryptoProvider,
Scopes = new List<string>() { "https://www.googleapis.com/auth/glass.thirdpartyauth" },
User = "MYCLIENTID@developer.gserviceaccount.com"
});

var mirrorService = new MirrorService(new BaseClientService.Initializer() { HttpClientInitializer = serviceAccountCredential });
Account account = new Account() { AuthTokens = new AuthToken[] { new AuthToken() { AuthTokenValue = sessionKey, Type = "sessionKey" } } };

// Exception thrown here
Account insertedAccount = mirrorService.Accounts.Insert(account, userToken: userToken, accountType: "example.com", accountName: accountName).Execute();

Some things I've double and triple-checked:

  • I have the correct client ID and email address.
  • I have provided the scope: https://www.googleapis.com/auth/glass.thirdpartyauth.
  • The userToken that was passed to me as a query parameter by the MyGlass app webview is provided back to the MirrorAPI.
  • I'm signing the request with the certificate provided to me by Google.
  • MyGlass is using the correct authentication redirect URL
  • The Google Analytics documentation says about an "invalid_grant" error:
  1. Your server's clock is not in sync with NTP.
  2. The refresh token limit has been exceeded.

Server clock is in-sync. This happens on multiple machines. Will investigate whether/how the token limit has been exceeded. I was under the impression that the API library would handle refreshes for me.

I feel I've completely mis-understood something (likely). I'd appreciate someone pointing out what I've got wrong here.

2
If this question requires further clarification, please let me know.PabloC
You do understand that a service account is its own entity, which means even if you could use a service account with the mirror api you would only manage to write to the service accounts glass timeline. I don't think that the service account owns a pair of glass. What exactly is it you are trying to do?DaImTo
The account command lets you insert a new Account into the Account Manager on Glass. You are logging in using a Service account. What is it you are trying to do exactly. More information needed.DaImTo
Thanks DalmTo. I'm trying to insert an account into the mirror api so that I can retrieve the account on the set of glasses that authenticated when the app was installed in MyGlass. I'm following Google's documentation on this, which specifically says you should use a service account to do this. Granted the documentation could be wrong, but they've referred to it themselves throughout the approval process, so I'm pretty sure it's correct.PabloC
No it's specifically for running on server side apps. That's why it's a service account and not a client account.PabloC

2 Answers

1
votes

From the chat thread, the issue was due to the Service Account authentication failing due to the wrong data being passed in the ServiceAccountCredential class.

The code snippet used in the original question should be fixed with:

var serviceAccountCredential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(id: "MYCLIENTID@developer.gserviceaccount.com")
{
    Key = cryptoProvider,
    Scopes = new List<string>() { "https://www.googleapis.com/auth/glass.thirdpartyauth" }
});
0
votes

Make sure that you've submitted your Glassware at https://developers.google.com/glass/distribute/form. Be sure to mention that you're wanting to add accounts as well, and give them the account type for your application. I recall having to do this on my application before I was able to add accounts. You'd think this would be in their documentation, but I don't believe it is.