Blazor MS-Teams Tab App Series – Part 7

Welcome to the eigth and final post (0-7) of a series that will show you how you can write a Microsoft Teams Application with Blazor Webassembly.

You will find the complete source code in this github repository. I placed my changes in different branches so you can easily jump in at any time.

In this post we will secure our WeatherForeCast API in the Blazor-Server project and access it the same way we did with Microsoft Graph.

Webservice AAD App Registration

Click +New registration to register a new AAD application. Replace https://demo.ngrok.io with your own domain.

  • Name: BlazorService
  • Account Type: Accounts in any organizational directory (Mulitenant)
  • Redirect Uri: https://demo.ngrok.io

and click Register.

Make sure to copy your Application (Client) ID and store it for later. I am using the following Client-ID throughout the samples which need to be replaced with yours: 38a06d0f-e38d-4e93-be39-aa42d8ebe604

IMPORTANT

Microsoft Teams will only allow URLs that are in the premitted domain. Make sure that you use your NGROK or registered domain here!

Select Expose an API under the Manage section and set an application ID Uri. You can reuse the given UID from the proposed App ID Uri. For example:

  • api://spectologic.eu.ngrok.io/38a06d0f-e38d-4e93-be39-aa42d8ebe604

Now select +Add a scope to add the required "access_as_user" scope:

  • Scope Name : weather_api
  • Who can consent: Admins and Users
  • Provide some titel/text for the information that is displayed when the administrator of a tenant or a user is asked to grant consent.

Finally click Add scope to add the scope. You will get an url that looks like this:

  • api://spectologic.eu.ngrok.io/38a06d0f-e38d-4e93-be39-aa42d8ebe604/weather_api

To directly allow Teams to access we add the well known client IDs of the Microsoft Teams-Applications. There are two of them: Microsoft Teams Web Client and Microsoft Teams Desktop Client

Use the +Add a client application button to add the following client ids. You need to also select the scope "access_as_user" so that the Teams-Apps can access this specific scope!

  • Teams Web Application: 5e3ce6c0-2b1f-4285-8d4b-75ee78787346
  • Teams Mobile/Desktop App: 1fec8e78-bce4-4aaf-ab1b-5451cc387264

Adding the service to the BlazorApp AAD App Registration

Open the BlazorApp AAD application

Click on API permissions under the Manage section to add permissions to access our webservice. We do want to add delegated permissions (permissions in the context of the user). To add the following permission click + Add a permission and select My APIs / BlazorService and select delegated permissions:

  • weather_api

Afer clicking save make sure to click Grant admin consent for … button to grant these permissions within this tenant. So all users of this tenant do not have to explicitly consent to your MS-Teams App.

Securing the WeatherForeCast API

In BlazorTeamApp.Server project add following configuration to the appsettings.json file:

"AzureAd": {
  "Instance": "https://login.microsoftonline.com/",
  "ClientId": "38a06d0f-e38d-4e93-be39-aa42d8ebe604",
  "TenantId": "common",
  "Audience": "api://demo.ngrok.io/38a06d0f-e38d-4e93-be39-aa42d8ebe604",
  "Issuer": "https://sts.windows.net/c409088f-0d18-498f-8ece-8659ea219c20/"
}

Add following nuget package to BlazorTeamApp.Server:

  • Microsoft.AspNetCore.Authentication.AzureAD.UI

In Startup.cs modify the method ConfigureService to add authentication:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultScheme = AzureADDefaults.AuthenticationScheme;
    }).AddJwtBearer("AzureAD", options =>
    {
        options.Audience = Configuration.GetValue<string>("AzureAd:Audience");
        options.Authority = Configuration.GetValue<string>("AzureAd:Instance")  
            + Configuration.GetValue<string>("AzureAd:TenantId");
        options.TokenValidationParameters = new 
            Microsoft.IdentityModel.Tokens.TokenValidationParameters()
            {
                ValidIssuer = Configuration.GetValue<string>(
                    "AzureAd:Issuer"),
                ValidAudience = Configuration.GetValue<string>(
                    "AzureAd:Audience")
            };
    });
 
    services.AddControllersWithViews();
    services.AddRazorPages();
    services.AddTokenProvider();
}

Also in Startup.cs modify the method Configure(IApplicationBuilder,…:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    ... 
    app.UseAuthentication();    // Add this line here
    // app.UseHttpsRedirection();
    app.UseBlazorFrameworkFiles();
    app.UseStaticFiles();
    app.UseRouting();
    app.UseAuthorization();    // Add this line here
    app.UseEndpoints(endpoints =>
    {
      ...

Now use the Authorize attribute to secure our WeatherForeCast controller by adapting Controllers/WeatherForeCastController.cs

...
namespace BlazorTeamApp.Server.Controllers
{
    [Authorize]     // Add this line
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
...

If you now start your application in Visual Studio and try to access this url:

you should receive a 401 error!

Access the Service from our Blazor Application

Since we have written all the necessary code before we can simply add a new button to our razor page and call our GetServerToken method to get an access token for our weather api:

Add this button to your Tab.razor page

<br  />
<button class="btn btn-light"  @onclick="GetWeatherServerAuthToken">Get Weather Server-Token</button>

Implement the button handler in Tab.razor

private string weatherAuthServerToken = string.Empty;
private async Task GetWeatherServerAuthToken()
{
    try
    {
        authServerToken = await TeamsClient.GetServerToken(authToken, 
           new string[1] { "api://spectologic.eu.ngrok.io/38a06d0f-e38d-4e93-be39-aa42d8ebe604/weather_api" });
    }
    catch (Exception ex)
    {
        authServerToken = $"Fehler: {ex.Message}";
    }
}

I leave implementing the call to the webservice with the gained token up to you. It is done the same way as we have called the Microsoft Graph me-Endpoint.

That’s it, have a great day

AndiP

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit deinem WordPress.com-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s