Tutorial: Call a protected web API from your .NET daemon app
This tutorial is the final part of a series that demonstrates how to call a protected web API from a .NET daemon app. In part 1 of this series, you prepared your external tenant to authorize a .NET daemon app. In this tutorial, you build your client daemon app and call a protected web API. You enable the client daemon app to acquire an access token using its own identity, then call the web API.
In this tutorial;
- Configure a daemon app to use it's app registration details.
- Build a daemon app that acquires a token on its own behalf and calls a protected web API.
Prerequisites
Create a .NET daemon app
Open your terminal and navigate to the folder where you want your project to live.
Initialize a .NET console app and navigate to its root folder.
dotnet new console -n ToDoListClient cd ToDoListClient
Install packages
Install Microsoft.Identity.Web
and Microsoft.Identity.Web.DownstreamApi
packages:
dotnet add package Microsoft.Identity.Web
dotnet add package Microsoft.Identity.Web.DownstreamApi
Microsoft.Identity.Web
provides the glue between ASP.NET Core, the authentication middleware, and the Microsoft Authentication Library (MSAL) for .NET making it easier for you to add authentication and authorization capabilities to your app. Microsoft.Identity.Web.DownstreamApi
provides an interface used to call a downstream API.
Create appsettings.json file an add registration configs
Create appsettings.json file in the root folder of the app.
Add app registration details to the appsettings.json file.
{ "AzureAd": { "Authority": "https://<Enter_the_Tenant_Subdomain_Here>.ciamlogin.com/", "ClientId": "<Enter_the_Application_Id_here>", "ClientCredentials": [ { "SourceType": "ClientSecret", "ClientSecret": "<Enter_the_Client_Secret_Here>" } ] }, "DownstreamApi": { "BaseUrl": "<Web_API_base_url>", "RelativePath": "api/todolist", "RequestAppToken": true, "Scopes": [ "api://<Enter_the_Web_Api_Application_Id_Here>/.default" ] } }
Replace the following values with your own:
Value Description Enter_the_Application_Id_Here The Application (client) ID of the client daemon app that you registered. Enter_the_Tenant_Subdomain_Here The Directory (tenant) subdomain. Enter_the_Client_Secret_Here The daemon app secret value you created. Enter_the_Web_Api_Application_Id_Here The Application (client) ID of the web API app you registered. Web_API_base_url The base URL of the web API. For example, https://localhost:44351/
where 44351 is the port number of the port your API is running on. Your API should already be running and awaiting requests by this stage for you to get this value.
Add models
Navigate to the root of your project folder and create a models folder. In the models folder, create a ToDo.cs file and add the following code:
using System;
namespace ToDoListClient.Models;
public class ToDo
{
public int Id { get; set; }
public Guid Owner { get; set; }
public string Description { get; set; } = string.Empty;
}
Acquire access token
You have now configured the required items in for your daemon application. In this step, you write the code that enables the daemon app to acquire an access token.
Open the program.cs file in your code editor and delete its contents.
Add your packages to the file.
using Microsoft.Extensions.DependencyInjection; using Microsoft.Identity.Abstractions; using Microsoft.Identity.Web; using ToDoListClient.Models;
Create the token acquisition instance. Use the
GetDefaultInstance
method of theTokenAcquirerFactory
class ofMicrosoft.Identity.Web
package to build the token acquisition instance. By default, the instance reads an appsettings.json file if it exists in the same folder as the app.GetDefaultInstance
also allows us to add services to the service collection.Add this line of code to the program.cs file:
var tokenAcquirerFactory = TokenAcquirerFactory.GetDefaultInstance();
Configure the application options to be read from the configuration and add the
DownstreamApi
service. TheDownstreamApi
service provides an interface used to call a downstream API. We call this service DownstreamAPI in the config object. The daemon app reads the downstream API configs from the DownstreamApi section of appsettings.json. By default, you get an in-memory token cache.Add the following code snippet to the program.cs file:
const string ServiceName = "DownstreamApi"; tokenAcquirerFactory.Services.AddDownstreamApi(ServiceName, tokenAcquirerFactory.Configuration.GetSection("DownstreamApi"));
Build the token acquirer. This composes all the services you have added to Services and returns a service provider. Use this service provider to get access to the API resource you have added. In this case, you added only one API resource as a downstream service that you want access to.
Add the following code snippet to the program.cs file:
var serviceProvider = tokenAcquirerFactory.Build();
Call the web API
Add code to call your protected web API using the IDownstreamApi
interface. In this tutorial, you only implement a call to Post a todo and another one to Get all todos. See the other implementations such as Delete and Put in the sample code.
Add this line of code to the program.cs file:
var toDoApiClient = serviceProvider.GetRequiredService<IDownstreamApi>();
Console.WriteLine("Posting a to-do...");
var firstNewToDo = await toDoApiClient.PostForAppAsync<ToDo, ToDo>(
ServiceName,
new ToDo()
{
Owner = Guid.NewGuid(),
Description = "Bake bread"
});
await DisplayToDosFromServer();
async Task DisplayToDosFromServer()
{
Console.WriteLine("Retrieving to-do's from server...");
var toDos = await toDoApiClient!.GetForAppAsync<IEnumerable<ToDo>>(
ServiceName,
options => options.RelativePath = "/api/todolist"
);
if (!toDos!.Any())
{
Console.WriteLine("There are no to-do's in server");
return;
}
Console.WriteLine("To-do data:");
foreach (var toDo in toDos!) {
DisplayToDo(toDo);
}
}
void DisplayToDo(ToDo toDo) {
Console.WriteLine($"ID: {toDo.Id}");
Console.WriteLine($"User ID: {toDo.Owner}");
Console.WriteLine($"Message: {toDo.Description}");
}
Run the client daemon app
Navigate to the root folder of the daemon app and run the following command:
dotnet run
If everything is okay, you should see the following output in your terminal.
Posting a to-do...
Retrieving to-do's from server...
To-do data:
ID: 1
User ID: 00aa00aa-bb11-cc22-dd33-44ee44ee44ee
Message: Bake bread
Troubleshoot
In case you run into errors,
- Confirm the registration details you added to the appsettings.json file.
- Confirm that you're calling the web API via the correct port and over https.
- Confirm that your app permissions are configured correctly.
The full sample code is available on GitHub.
Clean up resources
If you don't intend to use the apps you have registered and created in this tutorial, delete them to avoid incurring any costs.