Trouble Implementing Federated Credentials with Managed Identities in Azure Function App API Authentication
I have an API inside my function app and have been accessing the API using the secrets created in the app registration associated with the function app. Now we're looking to eliminate the secrets and modify it to use managed idenities as the client app accessing will also be from ms azure. So I came across the federated credentials and thought to use this. I created a client function app and a managed identity associated with this function app. For the API function app's app registration I created a federated credential linking it to the managed identity of my client's function app. Now to test it, using .net from the client app, I am trying to get a token, and add it to the request header and call the API. But the token I get is incorrect and gives me a 401 when I try to access the API. I am not sure if I am missing something in my code, but it would be great if anyone can help me.
I am guessing the URL that am using to get the token is incorrect, but not sure. I tried with multiple other URLs as well but still getting a 401. If you can think of any other alternate ways to replace the secrets by managed identities please me know.
Here's the code -
using Azure.Core;
using Azure.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using Microsoft.Identity.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace EB_test_project
{
public class ClientFunctionCall
{
private readonly ILogger<Function1> _logger;
private readonly HttpClient _httpClient;
public ClientFunctionCall(ILogger<Function1> logger)
{
_logger = logger;
}
public async Task<string> GetAccessTokenAsync(string clientId)
{
string userAssignedClientId = clientId;
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = userAssignedClientId });
var accessTokenRequestContext = new TokenRequestContext(new[] { "api://AzureADTokenExchange" });
var accessToken = credential.GetToken(accessTokenRequestContext);
return accessToken.Token;
}
[Function("ClientFunction")]
public async Task<IActionResult> RunAsync([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
HttpClient _httpClient = new HttpClient();
var msiClientId = "{client id of my managed identity}";
var apiUrl = "https://{apiurl}";
try
{
var token = await GetAccessTokenAsync(msiClientId);
if (String.IsNullOrEmpty(token))
_logger.LogInformation("Token is empty");
else
{
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, apiUrl);
HttpResponseMessage response = await _httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
_logger.LogInformation("Api response " + await response.Content.ReadAsStringAsync());
}
else
{
_logger.LogInformation("Request failed " + response.StatusCode);
string errorResponse = await response.Content.ReadAsStringAsync();
_logger.LogInformation("Error Response: " + errorResponse);
}
}
return new OkObjectResult("end");
}
catch (MsalServiceException ex)
{
_logger.LogError($"Authentication failed: {ex.Message}");
return new BadRequestResult();
}
catch (HttpRequestException ex)
{
_logger.LogError($"HTTP request failed: {ex.Message}");
return new BadRequestResult();
}
}
}
}