Securing an ASP.NET Core 2.0 Web API with Azure AD AuthN, and consuming it from a native WPF Desktop App using ADAL

As part of this Article, we are going to perform 3 different things –

  1. Create a new Azure AD Tenant, and add a new User to it.
  2. Create a new ASP.NET Rest API in Visual Studio 2017 using the new ASP.NET Core 2.0 with “No Authentication” and then later implementing Azure AD Authentication into the API to enforce authentication through the newly created Azure AD Tenant in Step 1.
  3. Create a native WPF desktop application and make it consume the Rest API created (and secured with Azure AD) in the earlier steps.

So, now, let’s get started with the 3 different Steps, one by one.

 

  1. Create a new Azure AD Tenant, and add a new User to it.

At this first step, we create a new Azure AD tenant. As of 26.10.2017, a new Azure AD Tenant can only be created from the old Azure Portal located at https://manage.windowsazure.com/ After navigating to “Active Directory” from the left pan, “+NEU” button at the bottom left corner of the screen must be clicked.

Once the new Tenant has been created, it may be accessed from the new Azure Portal at https://portal.azure.com/ (You can of course continue at the old portal if you wish, and do the various available operations with the newly created Tenant there). On the new Portal, you can easily switch over to the newly created Azure AD Tenant by clicking on the top-right corner of the page.

Next, you’d need to add some new users to this new Azure AD tenant.

  1. Create a new ASP.NET Rest API in Visual Studio 2017 using the new ASP.NET Core 2.0 with “No Authentication” and then later implementing Azure AD Authentication into the API to enforce authentication through the newly created Azure AD Tenant in Step 1.

We next fire up Visual Studio 2017 to create a new Project, and select “ASP.NET Core Web Application” from the selection list for ‘Web’. From the next dialogue window, we then select “Web API” and “ASP.NET Core 2.0”. For this article, we select ‘No Authentication’ since we’ll be incorporating the Azure AD Authentication into this project at a later stage. However, if you don’t wish to take that burden of plugging in extra code yourself, you could always select your Azure AD authentication right at this stage and Visual Studio does all the necessary hooking up to make your new Web API use the selected Azure AD for Authentication.

Now with the blank Web API Project with ‘No Authentication’ created, we first register the API on our Azure AD Tenant. To do so, we need to navigate to Azure Active Directory>App-Registrierungen>Registrierung einer neuen Anwendung.

Once the API has been registered, we can take note of certain information such as Anwendung-ID (or, Application-ID), Startseite (or Homepage).

Important to note: The Startseite should match with the App URL set at Properties> Debug for your project in Visual Studio.

Next, we add a folder to the Project, name it ‘Extensions’ and proceed to adding two .cs class files in it that are required for making the necessary C# code for Azure AD Authentication to work.

Following are the code details of the two .cs files.

 AzureAdAuthenticationBuilderExtensions.cs


using System;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Authentication
{
    public static class AzureAdServiceCollectionExtensions
    {
        public static AuthenticationBuilder AddAzureAdBearer(this AuthenticationBuilder builder)
            => builder.AddAzureAdBearer(_ => { });
        public static AuthenticationBuilder AddAzureAdBearer(this AuthenticationBuilder builder, Action configureOptions)
        {
            builder.Services.Configure(configureOptions);
            builder.Services.AddSingleton<iconfigureoptions, configureazureoptions="">();
            builder.AddJwtBearer();
            return builder;
        }
        private class ConfigureAzureOptions: IConfigureNamedOptions
        {
            private readonly AzureAdOptions _azureOptions;
            public ConfigureAzureOptions(IOptions azureOptions)
            {
                _azureOptions = azureOptions.Value;
            }
            public void Configure(string name, JwtBearerOptions options)
            {
                options.Audience = _azureOptions.ClientId;
                options.Authority = $"{_azureOptions.Instance}{_azureOptions.TenantId}";
            }
            public void Configure(JwtBearerOptions options)
            {
                Configure(Options.DefaultName, options);
            }
        }
    }
}

 

 

 AzureAdOptions.cs

namespace Microsoft.AspNetCore.Authentication
{
    public class AzureAdOptions
    {
        public string ClientId { get; set; }
        public string ClientSecret { get; set; }
        public string Instance { get; set; }
        public string Domain { get; set; }
        public string TenantId { get; set; }
    }
}


Next, we need to update the appsettings.json file with the details of the Azure AD that should be used

+++++++++++

appsettings.json

++++++++++++


{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "Domain": "XXX.onmicrosoft.com",
    "ClientId": "https://XXX.onmicrosoft.com/App23"
  },
  "Logging": {
    "IncludeScopes": false,
    "Debug": {
      "LogLevel": {
        "Default": "Warning"
      }
    },
    "Console": {
      "LogLevel": {
        "Default": "Warning"
      }
    }
  }
}

+++++++++++

Instance should be same irrespective of the API register.

TenantId can be found by navigating to Azure Ad>Properties, or from Powershell using the following command :

(Invoke-WebRequest https://login.windows.net/sampleazuread.onmicrosoft.com/.well-known/openid-configuration|ConvertFrom-Json).token_endpoint.Split('/')[3]

sampleazuread.onmicrosoft.com will have to be obviously replaced by your own Azure AD Tenant’s Domain.

Domain can be easily found by navigating to Azure AD>Domainnames.

ClientId should be the same as App-ID-URI accessed by navigating to the registered API’s Properties.

Next, we need to update our Startup.cs file so that the Azure AD Authentication is added to the API project when it starts up.


public class Startup
{
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        public IConfiguration Configuration { get; }
        // This method gets called by the runtime. Use this method to add services       to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(sharedOptions =>
            {
                sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddAzureAdBearer(options => Configuration.Bind("AzureAd", options));
            services.AddMvc();
        }
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseAuthentication();
            app.UseMvc();
        }
  1. Create a native WPF desktop application and make it consume the Rest API created (and secured with Azure AD) in the earlier steps.

Now we create a tiny little native desktop WPF application that shall be consuming the Restful API just created in the earlier steps.

First, we fire up Visual Studio’s Create New Project and this time choose Visual C#>Windows Classic Desktop>WPF App (.Net Framework).

Now, we’d need to first register our native client app at the Azure AD Tenant where the API is already registered.

This time, for the client application, however, the application type must be chosen as “Native” rather than “Web App/API”.

After choosing the application type, we need to also choose a redirection-URL, and subsequently give the registered native client app the permission to access the API that was earlier registered.

So, with the registration of the native client application done, we take note of the Application ID, Redirect URL of the client app and move over to Visual Studio write the remaining code in the WPF application.

We need to install two NuGet packages first.

PM> Install-Package Microsoft.Net.Http

PM> Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory

Next we simply write some lines of code in the MainWindow class’ code behind.


using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Windows;
namespace WpfApp1
{
    /// 
    /// Interaction logic for MainWindow.xaml
    /// 
    public partial class MainWindow : Window
    {
        private static string aadInstance = "https://login.windows.net/{0}/";
        private static string tenant = "XXX.onmicrosoft.com";
        private static string clientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
        Uri redirectUri = new Uri("https://App23AzureADClient");
        private static string authority = String.Format(aadInstance, tenant);
        private static string apiResourceId = "https://xxx.onmicrosoft.com/App23";
        private static string apiBaseAddress = "https://localhost:44324/";
        private AuthenticationContext authContext = null;
        public MainWindow()
        {
            InitializeComponent();
        }
        private async void Btn1_Click(object sender, RoutedEventArgs e)
        {
            authContext = new AuthenticationContext(authority);
            AuthenticationResult authResult = authContext.AcquireTokenAsync(apiResourceId, clientId, redirectUri, new PlatformParameters(PromptBehavior.Auto)).Result;
            HttpClient client = new HttpClient();
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
            HttpResponseMessage response = await client.GetAsync(apiBaseAddress + "api/values");
            string responseString = await response.Content.ReadAsStringAsync();
            Txt1.Text = responseString;
        }
    }
}

 

aadInstance remains same for all cases.

tenant is the DomainName of the Azure AD Tenant.
clientId is the ApplicationId of the registered client App.

redirectUri is simply the Redirect URI of the registered client App.
apiResourceId and apiBaseAddress are to be directly fetched the registered client app’s details from the Azure AD Tenant on the Azure Portal.


With the whole code set up and explained, let us now test the client application if it can successfully authenticate and consume the Restful API. Need to however ensure that the Restful API is already running on the local IIS before testing the native WPF app.

After we click on the “Click!” button, Azure AD Tenant prompts us for authentication. On Successful authentication, the client Application receives an Access Token. Next, this Access Token is added to the Header of the GET request that the client application sends to the Rest API. If the Access Token is valid, the request information is returned. In this case, we are accessing a simple JSON array of strings – which gets returned to the textbox of the MainWindow.

  • Erstellt am .
  • Region Nord: Hamburg, Emden

  • Region Mitte: Langenfeld, Offenbach am Main

  • Region Ost: Leipzig

  • Region Süd: München, Ulm

Copyright by Orange Networks GmbH
Cookies erleichtern die Bereitstellung unserer Dienste. Mit der Nutzung unserer Dienste erklären Sie sich damit einverstanden, dass wir Cookies verwenden.
Weitere Informationen Ok