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:
- Client ID : Client ID of the BlazorService
- Audience: AAD App Uri
- Issuer: https://sts.windows.net/{tenantID}/
"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