Hello World!
Привет всем!Я новичок в StackOverflow, поэтому я приложу все усилия, чтобы лучше объяснить мою проблему.
Проблема
Я пытаюсь взаимодействовать с Outlook Calendar и Tasks REST API из ac #.NET Framework Cli Program, которая станет фоновой службой Windows.
Я создал выделенную учетную запись Outlook, зарегистрировал приложение на портале Azure и адаптировал rest-sender проект.
С помощью PublicClientApplicationBuilder.AcquireTokenInteractive
я могу получить действительный, хотя и временный (1 час), токен доступа и список моих задач.Он работает и для других вызовов API, но проблема в том, что каждый раз, когда мне требуется токен, он показывает и интерактивное окно веб-браузера для получения учетной записи.Приложение станет фоновой службой Windows на производственном головном сервере, поэтому интерактивное окно невозможно.
Я обнаружил ConfidentialClientApplication.AcquireTokenForClient
, для которого требуется секрет клиента.Я создал один из портала Azure.Я могу получить токен, но кажется, что он не авторизован.
Портал
приложение Это экран приложения на странице Azure.
auth Это настройки аутентификации.
auths Это настройки авторизации.
secret Это секретная страница.
почта Из почты я вижу эти разрешения (предоставленные с GetInteractiveToken
)
токен Этоэто декодированный токен.
Это иллюстративный код:
static async Task<string> GetSecretAccessToken()
{
var client =
ConfidentialClientApplicationBuilder
.Create("<applicationId>")
.WithClientSecret("<clientSecret>")
.Build();
var result = client.AcquireTokenForClient(new []{
"https://outlook.office.com/.default",
}).ExecuteAsync();
var r = result.Result.AccessToken;
return r;
}
static async Task<string> GetInteractiveAccessToken()
{
var client =
PublicClientApplicationBuilder
.Create("<applicationId>")
.Build();
var result = client.AcquireTokenInteractive(new []{
"https://outlook.office.com/Tasks.ReadWrite"
}).ExecuteAsync();
var r = result.Result.AccessToken;
return r;
}
static async Task<string> GetAccessFromPasswordToken(string[] scopes){
try
{
var b =
PublicClientApplicationBuilder.Create(ConfigurationManager.AppSettings.Get("applicationId"))
.Build();
var result = b.AcquireTokenByUsernamePassword(scopes, "CodeGen.Preventizzatore@Outlook.it", GetSecureString()).ExecuteAsync();
return result.Result.AccessToken;
}
catch (MsalException ex)
{
Output.WriteLine(Output.Error, "Could not acquire access token: Error code: {0}, Error message: ",
ex.ErrorCode, ex.Message);
return string.Empty;
}
}
static async Task ListTasks()
{
string token = await GetAccessToken();
var request = new HttpRequestMessage(
new HttpMethod("GET"),
new UriBuilder("https://outlook.office.com/api/v2.0/users/CodeGen.Preventizzatore@Outlook.it/taskfolders").Uri
);
request.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
request.Headers.UserAgent.Add(
new System.Net.Http.Headers.ProductInfoHeaderValue("rest-sender", "1.0"));
request.Headers.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var result = await new HttpClient().SendAsync(request);
if (result.StatusCode == HttpStatusCode.Unauthorized)
return; // when GetSecretAccessToken
// when GetInteractiveAccessToken
string response = await result.Content.ReadAsStringAsync();
}
И это манифест на Azure:
{
"id": "a752666c-11a9-425e-a382-c63ebcbc8bb2",
"acceptMappedClaims": null,
"accessTokenAcceptedVersion": 2,
"addIns": [],
"allowPublicClient": null,
"appId": "608bd039-6bc9-4a7e-ad86-2b6a3e20584b",
"appRoles": [],
"oauth2AllowUrlPathMatching": false,
"createdDateTime": "2019-06-21T08:18:56Z",
"groupMembershipClaims": null,
"identifierUris": [
"api://608bd039-6bc9-4a7e-ad86-2b6a3e20584b"
],
"informationalUrls": {
"termsOfService": null,
"support": null,
"privacy": null,
"marketing": null
},
"keyCredentials": [],
"knownClientApplications": [],
"logoUrl": null,
"logoutUrl": null,
"name": "CodeGen.Preventizzatore",
"oauth2AllowIdTokenImplicitFlow": false,
"oauth2AllowImplicitFlow": true,
"oauth2Permissions": [],
"oauth2RequirePostResponse": false,
"optionalClaims": null,
"orgRestrictions": [],
"parentalControlSettings": {
"countriesBlockedForMinors": [],
"legalAgeGroupRule": "Allow"
},
"passwordCredentials": [
{
"customKeyIdentifier": null,
"endDate": "2299-12-30T23:00:00Z",
"keyId": "c4a2fe3a-a09c-41ff-9115-40a2a2ef1a89",
"startDate": "2019-06-21T11:46:06.306Z",
"value": null,
"createdOn": "2019-06-21T11:46:08.2116173Z",
"hint": "nR?",
"displayName": "Preventizzatore"
}
],
"preAuthorizedApplications": [],
"publisherDomain": null,
"replyUrlsWithType": [
{
"url": "urn:ietf:wg:oauth:2.0:oob",
"type": "InstalledClient"
}
],
"requiredResourceAccess": [
{
"resourceAppId": "00000002-0000-0ff1-ce00-000000000000",
"resourceAccess": [
{
"id": "bbd1ca91-75e0-4814-ad94-9c5dbbae3415",
"type": "Scope"
},
{
"id": "6b49b74d-642f-4417-a6b4-820576845707",
"type": "Scope"
},
{
"id": "bf24470f-10c1-436d-8d53-7b997eb473be",
"type": "Role"
},
{
"id": "77e65b5a-ceae-48b3-9490-50a86a038a48",
"type": "Role"
},
{
"id": "dc890d15-9560-4a4c-9b7f-a736ec74ec40",
"type": "Role"
},
{
"id": "798ee544-9d2d-430c-a058-570e29e34338",
"type": "Role"
},
{
"id": "c1b0de0a-1de9-455d-919f-eca451053141",
"type": "Role"
},
{
"id": "2c6a42ca-0d4d-49ad-bc0e-21222c449a65",
"type": "Role"
},
{
"id": "ef54d2bf-783f-4e0f-bca1-3210c0444d99",
"type": "Role"
},
{
"id": "2dfdc6dc-2fa7-4a2c-a922-dbd4f85d17be",
"type": "Role"
}
]
}
],
"samlMetadataUrl": null,
"signInUrl": null,
"signInAudience": "AzureADandPersonalMicrosoftAccount",
"tags": [],
"tokenEncryptionKeyId": null
}
Когдаиспользуя неинтерактивный токен, я получаю:
{StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Transfer-Encoding: chunked
Cache-Control: private
WWW-Authenticate: Bearer client_id="00000002-0000-0ff1-ce00-000000000000", trusted_issuers="00000001-0000-0000-c000-000000000000@*", token_types="app_asserted_user_v1 service_asserted_app_v1", authorization_uri="https://login.windows.net/common/oauth2/authorize", error="invalid_token"
WWW-Authenticate: Basic Realm=""
request-id: 8a2ccebc-e8c3-4ec2-b8cb-64d1005a5bf7
X-CalculatedBETarget: MR2P264MB0817.FRAP264.PROD.OUTLOOK.COM
X-BackEndHttpStatus: 401
X-RUM-Validated: 1
x-ms-diagnostics: 2000008;reason="The token contains no permissions, or permissions can not be understood.";error_category="invalid_grant"
X-AspNet-Version: 4.0.30319
X-BeSku: WCS5
X-DiagInfo: MR2P264MB0817
X-BEServer: MR2P264MB0817
X-Powered-By: ASP.NET
X-FEServer: MRXP264CA0011
X-MSEdge-Ref: Ref A: 25DDA6AB2E72497199D611B555DACB60 Ref B: MIL30EDGE0414 Ref C: 2019-06-24T06:10:44Z
Date: Mon, 24 Jun 2019 06:10:43 GMT
Content-Type: text/html; charset=utf-8
}}
Оба GetSecretAccessToken
и GetInteractiveAccessToken
предоставляют токен, но только интерактивный работает в методе ListTasks
.Другой нужен для работы с сервисом Win.
Я прочитал, что https://outlook.office.com/.default
заставляет приложение использовать статические разрешения приложения, которые я установил на портале Azure.Но я не могу заставить этот метод работать.
Я просто не знаю, какое разрешение требуется для работы в этом режиме.
Я почти неделю пытаюсьзаставить это работать, без успеха.Может быть, я упускаю что-то тривиальное ... Поэтому я ищу помощь в лучшем месте на планете.
Я также нашел пример в ядре .net, который регистрирует код устройства, но я предпочитаю остатьсяс базовой платформой .net, потому что у меня уже есть работающий проект в моей клиентской системе.
Я надеюсь, что прояснил свою проблему, и это возможно.Было бы очень хорошо иметь интеграцию с Outlook.
Заранее спасибо .
=============
Tl; Dr;
Платформа идентификации Microsoft не поддерживает личные учетные записи, поэтому, если вы хотите получить доступ к API REST outlook365.com из фоновой службы / демона, вам нужно быть под Azure AD.и должен быть одобрен администратором (он нажмет кнопку «Предоставить согласие администратора для [себя]»).В противном случае вы не сможете выполнить какой-либо запрос, поскольку ваш неинтерактивный токен не будет содержать никаких разрешений на область действия.
Спасибо Джейсону Джонстону за большую помощь!