Telerik blogs

Your app’s server-side ASP.NET Core frontend needs to claim the roles that will allow it to access your app’s secured backend.

In Part 8: Securing a Web Service in an Azure App Service, I showed how to create an App Registration for a Web Service running in an App Service. That Web Service’s App Registration defined one or more roles that the Web Service’s code expects any server-side frontend to provide before the Web Service would execute any of the frontend’s requests.

In this post, I’m going to walk through creating an App Registration for your server-side (e.g., ASP.NET Core) frontend that makes those roles available to your frontend application, along with the code to both claim to pass those roles onto the Web Service.

Creating a Frontend App Registration

To create an App Registration for your frontend, surf to the Azure Portal, and open the App Registrations page. Then:

  1. At the left end of the menu across the top of this page, click on + New Registration. Give your registration a name that refers to your frontend (I used WarehouseMgmtRegFrontEndAspNet).
  2. In the following radio button list of choices, I’m assuming that you’re creating an app to be used internally, so you want the first choice: Accounts in this organizational directory only. The other options are there to support organizations that have multiple Azure tenants, third-party vendors selling solutions to Azure users, and products accessed by random users (e.g., your customers).
  3. Click the Register button to save your App Registration.

If you’re creating this registration for someone else to modify, in the menu on the left, select the Owners choice and add the user who will manage the registration as an Owner for the App Registration.

Completing the Server-Side App Registration

Since you’re creating a server-side (e.g., ASP.NET Core) frontend, you can make it difficult for applications to claim this registration by requiring the frontend application to provide some credentials—either a secret string or a certificate.

To specify those credentials, in the App Registration’s menu on the left, expand the Manage node and select Certificates & secrets to display the Certificates and secrets page on the right. To simplify this example, I’ll walk through adding a secret (using a certificate really requires using the Azure Key Vault which I’ll cover in a later post).

To add a secret key:

  1. Click on the Client secrets tab.
  2. When the tab displays, click on the + New client secret choice to open a panel on the right.
  3. In this panel, enter a description to document your secret (I used WarehouseMgmtFrontendRegSecret) and set an expiry period (for development purposes, I picked the longest period provided—730 days).
  4. Click the Add button to create your secret and return to the main page.
  5. When your new secret appears in the list on this page, use the copy icon to copy the Value entry for your secret and save it somewhere (in my experience, if you return to this page, you won’t necessarily be able to either view or copy this secret).

Optional: Accepting Authentication Tokens

You can also specify a URL where authentication tokens are to be sent for your frontend (note: not your authorization tokens for talking to your Web Service—that comes later). This is optional for a server-side frontend and can be skipped if you can assume that your user is always logged in before running your frontend (e.g., if it’s you, in development).

To add this URL, in the menu on the left of your App Registration, expand the Manage node and select Authorization.

In your App Registration’s menu on the left, expand the Manage node and select Authentication to display the Platform configurations page. On that page, click the + Add a platform choice to display Web applications panel on the right.

In the Web applications panel, pick the Web choice. That will move you to the second page in the panel, where you should enter the URL for your frontend’s app service, followed by any URL-compliant string you want, though “/signin-oidc” is the convention (e.g., for my App Service, I would use https://warehousemgmtfrontendaspnet.azurewebsites.net/signin-oidc).

Next, scroll down to the bottom of the panel and check off the ID tokens for… choice before clicking the Configure button to close the panel.

Specifying the Roles

Now you need to specify which of all the roles defined in your Web Service’s App Registration are the ones you want your client-side frontend to use. To do that:

  1. In the menu on the left of your App Registration, under the Manage node select the Api Permissions choice to display a new page on the right.
  2. At the left end of the menu across the page, select the + Add a permission choice to display the Request API Permissions panel on the right.
  3. In this panel, select the APIs my organization uses tab.
  4. In that tab, in the Apps in your directory, type the name of your Web Service’s App Registration. When it appears, select it.
  5. To assign one or more of the roles you defined in your Web Service’s App Registration, click on the Application permissions box to display the roles you defined.
  6. Check the role(s) you want your frontend application to have.
  7. Click the Add permission button to close the panel and add the role to your list of roles to your API permissions page.

When your list of roles refreshes, their Status column will display a warning sign and a message beginning Not granted for…. You must grant consent to those roles now (this is something else you may not be allowed to do and may require an administrator to surf to this registration to finish this process).

To grant consent, still on the API Permissions page, at the top of the list of permissions, click the green checkmark beside the Grant admin consent for… choice and click Yes in the “are you sure” textbox that appears. All of the roles you’ve assigned will be given consent (if you want, you can use the ellipsis——at the right end of each role to selectively remove Admin consent).

You’re now ready to have your frontend app claim this App Registration with its roles and then pass those roles to your Web Service.

Troubleshooting

It’s worth taking a moment to use the provided tool to check that you’ve set up your Web Service’s registration correctly.

From the menu down the left side of your App Registration, select Integration assistant to open the assistant on the right. From the dropdown list, select Web App.

After making your selection in the first dropdown list, click anywhere on the page to close the list and reveal the Is this application calling APIs? option. Set the toggle beneath that to Yes and then click the Evaluate my app registration button.

The assistant will display a list of required and optional recommendations to implement and a set of recommendations that you shouldn’t implement. Provided you have green checkmarks on all the required and shouldn’t recommendations, your App Registration should be ready to use. (You can ignore the optional recommendations, at least for now).

Claiming the App Registration

Your next step is to add the code to your server-side app to claim the registration you just created and pass the roles it specifies on to your Web Service.

To do that, in your development tool (Visual Studio or Visual Studio Code), for an ASP.NET Core frontend, add the Microsoft.Identity.Web Nuget package. In your Program.cs file, make sure you have added both UseAuthorization and UseAuthentication to your application’s pipeline with code like this (these must follow your call to UseRouting):

app.UseAuthentication();
app.useAuthorization();

Still in your Program.cs file, add this code near the top of your file (e.g., right after the call to CreateBuilder is good) to claim your registration, acquire the authorization token your application needs, and store it in a cache for your code to use later:

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration)
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

The token in the cache (an encrypted string) represents the roles that were granted in your App Registration.

That code expects to find all the information it needs for your frontend to claim its App Registration in your appsettings.json file, in a section called AzureAd. This includes:

  • The secret key you created for your app registration
  • The client id and application id from the Overview page of your frontend’s App Registration
  • The URL for the identity provider you’re using (if you’re using Microsoft Entra ID as your identity provider, its URL is https://login.microsoftonline.com/)
  • The domain of your frontend’s app service (i.e., <your frontend app service>.azurewebsites.net).

An AzureAd section with the right property names has this structure (I’ve assumed you’re using the conventional “/signin-oidc” for your frontend’s redirect URL):

"AzureAd": {
  "Instance": "https://login.microsoftonline.com/",
  "Domain": "<name of your app service>.azurewebsites.net",
  "ClientId": "<Application (Client) Id from the App Registration’s Overview page>",
  "TenantId": "<Directory (tenant) Id from the App Registration’s Overview page>",
  "ClientSecret": "<Client secret>",
  "CallbackPath": "/signin-oidc"
},

Here’s what a typical AzureAd section would look like:

"AzureAd": {
  "Instance": "https://login.microsoftonline.com/",
  "Domain": "warehousemgmtfrontendaspnet.azurewebsites.net",
  "TenantId": "e98…-…-…-…-…",
  "ClientId": "80…-…-…-…-…",
  "ClientSecret": "Hp68…,
  "CallbackPath": "/signin-oidc"
},

Calling the Web Service

Finally, in the code where you call your Web Service, you need to retrieve the token from the token cache you loaded in your Program.cs file and to add the token as an authorization header to your HTTP request.

To support that, in the constructor for either the Razor Page or the controller where you’re calling the Web Service, you should grab these objects (these are in addition to the ILogger object that your code is probably already acquiring from ASP.NET Core’s services collection):

  • ITokenAcquisition: This object will let you pull your access token from your application’s cache
  • IConfiguration: To pull information from your appsettings.json file

As a result, a typical constructor with its supporting fields in a Razor Page would look something like this:

    private readonly ILogger<HomeController> logger;
    private readonly ITokenAcquisition tokenAcquisition;
    private readonly IConfiguration config;

    public IndexModel(ILogger<IndexModel> logger,
                            ITokenAcquisition tokenAcquisition,
                            IConfiguration config)
    {
        this.logger = logger;
        this.tokenAcquisition = tokenAcquisition;
        this.config = config;
    }

Next, you’ll need to pull the encrypted string (your access token) from the cache where you loaded it back in your Program.cs file. You pull that token using the GetAccessTokenForAppAsync method of ITokenAcquisition object you grabbed in your page or controller’s constructor.

You need to pass the GetAccessTokenForAppAsync method two parameters:

  • Your Web Service’s Application ID URL (available from your Web Service’s App Registration’s Overview page) with “/.default” tacked on the end (that id will probably be in the format api:<Web service client id>)
  • The TenantId from your appsettings.json file

Here’s that code:

string accessToken = await tokenAcquisition.GetAccessTokenForAppAsync(
     "<Web service Application ID URL>/.default",
     tenant: config["AzureAd:TenantId"]);

Once you have the token, you can use it to generate an authorization header of type Bearer and add that header to your HTTP request’s default request headers, like this (this code also adds a correlation id to the request to support tracking the request):

 HttpClient hc = new();

hc.DefaultRequestHeaders.Authorization 
             = new AuthenticationHeaderValue("Bearer", accessToken);

 hc.DefaultRequestHeaders.Add("X-Correlation-ID",
                                                                        new string[] { Guid.NewGuid().ToString() });       
HttpResponseMessage resp = await hc.GetAsync("<URL for Web service>");

You can now deploy your server-side frontend to its App Service and surf to it to see if it works (and if it doesn’t you may want to check out my earlier post on debugging, Coding Azure 6: Debugging and Logging an Application in an Azure App Service.

You can also test your frontend locally from your development tool, provided you’ve added the local URL your frontend runs at to your App Service’s CORS list.

Next Steps

There is more that you can do here to secure access between your Web Service and its frontend. In a later post, for example, I’ll integrate Web API Management into this application and restrict access to my Web Service’s App Services to only requests coming from my frontend’s App Service (with some caveats). I’ll also move all the sensitive information in my appsettings.json file into the Azure Key Vault.

You could also consider adding an Application Gateway with a Web Application Firewall to your Web Service’s App Service and use that to accept only requests from your frontend’s App Service’s outbound TCP addresses (as I did with the Azure SQL database).

But, while the backend Web Service is secured, the frontend application is not. In my next post, I’ll cover how to assign permissions to your frontend that you can check in your frontend’s code.


Peter Vogel
About the Author

Peter Vogel

Peter Vogel is both the author of the Coding Azure series and the instructor for Coding Azure in the Classroom. Peter’s company provides full-stack development from UX design through object modeling to database design. Peter holds multiple certifications in Azure administration, architecture, development and security and is a Microsoft Certified Trainer.

Related Posts

Comments

Comments are disabled in preview mode.