.NET Core (Part 3)

Neil HaddleySeptember 20, 2025

Creating a Blazor App that calls Microsoft Graph.

.NETdotnet

Microsoft Graph

Microsoft Graph is a REST API that can be used to access: Microsoft 365, Azure Active Directory, Windows and Dynamics 365.

Microsoft Graph explorer is a developer tool that lets you learn about Microsoft Graph APIs.

I reviewed Microsoft Graph Explorer

I reviewed Microsoft Graph Explorer

Blazor Server app

I wanted to create a Blazor Server app that would allow users to sign in using their M365/Azure Active Directory credentials.

I wanted the Blazor Server app to display the logged in user's name and their photograph.

I created an Azure application (app) registration.

I navigated to Azure Active Directory

I navigated to Azure Active Directory

I clicked New registration

I clicked New registration

I selected Single tenant

I selected Single tenant

I set the redirect URI to localhost

I set the redirect URI to localhost

The app registration was created

The app registration was created

I updated the Authentication settings

I updated the Authentication settings

I added a client secret

I added a client secret

The secret would be valid for 180 days

The secret would be valid for 180 days

I copied the value

I copied the value

Blazor Server

Blazor is a web framework for building Razor components.

Razor components run server-side in ASP.NET Core.

(Razor components run client-side in the browser using WebAssembly)

A blazor server application can be generated using the dotnet command line tool.

BASH
1$ dotnet new blazorserver -o <project name>

In this case I wanted to create a blazor server application that would authenticate users using Azure Active Directory (the App registration created above) and call Microsoft Graph to access the user's profile and the user's profile photograph.

BASH
1$ dotnet new blazorserver --auth SingleOrg --calls-graph -o haddley-blazor-graph --client-id "5df669b6-f661-473c-9f5d-100f792d16c7" --tenant-id "2788913d-04ad-47a2-ac42-4b02caa6a4be" --domain "p8lf.onmicrosoft.com" -f net7.0
I ran dotnet new blazorserver ...

I ran dotnet new blazorserver ...

I ran dotnet run and navigated to localhost

I ran dotnet run and navigated to localhost

The generated Blazor Server app (includes integration with Azure Active Directory and Microsoft Graph)

The generated Blazor Server app (includes integration with Azure Active Directory and Microsoft Graph)

I copied the ClientSecret to appsettings.json

I copied the ClientSecret to appsettings.json

I was now able to login to the Blazor Server app using Azure Active Directory credentials

I was now able to login to the Blazor Server app using Azure Active Directory credentials

I provided the password

I provided the password

I skipped Multi factor authentication (for now)

I skipped Multi factor authentication (for now)

I chose to stay signed in

I chose to stay signed in

I navigated to the Show profile page

I navigated to the Show profile page

I reviewed the /showprofile page

I reviewed the /showprofile page

I noted that the key call was to the injected GraphServiceClient to access Microsoft Graph

I noted that the key call was to the injected GraphServiceClient to access Microsoft Graph

I updated the navigation...

I updated the navigation...

...to include a link to a /showphoto page

...to include a link to a /showphoto page

I noted that the /showphoto page would need to make a "my photo" call to Microsoft Graph

I noted that the /showphoto page would need to make a "my photo" call to Microsoft Graph

I copied the "Me.Photo.Content" expression/path to the new /showphoto page

I copied the "Me.Photo.Content" expression/path to the new /showphoto page

The Show photo page was running

The Show photo page was running

Program.cs

I noticed that the Blazor Server project includes a Program.cs file with contents similar to what you would expect to see in a Model-View-Controller project.

A Blazor Server project and a Model-View-Controller project are both ASP.NET Core projects and it is possible to mix and match.

I reviewed Program.cs (part 1)

I reviewed Program.cs (part 1)

I reviewed Program.cs (part 2)

I reviewed Program.cs (part 2)

ShowPhoto.razor

RAZOR
1@page "/showphoto"
2
3@using Microsoft.Identity.Web
4@using Microsoft.Graph
5@inject Microsoft.Graph.GraphServiceClient GraphServiceClient
6@inject MicrosoftIdentityConsentAndConditionalAccessHandler ConsentHandler
7
8<h1>My Photo</h1>
9
10<p>This component demonstrates fetching data from a service.</p>
11
12@if (imgDataURL == null)
13{
14    <p><em>Loading...</em></p>
15}
16else
17{
18    <img src=@imgDataURL />
19}
20
21@code {
22    String? imgDataURL;
23
24    protected override async Task OnInitializedAsync()
25    {
26        try
27        {
28            Stream photo = await GraphServiceClient.Me.Photo.Content.Request().GetAsync();
29
30            if (photo != null)
31            {
32                MemoryStream ms = new MemoryStream();
33                photo.CopyTo(ms);
34                byte[] buffer = ms.ToArray();
35                string result = Convert.ToBase64String(buffer);
36                imgDataURL = string.Format("data:image/png;base64,{0}", result);
37            }
38            else
39            {
40                imgDataURL = "";
41            }
42        }
43        catch (Exception ex)
44        {
45            ConsentHandler.HandleException(ex);
46        }
47    }
48}

Program.cs

CSHARP
1using Microsoft.AspNetCore.Authentication;
2using Microsoft.AspNetCore.Authentication.OpenIdConnect;
3using Microsoft.Identity.Web;
4using Microsoft.Identity.Web.UI;
5using Microsoft.AspNetCore.Authorization;
6using Microsoft.AspNetCore.Components;
7using Microsoft.AspNetCore.Components.Web;
8using Microsoft.AspNetCore.Mvc.Authorization;
9using Graph = Microsoft.Graph;
10using haddley_blazor_graph.Data;
11
12var builder = WebApplication.CreateBuilder(args);
13
14// Add services to the container.
15var initialScopes = builder.Configuration["DownstreamApi:Scopes"]?.Split(' ');
16
17builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
18    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
19        .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
20            .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
21            .AddInMemoryTokenCaches();
22builder.Services.AddControllersWithViews()
23    .AddMicrosoftIdentityUI();
24
25builder.Services.AddAuthorization(options =>
26{
27    // By default, all incoming requests will be authorized according to the default policy
28    options.FallbackPolicy = options.DefaultPolicy;
29});
30
31builder.Services.AddRazorPages();
32builder.Services.AddServerSideBlazor()
33    .AddMicrosoftIdentityConsentHandler();
34builder.Services.AddSingleton<WeatherForecastService>();
35
36var app = builder.Build();
37
38// Configure the HTTP request pipeline.
39if (!app.Environment.IsDevelopment())
40{
41    app.UseExceptionHandler("/Error");
42    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
43    app.UseHsts();
44}
45
46app.UseHttpsRedirection();
47
48app.UseStaticFiles();
49
50app.UseRouting();
51
52app.MapControllers();
53app.MapBlazorHub();
54app.MapFallbackToPage("/_Host");
55
56app.Run();