Open electrocnic opened 2 months ago
I found a working solution but only tested it with the mgt-react person component and the two scopes "User.Read profile". The major thing was, that Kiota is not really suited for such Proxy implementations, as it seems, according to its docs: https://learn.microsoft.com/en-us/openapi/kiota/abstractions#request-adapter
This interface is meant to support the generated code and not to be used by application developers.
Therefore, implementing the proxy with System.Net.Http was the alternative, but I now had to take care of the correct auth header manually, which was done automatically before with the old v4 graphServiceClient:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Net.Http.Headers;
using Microsoft.Extensions.Primitives;
using Microsoft.Graph;
using Microsoft.Identity.Web;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
namespace webapi.Controllers.api.v1
{
[Authorize]
[Route("[controller]")]
[ApiController]
public class GraphProxyController : ControllerBase
{
private readonly HttpClient _httpClient;
private readonly ITokenAcquisition _tokenAcquisition;
public GraphProxyController(IHttpClientFactory httpClientFactory, GraphServiceClient graphServiceClient, ITokenAcquisition tokenAcquisition)
{
_httpClient = httpClientFactory.CreateClient();
_httpClient.BaseAddress = new Uri(GetBaseUrlWithoutVersion(graphServiceClient));
_tokenAcquisition = tokenAcquisition;
}
[HttpGet("{*all}")]
public async Task<IActionResult> GetAsync(string all)
{
return await ProcessRequestAsync(HttpMethod.Get, all, null).ConfigureAwait(false);
}
[HttpPost("{*all}")]
public async Task<IActionResult> PostAsync(string all, [FromBody] object body)
{
return await ProcessRequestAsync(HttpMethod.Post, all, body).ConfigureAwait(false);
}
[HttpDelete("{*all}")]
public async Task<IActionResult> DeleteAsync(string all)
{
return await ProcessRequestAsync(HttpMethod.Delete, all, null).ConfigureAwait(false);
}
[HttpPut("{*all}")]
public async Task<IActionResult> PutAsync(string all, [FromBody] object body)
{
return await ProcessRequestAsync(HttpMethod.Put, all, body).ConfigureAwait(false);
}
[HttpPatch("{*all}")]
public async Task<IActionResult> PatchAsync(string all, [FromBody] object body)
{
return await ProcessRequestAsync(HttpMethod.Patch, all, body).ConfigureAwait(false);
}
private async Task<IActionResult> ProcessRequestAsync(HttpMethod method, string all, object content)
{
// Construct the full Graph API request URL with query string
var requestUri = $"{all}{Request.QueryString.ToUriComponent()}";
var requestMessage = new HttpRequestMessage(method, requestUri);
var scopes = new[] { "User.Read", "profile" };
var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var headersToForward = new[] { "If-Match", "ConsistencyLevel" };
foreach (var headerKey in headersToForward)
{
if (Request.Headers.TryGetValue(headerKey, out StringValues headerValues))
{
requestMessage.Headers.TryAddWithoutValidation(headerKey, headerValues.ToArray());
}
}
if (content != null)
{
if (content is JObject jsonObject)
{
// Use Newtonsoft.Json to serialize JObject
var jsonContent = JsonConvert.SerializeObject(jsonObject);
requestMessage.Content = new StringContent(jsonContent, System.Text.Encoding.UTF8, "application/json");
}
else
{
// For other content types, use System.Text.Json
var jsonContent = System.Text.Json.JsonSerializer.Serialize(content);
requestMessage.Content = new StringContent(jsonContent, System.Text.Encoding.UTF8, "application/json");
}
}
var response = await _httpClient.SendAsync(requestMessage);
var responseBody = await response.Content.ReadAsStringAsync();
var contentType = response.Content.Headers.ContentType?.ToString() ?? "application/json";
return new ContentResult
{
Content = responseBody,
ContentType = contentType,
StatusCode = (int)response.StatusCode
};
}
private string GetBaseUrlWithoutVersion(GraphServiceClient graphClient)
{
var baseUrl = graphClient.RequestAdapter.BaseUrl;
var index = baseUrl.LastIndexOf('/');
return baseUrl.Substring(0, index);
}
}
}
@mnickii @andrueastman can you help with answering with the questions? It's very possible that Graph v5 is a little bit less suited for this scenario but I feel it's an interesting one. Would love your perspective. Thanks!
Which components would you like this sample to be about?
Sample description
I am struggling to migrate the previous proxy provider from https://github.com/pnp/mgt-samples/tree/main/samples/app/proxy-provider-asp-net-core to the new Graph SDK with Kiota versions. The versions I wanted to migrate to are:
I tried something like this, but the response is always null or I even get into the exception block with no helpful stacktrace in the case of $batch requests:
Are you willing to help?
Yes