{"id":5116,"date":"2020-07-21T13:14:45","date_gmt":"2020-07-21T20:14:45","guid":{"rendered":"https:\/\/officedevblogs.wpengine.com\/?p=5116"},"modified":"2021-11-17T13:13:50","modified_gmt":"2021-11-17T21:13:50","slug":"how-to-build-a-blazor-web-app-with-azure-active-directory-authentication-and-microsoft-graph","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/how-to-build-a-blazor-web-app-with-azure-active-directory-authentication-and-microsoft-graph\/","title":{"rendered":"How to build a Blazor web app with Azure Active Directory authentication and Microsoft Graph"},"content":{"rendered":"<p>If you\u2019re a .NET developer, then it\u2019s quite likely that you\u2019ve heard how Blazor is one of the hottest technologies these days.\u00a0 Blazor is a framework for building interactive client-side web UI with .NET. Blazor Server, the one that we will focus on this blog post, provides support for hosting Razor components on the server in an ASP.NET Core app. UI updates are handled over a SignalR connection. And since most applications require some form of authentication and authorization, what better way to show you how to implement authentication with Azure AD and how to retrieve data from Microsoft Graph.<\/p>\n<h3>Prerequisites<\/h3>\n<p>To follow along you\u2019ll need to have the latest version of the <a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet-core\/3.1\">.NET Core 3.1 SDK<\/a>, <a href=\"https:\/\/visualstudio.microsoft.com\/downloads\/\">Visual Studio 2019<\/a> (optional but a great choice) and an Azure AD tenant. If you don\u2019t have access to an Azure AD tenant, then you get one totally FREE by either registering to the <a href=\"https:\/\/developer.microsoft.com\/en-us\/microsoft-365\/dev-program\">Microsoft 365 Developer program<\/a> or by creating a <a href=\"https:\/\/azure.microsoft.com\/free\">Free Azure Trial<\/a> account<\/p>\n<h3>Blazor and authentication<\/h3>\n<p>If you\u2019re building Blazor (server-side) apps, then we have some great news. The Visual Studio and CLI templates support authentication out of the box. Open Visual Studio and create a new Blazor app. We will name it \u201cBlazorAppWithAuth\u201d and follow the rest of the instructions below.<\/p>\n<p><img decoding=\"async\" class=\"alignleft size-full wp-image-5117\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/1.gif\" alt=\"\" width=\"1236\" height=\"844\" \/><\/p>\n<p>By selecting the Work or School Accounts authentication option, Visual Studio created the appropriate app registration in Azure AD and configured our Blazor app with the necessary settings and code in order for authentication to work out of-the-box. We can confirm this by inspecting the appsettings.json.<\/p>\n<p><img decoding=\"async\" class=\"alignleft size-full wp-image-5118\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/2.png\" alt=\"\" width=\"618\" height=\"386\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/2.png 618w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/2-300x187.png 300w\" sizes=\"(max-width: 618px) 100vw, 618px\" \/><\/p>\n<p>And if we jump to our App Registrations tab in Azure AD in the Azure portal,\u00a0 we can see that there is an app registration that matches exactly what we see inside Visual Studio.<\/p>\n<p><img decoding=\"async\" class=\"alignleft wp-image-5119\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/3-1024x706.png\" alt=\"\" width=\"650\" height=\"448\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/3-1024x706.png 1024w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/3-300x207.png 300w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/3-768x530.png 768w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/3.png 1282w\" sizes=\"(max-width: 650px) 100vw, 650px\" \/><\/p>\n<p>Without writing a single line of code, our Blazor app will prompt users for a login before accessing any page. We can quickly test this by launching the app in Visual Studio. The first time we access the site, we will also be prompted to consent to the (default) permissions required by the app (i.e to read the user\u2019s profile)<\/p>\n<p><img decoding=\"async\" class=\"alignleft size-full wp-image-5120\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/4.png\" alt=\"\" width=\"439\" height=\"460\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/4.png 439w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/4-286x300.png 286w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/4-24x24.png 24w\" sizes=\"(max-width: 439px) 100vw, 439px\" \/><img decoding=\"async\" class=\"alignleft wp-image-5121\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/5-1024x208.png\" alt=\"\" width=\"650\" height=\"132\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/5-1024x208.png 1024w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/5-300x61.png 300w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/5-768x156.png 768w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/5.png 1428w\" sizes=\"(max-width: 650px) 100vw, 650px\" \/><\/p>\n<p>At this point you may assume that our task is done, but there are a couple of gotchas. Firstly, the code that the default template is using is older and for this reason it also defaults to the v1 Azure AD endpoints. If you want to know why you should be using the Microsoft identity platform and the v2 endpoint, then be sure to review our Microsoft identity platform <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/develop\/\">documentation<\/a>.<\/p>\n<h3>Modernizing authentication with Microsoft.Identity.Web<\/h3>\n<p>At <a href=\"https:\/\/techcommunity.microsoft.com\/t5\/azure-active-directory-identity\/build-2020-fostering-a-secure-and-trustworthy-app-ecosystem-for\/ba-p\/1257360\">Build 2020<\/a> we announced a new authentication and token management library for ASP.NET Core 3.1 (and above) apps. The new library does a great job in abstracting a lot of the complexities and allowing developers to quickly implement authentication within a few lines of code. However, the biggest benefit is that since this library is built on top of MSAL, you don\u2019t need two separate libraries to authenticate first and then acquire tokens for speaking to back-end APIs. So, let\u2019s see what it takes to migrate to the latest library.<\/p>\n<p>NOTE: Microsoft.Identity.Web is still in preview with the GA coming soon.<\/p>\n<p>First, we need to download the new NuGet packages:<\/p>\n<ul>\n<li>Identity.Web (0.2.2 preview)<\/li>\n<li>Identity.Web.UI (0.2.2 preview)<\/li>\n<\/ul>\n<p><img decoding=\"async\" class=\"alignleft wp-image-5122\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/6-1024x169.png\" alt=\"\" width=\"650\" height=\"107\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/6-1024x169.png 1024w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/6-300x49.png 300w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/6-768x127.png 768w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/6.png 1177w\" sizes=\"(max-width: 650px) 100vw, 650px\" \/><\/p>\n<p><img decoding=\"async\" class=\"alignleft wp-image-5123\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/7-1024x170.png\" alt=\"\" width=\"650\" height=\"108\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/7-1024x170.png 1024w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/7-300x50.png 300w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/7-768x128.png 768w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/7.png 1208w\" sizes=\"(max-width: 650px) 100vw, 650px\" \/><\/p>\n<p>Next, we need to do a couple small code changes to swap out the old authentication code and plug-in the new code.<\/p>\n<p>Open startup.cs and replace the following code<\/p>\n<pre class=\"lang: decode:true\">services.AddAuthentication(AzureADDefaults.AuthenticationScheme)\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 .AddAzureAD(options =&gt; Configuration.Bind(\"AzureAd\", options));\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 services.AddControllersWithViews(options =&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 var policy = new AuthorizationPolicyBuilder()\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 .RequireAuthenticatedUser()\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 .Build();\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 options.Filters.Add(new AuthorizeFilter(policy));\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 });<\/pre>\n<p>With<\/p>\n<pre class=\"lang: decode:true\">services.AddMicrosoftWebAppAuthentication(Configuration);\nservices.AddControllersWithViews(options =&gt;\n{\n\u00a0\u00a0 var policy = new AuthorizationPolicyBuilder()\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0.RequireAuthenticatedUser()\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 .Build();\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 options.Filters.Add(new AuthorizeFilter(policy));\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}).AddMicrosoftIdentityUI();<\/pre>\n<p>Finally we need to ensure that our app can use the right v2 endpoints for signin and signout. Open the LoginDisplay.razor and update the code as per below:<\/p>\n<pre class=\"lang: decode:true\">&lt;AuthorizeView&gt;\n\u00a0\u00a0\u00a0 &lt;Authorized&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Hello, @context.User.Identity.Name!\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;a href=\"MicrosoftIdentity\/Account\/SignOut\"&gt;Log out&lt;\/a&gt;\n\u00a0\u00a0\u00a0 &lt;\/Authorized&gt;\n\u00a0\u00a0\u00a0 &lt;NotAuthorized&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;a href=\"MicrosoftIdentity\/Account\/SignIn\"&gt;Log in&lt;\/a&gt;\n\u00a0\u00a0\u00a0 &lt;\/NotAuthorized&gt;\n&lt;\/AuthorizeView&gt;<\/pre>\n<p>You will notice that there are no dedicated pages for sign-in and sign out. Instead, they are built into the Microsoft.Identity.Web dll. So as long as we update the Area to be \u201cMicrosoftIdentity\u201d, no other change is needed.<\/p>\n<p>When we launch the app from Visual Studio again, we get to experience the new login capabilities available in v2 such as passwordless and multi-factor authentication. What\u2019s great is that these features are designed to work without any code changes; as long as an Azure AD admin has configured these settings, all users can benefit from strong security.<\/p>\n<p><img decoding=\"async\" class=\"alignleft size-full wp-image-5124\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/8.png\" alt=\"\" width=\"436\" height=\"447\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/8.png 436w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/8-293x300.png 293w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/8-24x24.png 24w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/8-48x48.png 48w\" sizes=\"(max-width: 436px) 100vw, 436px\" \/><\/p>\n<p>As you can see, with a couple of lines of code, we were able to leverage the Microsoft.Identity.Web library to authenticate against Azure AD.<\/p>\n<h3>Retrieving data from Microsoft Graph<\/h3>\n<p>Microsoft Graph offers a wide range of APIs to allow you to build rich and immersive apps with the data your users own. In the steps below, we are going to pull user\u2019s emails and display them within the app. To achieve this, we first need to extend the app registration permissions in Azure AD to add access to the email data and then we need to add some code in our Blazor app to retrieve and display this data in one of our pages.<\/p>\n<p><strong>To the Azure AD portal! <\/strong><\/p>\n<p>Find the app registration and go to API Permissions. Select Add New Permission and then select Graph API. From there, we want to select Delegated Permissions and select the \u201cMail.Read\u201d permission.<\/p>\n<p><img decoding=\"async\" class=\"alignleft size-full wp-image-5125\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/9.gif\" alt=\"\" width=\"1696\" height=\"1078\" \/><\/p>\n<p>We also need to create a User Secret since our app will need a way to validate the token and retrieve the data without any user interaction. Within the same app registration, open the Certificates &amp; Secrets tab and create a new secret that never expires as it\u2019s shown below:<\/p>\n<p><img decoding=\"async\" class=\"alignleft size-full wp-image-5126\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/10.gif\" alt=\"\" width=\"1758\" height=\"980\" \/><\/p>\n<p>Make sure to copy the password as you can\u2019t access it again once you navigate away from this tab\/page. However, you can always recreate it as needed \u2013 it\u2019s easy and free \ud83d\ude0a<\/p>\n<p>Back to our Blazor app, in Visual Studio, we first need to add the client secret in appsettings.json. Inside the AzureAD config section, we must add the following line:<\/p>\n<pre class=\"lang: decode:true \">\u201cClientSecret\u201d: \u201c&lt;your secret&gt;\u201d<\/pre>\n<p>In the startup.cs class, we need to update our code to ensure that it fetches the appropriate token with the right permissions and stored it in a cache so that we can use it later in the app when we make the call to Microsoft Graph. We will also add HttpClient to our services pipeline to allow us to efficiently make HTTP calls to Microsoft Graph later:<\/p>\n<pre class=\"lang: decode:true\">services.AddMicrosoftWebAppAuthentication(Configuration)\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 .AddMicrosoftWebAppCallsWebApi(Configuration, new string[] { \"User.Read\", \"Mail.Read\" })\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 .AddInMemoryTokenCaches();\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 services.AddHttpClient();<\/pre>\n<p>Next, we need to update the code in the FetchData.razor page to retrieve our email data instead of the default (random) weather details. The following code contains all we need to retrieve and populate the page with our email data.<\/p>\n<pre class=\"lang: decode:true\">@page \"\/fetchdata\"\n\n@inject IHttpClientFactory HttpClientFactory\n@inject Microsoft.Identity.Web.ITokenAcquisition TokenAcquisitionService\n\n&lt;h1&gt;Weather forecast&lt;\/h1&gt;\n\n&lt;p&gt;This component demonstrates fetching data from a service.&lt;\/p&gt;\n\n@if (messages == null)\n{\n\u00a0\u00a0\u00a0 &lt;p&gt;&lt;em&gt;Loading...&lt;\/em&gt;&lt;\/p&gt;\n}\nelse\n{\n\u00a0\u00a0\u00a0 &lt;h1&gt;Hello @userDisplayName !!!!&lt;\/h1&gt;\n\n\u00a0\u00a0\u00a0 &lt;table class=\"table\"&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;thead&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;tr&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;th&gt;Subject&lt;\/th&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;th&gt;Sender&lt;\/th&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;th&gt;Received Time&lt;\/th&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/tr&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/thead&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;tbody&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @foreach (var mail in messages)\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;tr&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;td&gt;@mail.Subject&lt;\/td&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;td&gt;@mail.Sender&lt;\/td&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;td&gt;@mail.ReceivedTime&lt;\/td&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/tr&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/tbody&gt;\n\u00a0\u00a0\u00a0 &lt;\/table&gt;\n}\n@code {\n\n\u00a0\u00a0\u00a0 private string userDisplayName;\n\u00a0\u00a0\u00a0 private List&lt;MailMessage&gt; messages = new List&lt;MailMessage&gt;();\n\n\u00a0\u00a0\u00a0 private HttpClient _httpClient;\n\n\u00a0\u00a0\u00a0 protected override async Task OnInitializedAsync()\n\u00a0\u00a0\u00a0 {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 _httpClient = HttpClientFactory.CreateClient();\n\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ get a token\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 var token = await TokenAcquisitionService.GetAccessTokenForUserAsync(new string[] { \"User.Read\", \"Mail.Read\" });\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ make API call\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(\"Bearer\", token);\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 var dataRequest = await _httpClient.GetAsync(\"https:\/\/graph.microsoft.com\/beta\/me\");\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (dataRequest.IsSuccessStatusCode)\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 var userData = System.Text.Json.JsonDocument.Parse(await dataRequest.Content.ReadAsStreamAsync());\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 userDisplayName = userData.RootElement.GetProperty(\"displayName\").GetString();\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\n\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 var mailRequest = await _httpClient.GetAsync(\"https:\/\/graph.microsoft.com\/beta\/me\/messages?$select=subject,receivedDateTime,sender&amp;$top=10\");\n\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (mailRequest.IsSuccessStatusCode)\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 var mailData = System.Text.Json.JsonDocument.Parse(await mailRequest.Content.ReadAsStreamAsync());\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 var messagesArray = mailData.RootElement.GetProperty(\"value\").EnumerateArray();\n\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 foreach (var m in messagesArray)\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 var message = new MailMessage();\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 message.Subject = m.GetProperty(\"subject\").GetString();\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 message.Sender = m.GetProperty(\"sender\").GetProperty(\"emailAddress\").GetProperty(\"address\").GetString();\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 message.ReceivedTime = m.GetProperty(\"receivedDateTime\").GetDateTime();\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 messages.Add(message);\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\n\u00a0\u00a0\u00a0 }\n\n\n\u00a0\u00a0\u00a0 public class MailMessage\n\u00a0\u00a0\u00a0 {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 public string Subject;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 public string Sender;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 public DateTime ReceivedTime;\n\u00a0\u00a0\u00a0 }\n}<\/pre>\n<p>Let\u2019s launch our app and ensure that we are logged out first as the current token won\u2019t have the right permissions and we\u2019ve changed quite a few things in our code. You\u2019ll notice that the next time we log in, we are prompted for the newly added permissions which means that everything is working as expected. Now, beyond basic user profile data, the app is requesting access to our email data.<\/p>\n<p><img decoding=\"async\" class=\"alignleft size-full wp-image-5127\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/11.png\" alt=\"\" width=\"438\" height=\"585\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/11.png 438w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/11-225x300.png 225w\" sizes=\"(max-width: 438px) 100vw, 438px\" \/><\/p>\n<p>After granting consent we can navigate to the \u201cFetch Data\u201d page to read some emails!<\/p>\n<p><img decoding=\"async\" class=\"alignleft wp-image-5128\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/12-1024x520.png\" alt=\"\" width=\"650\" height=\"330\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/12-1024x520.png 1024w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/12-300x152.png 300w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/12-768x390.png 768w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2020\/07\/12.png 1669w\" sizes=\"(max-width: 650px) 100vw, 650px\" \/><\/p>\n<p>If you followed the steps correctly, you should now be able to see your email data as per the image above.<\/p>\n<h3>Summary<\/h3>\n<p>The new Microsoft.Identity.Web does a great job in simplifying authentication and token management and you should start using it today. Before wrapping up, a couple of things worth highlighting:<\/p>\n<ol>\n<li>Unlike normal web apps that can support dynamic\/incremental consent, in Blazor you need to request all the scopes necessary for your application up front. Failing to do so will cause an error as the TokenAcquisition method will be unable to validate the token against the new permissions. This is part of the Blazor mechanics so something that keen in mind when creating your apps.<\/li>\n<li>Instead of hand rolling your Microsoft Graph HTTP requests, you should leverage the Microsoft Graph SDK which simplifies the interaction with Microsoft Graph and provides all the data objects you\u2019ll need to serialize and deserialize from. However, in this instance we went with option 1 because we only make one call to Microsoft Graph.<\/li>\n<\/ol>\n<p>Finally, you can find the full source code and a working example of this Blazor application on <a href=\"https:\/\/github.com\/jpda\/msiddev-blazor-aad-graph\">GitHub<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This step by step guide will help you build a Blazor web app with Azure Active Directory authentication and Microsoft Graph.<\/p>\n","protected":false},"author":69081,"featured_media":25159,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[3,5],"tags":[22],"class_list":["post-5116","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-microsoft-graph","category-microsoft-identity-platform","tag-azure-ad"],"acf":[],"blog_post_summary":"<p>This step by step guide will help you build a Blazor web app with Azure Active Directory authentication and Microsoft Graph.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/posts\/5116","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/users\/69081"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/comments?post=5116"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/posts\/5116\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/media\/25159"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/media?parent=5116"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/categories?post=5116"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/tags?post=5116"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}