Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Azure Function to Function authentication with MSI supported

I created 2 Azure Function Apps, both setup with Authentication/Authorization so an AD App was created for both. I would like to setup AD Auth from one Function to the other using MSI. I setup the client Function with Managed Service Identity using an ARM template. I created a simple test function to get the access token and it returns: Microsoft.Azure.Services.AppAuthentication: Token response is not in the expected format.

try {
    var azureServiceTokenProvider = new AzureServiceTokenProvider();
    string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://myapp-registration-westus-dev.azurewebsites.net/");
    log.Info($"Access Token: {accessToken}");
    return req.CreateResponse(new {token = accessToken});
}
catch(Exception ex) {
    log.Error("Error", ex);
    throw;
}
like image 545
Brian Aabel Avatar asked Oct 04 '17 14:10

Brian Aabel


2 Answers

Yes, there is a way to do this. I'll explain at a high level, and then add an item to the MSI documentation backlog to write a proper tutorial for this.

What you want to do is follow this Azure AD authentication sample, but only configure and implement the parts for the TodoListService: https://github.com/Azure-Samples/active-directory-dotnet-daemon.

The role of the TodoListDaemon will be played by a Managed Service Identity instead. So you don't need to register the TodoListDaemon app in Azure AD as instructed in the readme. Just enable MSI on your VM/App Service/Function.

In your code client side code, when you make the call to MSI (on a VM or in a Function or App Service), supply the TodoListService's AppID URI as the resource parameter. MSI will fetch a token for that audience for you.

The code in the TodoListService example will show you how to validate that token when you receive it.

So essentially, what you want to do is register an App in Azure AD, give it an AppID URI, and use that AppID URI as the resource parameter when you make the call to MSI. Then validate the token you receive at your service/receiving side.

like image 149
skwan Avatar answered Oct 01 '22 17:10

skwan


Please check that the resource id used "https://myapp-registration-westus-dev.azurewebsites.net/" is accurate. I followed steps here to setup Azure AD authentication, and used the same code as you, and was able to get a token. https://docs.microsoft.com/en-us/azure/app-service/app-service-mobile-how-to-configure-active-directory-authentication

You can also run this code to check the exact error returned by MSI. Do post the error if it does not help resolve the issue.

HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Secret", Environment.GetEnvironmentVariable("MSI_SECRET"));
var response = await client.GetAsync(String.Format("{0}/?resource={1}&api-version={2}", Environment.GetEnvironmentVariable("MSI_ENDPOINT"), "https://myapp-registration-westus-dev.azurewebsites.net/", "2017-09-01"));
string msiResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
log.Info($"MSI Response: {msiResponse}");

Update:- This project.json file and run.csx file work for me. Note: The project.json refers to .NET 4.6, and as per Azure Functions documentation (link in comments), .NET 4.6 is the only supported version as of now. You do not need to upload the referenced assembly again. Most probably, incorrect manual upload of netstandard assembly, instead of net452 is causing your issue.

Only the .NET Framework 4.6 is supported, so make sure that your project.json file specifies net46 as shown here. When you upload a project.json file, the runtime gets the packages and automatically adds references to the package assemblies. You don't need to add #r "AssemblyName" directives. To use the types defined in the NuGet packages, add the required using statements to your run.csx file.

project.json

{
  "frameworks": {
    "net46":{
      "dependencies": {
        "Microsoft.Azure.Services.AppAuthentication": "1.0.0-preview"
      }
    }
   }
}

run.csx

using Microsoft.Azure.Services.AppAuthentication;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    try
    {
        var azureServiceTokenProvider = new AzureServiceTokenProvider();
        string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://vault.azure.net/");
        log.Info($"Access Token: {accessToken}");
        return req.CreateResponse(new {token = accessToken});
    }
    catch(Exception ex) 
    {
        log.Error("Error", ex);
        throw;
    }    
}
like image 43
Varun Sharma Avatar answered Oct 01 '22 18:10

Varun Sharma