Как отправить запрос на получение доступных дисков в graph api используя токен приложения
У меня есть web api .net core приложение. Которое принимает запросы от клиента и дальше стучится в graph api. Когда я пытаюсь получить список дисков в graph api используя токен, полученный после авторизации пользователя, все работает хорошо. Пример:
var drives = await _graphServiceClient.Me.Drives.Request().GetAsync();
Когда я пытаюсь получить тоже самое, используя неявную авторизацию (токен приложения), получаю ошибку от graph api: Microsoft.Graph.ServiceException: Code: generalException Message: An error occurred sending the request. Пример:
var drives = await _graphServiceClient.Drives.Request().GetAsync();
При этом, попробовал аналогичный запрос в Online REST & SOAP API Testing Tool, все работает:

UPD:
Вот мой конфиг файл:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "company.eu",
"TenantId": "******-7906-447b-****-d7fdcd7a90d8",
"ClientId": "******-7a40-4ba7-****-f3546c0ba1f9",
"ClientSecret": "**********.V.A0gm-yU0D7xHQRg"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"MicrosoftGraph": {
"Scopes": "user.read",
"BaseUrl": "https://graph.microsoft.com/v1.0"
}
}
Здесь конфигурация в файле Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph(Configuration.GetSection("MicrosoftGraph"))
.AddInMemoryTokenCaches();
services.AddControllers();
}
Есть два роута:
//действия в graph api c токеном юзера(после авторизации)
[Authorize]
[HttpPost]
[DisableRequestSizeLimit]
[Route("uploadasuser")]
public async Task<ActionResult> PostUploadFileAsUser()
{
var drives = await _graphServiceClient.Me.Drives.Request().GetAsync();
}
//действия в graph api c токеном приложения(неявная авторизация)
[HttpPost]
[DisableRequestSizeLimit]
[Route("uploadasapplication")]
public async Task<ActionResult> PostUploadFileAsApplication()
{
var drives = await _graphServiceClient.Drives.Request().GetAsync();
}
Если добавить в оба роута
Request.Headers.TryGetValue("Authorization", out var token);
Вижу что токены прилетают вместе с запросом. Токены точно валидные. Однако первый роут где я обращаюсь с токеном пользователя, отрабатывает как надо. А во втором, где я обращаюсь с токеном приложения, я получаю ошибку, полный стек ошибки:
Microsoft.Graph.ServiceException: Code: generalException
Message: An error occurred sending the request.
---> System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.Identity.Web.MergedOptions.PrepareAuthorityInstanceForMsal()
at Microsoft.Identity.Web.TokenAcquisition.BuildConfidentialClientApplication(MergedOptions mergedOptions)
at Microsoft.Identity.Web.TokenAcquisition.GetOrBuildConfidentialClientApplication(MergedOptions mergedOptions)
at Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForUserAsync(IEnumerable`1 scopes, String authenticationScheme, String tenantId, String userFlow, ClaimsPrincipal user, TokenAcquisitionOptions tokenAcquisitionOptions)
at Microsoft.Identity.Web.TokenAcquisition.GetAccessTokenForUserAsync(IEnumerable`1 scopes, String authenticationScheme, String tenantId, String userFlow, ClaimsPrincipal user, TokenAcquisitionOptions tokenAcquisitionOptions)
at Microsoft.Identity.Web.TokenAcquisitionAuthenticationProvider.AuthenticateRequestAsync(HttpRequestMessage request)
at Microsoft.Graph.AuthenticationHandler.SendAsync(HttpRequestMessage httpRequestMessage, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.SendAsyncCore(HttpRequestMessage request, HttpCompletionOption completionOption, Boolean async, Boolean emitTelemetryStartStop, CancellationToken cancellationToken)
at Microsoft.Graph.HttpProvider.SendRequestAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at Microsoft.Graph.HttpProvider.SendRequestAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
at Microsoft.Graph.HttpProvider.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
at Microsoft.Graph.BaseRequest.SendRequestAsync(Object serializableObject, CancellationToken cancellationToken, HttpCompletionOption completionOption)
at Microsoft.Graph.BaseRequest.SendAsync[T](Object serializableObject, CancellationToken cancellationToken, HttpCompletionOption completionOption)
at Microsoft.Graph.GraphServiceDrivesCollectionRequest.GetAsync(CancellationToken cancellationToken)
at MediaGun.Controllers.Api.FilesController.UploadFileToCloud(MemoryStream proccesedPresentation, String fileName, Boolean uploadAsUser) in E:\MediaGun\Controllers\Api\FilesController.cs:line 369
at MediaGun.Controllers.Api.FilesController.PostUploadFileAsApplication() in E:\MediaGun\Controllers\Api\FilesController.cs:line 131
at lambda_method80(Closure , Object )
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
Скорее всего что-то с конфигурацией, но я не могу понять что именно.
Ответы (1 шт):
Для неявной авторизации (используя токен приложения) создал свой GraphServiceClient.
Добавил метод расширения:
public static void AddAzureGraphApiClientConfig(this IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton(x =>
{
var microsoftAzureAdConstant = configuration.GetSection("AzureAD");
var scopes = new[] { microsoftAzureAdConstant["Scope"] };
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
var clientSecretCredential = new ClientSecretCredential(
microsoftAzureAdConstant["TenantId"],
microsoftAzureAdConstant["ClientId"],
microsoftAzureAdConstant["ClientSecret"], options);
return new GraphServiceClient(clientSecretCredential, scopes);
});
}
Далее, добавляем сервис в ConfigureServices и можем использовать его в любой части приложения.