MiddleTommy
MiddleTommy

Reputation: 389

WPF app Accessing Azure Function with AAD

I have created a sample function in Azure Functions. I have secured it using AAD. I can successfully go to the URL, login with my Office 365 account and the function runs.

I created another app in AAD for my WPF client. I can successfully login to the WPF client with my office 365 credentials. I then created a permision for this app in AAD to access the Azure Function AAD app.

Here is my problem: How do I use the login token from my WPF app to access the Azure Functions?

I have gone through about a dozen different tutorials showing how to set this up and each one fails to actually authenticate the call to the Function.

What am I missing?

Upvotes: 3

Views: 1273

Answers (1)

MiddleTommy
MiddleTommy

Reputation: 389

After much research and trial and error I finally was able to login with my Office365 account and access the Azure Function.

Step 1 - Register your App Service app with Azure Active Directory

  1. Log on to the Azure portal, and navigate to your App Service app. Copy your app URL to your Azure Function. You will use this to configure your Azure Active Directory app registration.
  2. Navigate to Active Directory, then select the App registrations, then click New application registration at the top to start a new app registration.
  3. In the Create page, enter a Name for your app registration, select the Web App / API type, in the Sign-on URL box paste the application URL (from step 1). Then click to Create.
  4. In a few seconds, you should see the new app registration you just created.
  5. Once the app registration has been added, click on the app registration name, click on Settings at the top, then click on Properties
  6. In the App ID URI box, paste in the Application URL (from step 1), also in the Home Page URL paste in the Application URL (from step 1) as well, then click Save
  7. Now click on the Reply URLs, edit the Reply URL, paste in the Application URL (from step 1), modify the protocol to make sure you have https:// protocol (not http://), then appended to the end of the URL, /.auth/login/aad/callback (For example, https://contoso.azurewebsites.net/.auth/login/aad/callback). Click Save.
  8. At this point, copy the Application ID for the app. Keep it for later use. You will need it to configure your App Service app.
  9. Close the Registered app page. On the App registrations page, click on the Endpoints button at the top, then copy the Federation Metadata Document URL.
  10. Open a new browser window and navigate to the URL by pasting and browsing to the XML page. At the top of document is an EntityDescriptor element. Find the entityID attribute and copy its value. It serves as your Issuer URL. You will configure your application to use it later.

Step 2 - Add Azure Active Directory information to your App Service app

  1. Back in the Azure portal, navigate to your App Service app. Click Authentication/Authorization. If the Authentication/Authorization feature is not enabled, turn the switch to On. Click on Azure Active Directory, under Authentication Providers, to configure your app. (Optional) By default, App Service provides authentication but does not restrict authorized access to your site content and APIs. You must authorize users in your app code. Set Action to take when request is not authenticated to Log in with Azure Active Directory. This option requires that all requests be authenticated, and all unauthenticated requests are redirected to Azure Active Directory for authentication.
  2. In the Active Directory Authentication configuration, click Advanced under Management Mode. Paste the Application ID into the Client ID box (from step 8) Then click OK.
  3. On the Active Directory Authentication configuration page, click Save.

Step 3 - Configure a native client application

Azure Active Directory also allows you to register native clients, which provides greater control over permissions mapping. You need this if you wish to perform logins using a library such as the Active Directory Authentication Library.

  1. Navigate to Azure Active Directory in the Azure portal.
  2. In the left navigation, select App registrations. Click New app registration at the top.
  3. In the Create page, enter a Name for your app registration. Select Native in Application type.
  4. In the Redirect URI box, enter your site's /.auth/login/done endpoint, using the HTTPS scheme. This value should be similar to https://contoso.azurewebsites.net/.auth/login/done. If creating a Windows application, instead use the package SID as the URI.
  5. Click Create.
  6. Once the app registration has been added, select it to open it. Find the Application ID and make a note of this value.
  7. Click All settings > Required permissions > Add > Select an API.
  8. Type the name of the App Service app that you registered earlier to search for it, then select it and click Select.
  9. Select Access . Then click Select. Then click Done.

The above directions were taken from https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad?toc=%2fazure%2fazure-functions%2ftoc.json

Below are my alterations to get it to work with a WPF app calling an Azure Function

Step 4 - Authorize Client App in Service App

  1. Copy the Client App ID from Step 3:
  2. Open Azure Active Directory
  3. Click on App registrations (preview)
  4. Click on the Service App registered in Step 1
  5. Click on Expose an API
  6. Under Authorized Client Applications click Add a client application.
  7. Paste the Client ID and Click on the Scope https://{your tenant name}.onmicrosoft.com/{Your Service App ID}/user_impersonation
  8. Click Add Application

Step 5 - Make Service App Multi Tenant

  1. Open Azure Active Directory
  2. Click on App registrations (preview)
  3. Click on the Service App registered in Step 1
  4. Click on Authentication
  5. Under Supported account types select "Accounts in any organizational directory"
  6. You will receive an error. This is because of Step 1 sub step 6. We need to create an App URI based on the tenant domain {tenantname}.onmicrosoft.com
  7. Navigate to the old App registrations (as this is not available in the App registrations (preview) unless you edit the manifest)
  8. Select your service app (you may need to click view all applications)
  9. Click on Settings. Then Click on Properties.
  10. Under App ID URI change this to https://{ad tenant name}.onmicrosoft.com/{Service App ID}
  11. Make sure Multi-tenanted is set to Yes (it should be because of Step 5 sub step 6.)

Step 6 - Add scope to function APP

  1. Open your function app in the Azure Portal and navigate to the Authenticaiton
  2. Change the Active Directory Settings to have Allowed Token Audiences of the full value of your API scope permission from the Service APP IE

https://{AD TENANT}.onmicrosoft.com/{SERVICE APP ID}/user_impersonation

Now for the code

From your client app. Install nuget Microsoft.Identity.Client

using Microsoft.Identity.Client;
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;

namespace TestAzureFunctionLogin
{
public class ManualTestApp
{
    static string ClientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; //replace with AppID from Client App Azure AD registration
    static string ServiceId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; //replace with AppID from Service App Azure AD registration
    static string Scope = $"{ServiceId}/user_impersonation";
    static string Authority = "https://login.microsoftonline.com/organizations";

    string[] _scopes => new string[] { Scope };
    private PublicClientApplication _clientApp = new PublicClientApplication(ClientId, Authority);

    private AuthenticationResult authResult;
    public PublicClientApplication ClientApp => _clientApp;

    public async Task LoginAsync()
    {
        var user = (await ClientApp.GetAccountsAsync()).FirstOrDefault();
        authResult = await ClientApp.AcquireTokenAsync(_scopes, user);
    }

    public async Task<string> CallAzureFunction(string url)
    {
        return await GetHttpContentWithToken(url, authResult.AccessToken);
    }

    //Code taken from somewhere on the Microsoft Website
    public async Task<string> GetHttpContentWithToken(string url, string token)
    {
        var httpClient = new System.Net.Http.HttpClient();
        System.Net.Http.HttpResponseMessage response;
        try
        {
            var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url);
            //Add the token in Authorization header
            request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
            response = await httpClient.SendAsync(request);

            var content = await response.Content.ReadAsStringAsync();
            return content;
        }
        catch (Exception ex)
        {
            return ex.ToString();
        }
    }
}

}

Upvotes: 0

Related Questions