Calling a ASP.NET Core Web API from a WPF application using Azure AD
About this sample
Scenario
You expose a Web API and you want to protect it so that only authenticated user can access it.
Overview
This sample presents a Web API running on ASP.NET Core 2.0, protected by Azure AD OAuth Bearer Authentication. The Web API is exercised by a .NET Desktop WPF application. The .Net application uses the Active Directory Authentication Library (ADAL.Net) to obtain a JWT access token through the OAuth 2.0 protocol. The access token is sent to the ASP.NET Core Web API, which authenticates the user using the ASP.NET JWT Bearer Authentication middleware.
This sample has been updated to ASP.NET Core 2.0. Looking for previous versions of this code sample? Check out the tags on the releases GitHub page.
User experience with this sample
The Web API (TodoListService) maintains an in-memory collection of to-do items per authenticated user. Several applications signed-in under the same identity share the same to-do list.
The WPF application (TodoListClient) enables a user to:
- Sign in. The first time a user signs it, a consent screen is presented letting him/her consent for the application accessing the TodoList Service and the Azure Active Directory. When s/he has signed-in, the user sees the list of to-do items exposed by Web API for the signed-in identity
- add more to-do items (buy clicking on Add item).
Next time a user runs the application, the user is signed-in with the same identity as the application maintains a cache on disk. Users can clear the cache (which will also have the effect of signing them out)
How to run this sample
Pre-requisites
- Install .NET Core for Windows by following the instructions at dot.net/core, which will include. (See Quickstart: Add new users to Azure Active Directory.
- From the Azure portal, note the following information:
- The Tenant domain: See the App ID URI base URL. For example: contoso.onmicrosoft.com
- The Tenant ID: See the Endpoints window (button next to New application registration). Record the GUID from any of the endpoint URLs. For example: da41245a5-11b3-996c-00a8-4d99re19f292. Alternatively you can also find the Tenant ID in the Properties of the Azure Active Directory object (this, contoso.onmicrosoft.com.
- Find the
TenantIdproperty and replace the value with the Tenant ID you registered earlier,
- Find the
ClientIdproperty and replace the value with the Application ID (Client ID) property of the Service application, that you registered earlier.
Configure the TodoListClient C# project
- In the TodoListClient project, open
App.config.
- Find the app key
ida:Tenantand replace the value with your AAD Tenant ID (GUID). Alternatively you can also use your AAD tenant Name (for example, contoso.onmicrosoft.com).
- Find the app key
ida:ClientIdand replace the value with the Application ApplicationID (Client ID) of the Service application (a GUID)
- If you changed the default value, find the app key
todo:TodoListBaseAddressand replace the value with the base address of the TodoListService project.
Step 4: Run the sample
Clean the solution, rebuild the solution, and run it. You might want to go into the solution properties and set both projects as startup projects, with the service project starting first.
When you start the Web API, you'll get an empty web page. This is expected.
Explore the sample by signing in into the TodoList client, adding items to the To Do list, removing the user account (clearing the cache), and starting again. As explained, if you stop the application without removing the user account, the next time you run the application, you won't be prompted to sign in again - that is because.
How the controller
In the TodoListService project, add a folder named
Models and then a file named
TodoItem.cs with the following content:
namespace TodoListService.Models { public class TodoItem { public string Owner { get; set; } public string Title { get; set; } } }
Under the
Controllers folder, rename the file
ValuesController.cs to
TodoListController.cs.
Make sure the content is the following:
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using TodoListService.Models; namespace TodoListService.Controllers { [Authorize] [Route("api/[controller]")] public class TodoListController : Controller { static ConcurrentBag<TodoItem> todoStore = new ConcurrentBag<TodoItem>(); [HttpGet] public IEnumerable<TodoItem> Get() { string owner = (User.FindFirst(ClaimTypes.NameIdentifier))?.Value; return todoStore.Where(t => t.Owner == owner).ToList(); } [HttpPost] public void Post([FromBody]TodoItem Todo) { string owner = (User.FindFirst(ClaimTypes.NameIdentifier))?.Value; todoStore.Add(new TodoItem { Owner = owner, Title = Todo.Title }); } } }