Thursday, December 27, 2018

Call Azure AD secured Azure functions from C#

In this post I will show how to call Azure functions which are secured with AAD from C#. Let’s assume that you already created Function app and secured it with AAD. In this example I will use Advanced AAD authentication configuration of the Function app which may be used e.g. when Function app is created in one tenant but it’s Azure functions supposed to be used in another tenant (and thus should be secured against this other tenant):

image

Briefly in order to secure Azure function with AAD we need to register new app in the directory against which we want to authenticate users which will use Azure function (Azure Active Directory > App registrations > New app registration); in this example let’s call it test-secured-functions for reference. Then in authentication settings of the Function app: in Client ID specify app id of test-secured-functions, Client Secret – client secret of test-secured-functions (can be generated from app’s Settings > Keys) and in Issuer Url – url of the form https://sts.windows.net/{tenantId} where tenantId is id of the tenant where test-secured-functions is registered (can be found in Azure Active Directory > Properties > Directory ID).

After that copy base url of any Azure function (e.g. https://{azurefuncname}.azurewebsites.net – those which comes before /api/… part) in the Function app and add it to Allowed token audiences. Otherwise you will get HTTP 401 Unauthorized when will try to get access token for calling Azure function from C#.

Now the tricky part; go to test-secured-functions > Settings > Properties and change “Home page URL” and “App ID URI” properties to the base url of Azure function – same which was added to Allowed token audiences on previous step (https://{azurefuncname}.azurewebsites.net). It is more important to set “Home page URL”  - it will be used as app id when we will get access token:

image

Now we may call our AAD secured Azure function from C#:

string aadInstance = "https://login.windows.net/{0}";
string tenant = "{tenant}.onmicrosoft.com";
string serviceResourceId = "https://{azurefuncname}.azurewebsites.net";
string clientId = "{clientId}";
string appKey = "{clientSecret}";

var authContext = new AuthenticationContext(string.Format(CultureInfo.InvariantCulture, aadInstance, tenant));
var clientCredential = new ClientCredential(clientId, appKey);
AuthenticationResult result = authContext.AcquireTokenAsync(serviceResourceId, clientCredential).Result;
Console.WriteLine(result.AccessToken);

Here you need to replace {tenant} with your tenant name, {azurefuncname} with name of your Azure function, {clientId} and {clientSecret} are from test-secured-functions app – same which were used in Fucntion app > Active Directory Authentication above. Notice that serviceResourceId variable contains base url of our Azure functions. I.e. we ask access token for scope = url of our Azure function. If we won’t set this property we will get the following error when try to call above code:

AADSTS50001: The application named https://{azurefuncname}.azurewebsites.net was not found in the tenant named {tenant}.onmicrosoft.com.  This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant.  You might have sent your authentication request to the wrong tenant

If we will decode token (e.g. on https://jwt.io/) we will see that it’s aud property is set exactly to requested serviceResourceId, i.e. base url of Azure function. This is why we set this base url to the app’s “Home page url” above – API searched for the app by this url during the call of AuthenticationContext.AcquireTokenAsync() method:

image

Once token is obtained we may call Azure function using regular way by providing Authorization HTTP header with Bearer token:

var request = WebRequest.Create("https://{azurefuncname}.azurewebsites.net/api/test") as HttpWebRequest;
request.Method = "POST";
request.ContentType = "application/json";
request.Headers.Add("Authorization", "Bearer" + " " + accessToken);

string data = ...;
var byteData = Encoding.UTF8.GetBytes(data);
request.ContentLength = byteData.Length;
var stream = request.GetRequestStream();
stream.Write(byteData, 0, byteData.Length);

using (var response = request.GetResponse() as HttpWebResponse)
{
    ...
}

Using this method you will be able to call your AAD secured Azure functions from C# or PowerShell. Hope this information will help someone.

No comments:

Post a Comment