Проблема
Когда я вызываю «Запрос синхронизации c» в Google HomeGraph API, я получаю ответ «403 Запрещено».
Фон
Я написание «Умного дома» и успешно реализовали SYN C, QUERY и EXECUTE. Тестируя на своем мобильном телефоне, я могу видеть и взаимодействовать с устройствами в порядке. Сейчас я пытаюсь реализовать запрос Syn c, но не могу взаимодействовать с API. Я делаю то, что кажется успешным, запросы на токен доступа. Маркер всегда начинается с "ya29. c." который в моем наивном понимании предполагает пустой заголовок и полезную нагрузку (пробуя его на https://jwt.io). Однако при тестировании на https://accounts.google.com/o/oauth2/tokeninfo?access_token= он кажется действительным, показывая как уникальный идентификатор моей учетной записи службы, так и предполагаемую область. Когда я звоню в API, либо публикую данные вручную, либо через собственный код Google, это дает мне грубую ошибку 403. Я не знаю, где я могу получить больше информации об этой ошибке, кроме объектов исключения. Я новичок в GCP и не могу найти какой-либо журнал. Учитывая, что я пробовал разные методы и все возвращают 403, я склонен подозревать, что проблема связана с учетной записью или учетными данными, а не с кодом, но не может быть уверенным.
Ключ API
(я больше не могу воспроизвести какие-либо ошибки, связанные с отсутствием или недействительностью ключей API).
Хотя в документации это не показано, я видел, что некоторые люди используют Ключ API. Когда я не включаю ключ API с сертификатом p12 или включаю неверный, это приводит к ошибкам (либо с отсутствующим ключом API, либо с неправильным ключом API). Я создал неограниченный ключ API в IAM и использую его. Я не могу явно связать это с HomeGraph API, но он говорит, что он может вызывать любой API.
Код
В этом примере извлекается токен доступа, затем выполняется попытка вызова API через POST с ключом API и без него. Затем он пытается аутентифицировать и вызывать API через код библиотеки Google. Каждый сбой с 403.
using Google;
using Google.Apis.Auth.OAuth2;
using Google.Apis.HomeGraphService.v1;
using Google.Apis.HomeGraphService.v1.Data;
using Google.Apis.Services;
using Lambda.Core.Constants;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using static Google.Apis.HomeGraphService.v1.DevicesResource;
public class Example
{
public void RequestSync()
{
const string UrlWithoutKey = @"https://homegraph.googleapis.com/v1/devices:requestSync";
const string UrlWithKey = @"https://homegraph.googleapis.com/v1/devices:requestSync?key=" + OAuthConstants.GoogleApiKey;
string accessToken = this.GetAccessToken();
// Manual Attempt 1
try
{
string response = this.CallRequestSyncApiManually(accessToken, UrlWithoutKey);
}
catch (WebException ex)
{
// Receive 403, Forbidden
string msg = ex.Message;
}
// Manual Attempt 2
try
{
string response = this.CallRequestSyncApiManually(accessToken, UrlWithKey);
}
catch (WebException ex)
{
// Receive 403, Forbidden
string msg = ex.Message;
}
// SDK Attempt
try
{
this.CallRequestSyncApiWithSdk();
}
catch (GoogleApiException ex)
{
// Google.Apis.Requests.RequestError
// The caller does not have permission[403]
// Errors[Message[The caller does not have permission] Location[- ] Reason[forbidden] Domain[global]]
// at Google.Apis.Requests.ClientServiceRequest`1.ParseResponse(HttpResponseMessage response) in Src\Support\Google.Apis\Requests\ClientServiceRequest.cs:line 243
// at Google.Apis.Requests.ClientServiceRequest`1.Execute() in Src\Support\Google.Apis\Requests\ClientServiceRequest.cs:line 167
string msg = ex.Message;
}
}
private string GetAccessToken()
{
string defaultScope = "https://www.googleapis.com/auth/homegraph";
string serviceAccount = OAuthConstants.GoogleServiceAccountEmail; // "??????@??????.iam.gserviceaccount.com"
string certificateFile = OAuthConstants.CertificateFileName; // "??????.p12"
var oAuth2 = new GoogleOAuth2(defaultScope, serviceAccount, certificateFile); // As per https://stackoverflow.com/questions/26478694/how-to-produce-jwt-with-google-oauth2-compatible-algorithm-rsa-sha-256-using-sys
bool status = oAuth2.RequestAccessTokenAsync().Result;
// This access token at a glance appears invalid due to an empty header and payload,
// But verifies ok when tested here: https://accounts.google.com/o/oauth2/tokeninfo?access_token=
return oAuth2.AccessToken;
}
private string CallRequestSyncApiManually(string accessToken, string url)
{
string apiRequestBody = @"{""agentUserId"": """ + OAuthConstants.TestAgentUserId + @"""}";
var client = new HttpClient();
var request = (HttpWebRequest)WebRequest.Create(url);
var data = Encoding.ASCII.GetBytes(apiRequestBody);
request.Method = "POST";
request.Accept = "application/json";
request.ContentType = "application/json";
request.ContentLength = data.Length;
request.Headers.Add("Authorization", $"Bearer {accessToken}");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
var response = (HttpWebResponse)request.GetResponse();
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
return responseString;
}
private void CallRequestSyncApiWithSdk()
{
var certificate = new X509Certificate2(OAuthConstants.CertificateFileName, OAuthConstants.CertSecret, X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(OAuthConstants.GoogleServiceAccountEmail)
{
Scopes = new[] { "https://www.googleapis.com/auth/homegraph" },
}.FromCertificate(certificate));
var service = new HomeGraphServiceService(
new BaseClientService.Initializer()
{
// Complains if API key is not provided, even though we're using a certificate from a Service Account
ApiKey = OAuthConstants.GoogleApiKey,
HttpClientInitializer = credential,
ApplicationName = OAuthConstants.ApplicationName,
});
var request = new RequestSyncRequest(
service,
new RequestSyncDevicesRequest
{
AgentUserId = OAuthConstants.TestAgentUserId
});
request.Execute();
}
}
Конфигурация учетной записи
Скриншоты учетной записи. (Мне пока не разрешено публиковать изображения, поэтому они являются ссылками)
HomeGraph включен
Мой ключ API неограничен
В моей учетной записи службы включен создатель токенов владельца и учетной записи службы
Обновления
Я попытался пропустить получение маркера доступа вручную, согласно предложению Devunwired. Несмотря на то, что это устраняет ошибку, которую я получал, когда не предоставлял ключ API, я все равно получаю 403. Моя причина для выполнения части маркера доступа вручную была частью отладки 403, которую я получал с помощью вызова API. Таким образом, я мог, по крайней мере, увидеть, как часть процесса работает. Я счастлив использовать библиотечную версию для решения, так как токен доступа не является проблемой.
public void GoogleLibraryJsonCredentialExample()
{
try
{
GoogleCredential credential;
using (var stream = new FileStream(OAuthConstants.JsonCredentialsFileName, FileMode.Open, FileAccess.Read))
{
credential = GoogleCredential.FromStream(stream).CreateScoped(new[] { OAuthConstants.GoogleScope });
}
var service = new HomeGraphServiceService(
new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = OAuthConstants.ApplicationName,
});
var request = new RequestSyncRequest(
service,
new RequestSyncDevicesRequest
{
AgentUserId = OAuthConstants.TestAgentUserId
});
request.Execute();
}
catch (Exception ex)
{
// Receive 403, Forbidden
string msg = ex.Message;
}
}
Проблемы
Возможно ли, что мне нужно сделать вызов API из проверенного домена или домена из белого списка? В данный момент я запускаю его из консольного приложения, запущенного на моей машине для разработки. Насколько я понимаю, проверка домена не относится к входящим вызовам и поэтому не должна быть проблемой.