Jan 13

Written by: Michael Washington
1/13/2019 4:55 PM  RssIcon

 

Server-side Blazor (also known as Razor Components) provides options for deeper integration between the ‘client side’ and ‘server side’ code because the ‘client side’ code is processed server-side. With server-side Blazor, we end up using less code, and things are a lot less complex because we can trust that the end-user was not able to alter or hack the ‘client side’ code.

If the ‘client side’ code says a user is who they say they are, we can trust them. Otherwise, we have to always make a ‘round trip’ to the server, before permitting operations or returning sensitive data. The ‘round trip’ is still being made (because the ‘client site’ code is being generated ‘server side’), but, our code structure is now cleaner and more streamlined.

In this example, we will authenticate a person using their Microsoft Azure account (either Personal or Work/School account). While most of the code will be in the client project, the client code is running server side so we can trust that the user was not able to alter the identity that is set for them at login.

image

The user will click the Login button to start the login process.

image

The user enters their Microsoft Azure account (either Personal or Work/School account), and clicks Next.

image

Next, they enter their password and click the Sign in button.

image

They will be directed back to the application and their first name will display.

They can click the Logout button to log out.

 

Create The Application

image

Open Visual Studio 2017.

image

Select File then New then Project.

image

Select ASP.NET Core Web Application.

Name the project BlazorAD.

image

Select Server-side Blazor.

 

Enable SSL

image

To use Azure Active Directory for logging users in, the application should run using SSL (https),

To enable this, double-click on the Properties node in the server project (BlazorAD.Server).

image

  1. Select the Debug tab
  2. Click Enable SSL
  3. Click the Copy button
  4. Paste the SSL URL in the App URL box (this will start the application in SSL mode when debugging)

 

image

Click the Save button, to save the changes.

Also, save the App URL, you will need it in a later step.

 

Create The App Registration

image

To log in Azure Active Directory users, you need a App ID.

Go to : https://portal.azure.com, log in, and click Azure Active Directory, then App registrations (Preview), then New registration.

image

  1. Enter a Name for the App
  2. Select: Accounts in any organizational directory
  3. Enter the App URL you saved earlier and add signin-oidc to the end (because this will be specified as the CallbackPath in the appsettings.json file we will add in a later step)
  4. Click Register

image

The Overview page will display.

Copy and save the Application (Client) ID.

You will need it later.

image

Click on the Authentication node, enable ID tokens, and click Save.

 

Add Nuget Packages

image

Return to Visual Studio.

In the Solution Explorer, right-click on the solution node and select Manage NuGet Packages for Solution.

image

Add a reference to:

Microsoft.AspNetCore.Authentication.AzureAD.UI (must be 2.1.1 or higher)

This NuGet package will install a series of assemblies that will provide all the code needed for authentication.

In addition, when properly configured, in the remaining steps, it will create a virtual controller that will provide the functionality to sign users in and out of Azure Active Directory.

This eliminates a lot of code that you would otherwise have to write yourself.

 

Configure the Authentication Pipeline

image

Add a appsettings.json file to the server project (BlazorAD.Server), using the following code:

 

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/common",
    "ClientId": "{{{ YOUR APP ID }}}",
    "CallbackPath": "/signin-oidc"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}
Replace  {{{ YOUR APP ID }}} with the Application (Client) ID you saved earlier.

 

image

In the server project (BlazorAD.Server), open the Startup.cs file.

Add the following using statements to the top of the file:

 

using Microsoft.AspNetCore.Authentication.AzureAD.UI;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System.Net.Http;
using Microsoft.AspNetCore.Authentication;
using Microsoft.IdentityModel.Tokens;
using System.Threading.Tasks;

 

Add the following to the Startup class:

 

        // To hold the values from the appsettings.json file
        public IConfiguration Configuration { get; }
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

 

Add the following to the bottom of public void ConfigureServices(IServiceCollection services) method:

 

       // ***********************************************
        // For more info on:
        // Microsoft.AspNetCore.Authentication.AzureAD.UI
        // see:
        // https://bit.ly/2Fv6Zxp
        // This creates a 'virtual' controller 
        // called 'Account' in an Area called 'AzureAd' that allows the
        // 'AzureAd/Account/SignIn' and 'AzureAd/Account/SignOut'
        // links to work
        services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
            .AddAzureAD(options => Configuration.Bind("AzureAd", options));
        services.Configure<CookiePolicyOptions>(options =>
        {
            // This lambda determines whether user consent for non-essential
            // cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });
        // This configures the 'middleware' pipeline
        // This is where code to determine what happens
        // when a person logs in is configured and processed
        services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                // Instead of using the default validation 
                // (validating against a single issuer value, as we do in
                // line of business apps), we inject our own multitenant validation logic
                ValidateIssuer = false,
                // If the app is meant to be accessed by entire organizations, 
                // add your issuer validation logic here.
                //IssuerValidator = (issuer, securityToken, validationParameters) => {
                //    if (myIssuerValidationLogic(issuer)) return issuer;
                //}
            };
            options.Events = new OpenIdConnectEvents
            {
                OnTicketReceived = context =>
                {
                    // If your authentication logic is based on users 
                    // then add your logic here
                    return Task.CompletedTask;
                },
                OnAuthenticationFailed = context =>
                {
                    context.Response.Redirect("/Error");
                    context.HandleResponse(); // Suppress the exception
                    return Task.CompletedTask;
                },
                OnSignedOutCallbackRedirect = context =>
                {
                    // This is called when a user logs out
                    // redirect them back to the main page
                    context.Response.Redirect("/");
                    context.HandleResponse();
                    return Task.CompletedTask;
                },
                // If your application needs to do authenticate single users, 
                // add your user validation below.
                //OnTokenValidated = context =>
                //{
                //    return myUserValidationLogic(context.Ticket.Principal);
                //}
            };
        });
        services.AddMvc(options => { })
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        // From: https://github.com/aspnet/Blazor/issues/1554
        // Adds HttpContextAccessor
        // Used to determine if a user is logged in
        // and what their username is
        services.AddHttpContextAccessor();
        services.AddScoped<HttpContextAccessor>();
        // Required for HttpClient support in the Blazor Client project
        services.AddHttpClient();
        services.AddScoped<HttpClient>();

 

Add the following to the bottom of the public void Configure(IApplicationBuilder app, IHostingEnvironment env) method:

 

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();
            app.UseAuthentication();
            app.UseMvc(routes =>
            {
                // Allows Blazor Client project code to call Blazor
                // Server project pages
                routes.MapRoute(name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });

 

image

Save the file.

Rebuild the Solution.

It should build without errors.

 

Create The Login / Logout Control

image

In the client project (BlazorAD.App), create a LoginControl.cshtml file using the following code:

 

@using System.Security.Claims
@using Microsoft.AspNetCore.Http
@using BlazorAD.App.Shared
@page "/login"
@inject IHttpContextAccessor _httpContextAccessor
@inject HttpClient Http
<!--
The Microsoft.AspNetCore.Authentication.AzureAD.UI
NuGet package creates a 'virtual' controller that
contains code that allows the
 /Account/signin and /Account/signout
 links to work
-->
@if (User.Identity.Name != null)
{
    <b>You are logged in as: @GivenName</b>
    <a class="ml-md-auto btn btn-primary"
       href="/AzureAD/Account/SignOut?post_logout_redirect_uri=@RedirectUri"
       target="_top">Logout</a>
}
else
{
    <a class="ml-md-auto btn btn-primary"
       href="/AzureAD/Account/SignIn?redirectUri=@RedirectUri"
       target="_top">Login</a>
}
@functions {
    private ClaimsPrincipal User;
    private string RedirectUri;
    private string GivenName;
    protected override void OnInit()
    {
        base.OnInit();
        try
        {
            // Set the user to determine if they are logged in
            User = _httpContextAccessor.HttpContext.User;
            // Try to get the GivenName
            var givenName =
                _httpContextAccessor.HttpContext.User
                .FindFirst(ClaimTypes.GivenName);
            if (givenName != null)
            {
                GivenName = givenName.Value;
            }
            else
            {
                GivenName = User.Identity.Name;
            }
            // Need to determine where we are to set the RedirectUri
            RedirectUri = 
                _httpContextAccessor.HttpContext.Request.Host.Value;
        }
        catch { }
    }
}

 

Consume the Login Control

image

Open the MainLayout.cshtml file, and change all of the code to the following:

 

@inherits BlazorLayoutComponent
<div class="sidebar">
    <NavMenu />
</div>
<div class="main">
    <div class="top-row px-4">
        <!-- ******************************** -->
        <LoginControl />
        <!-- ******************************** -->
    </div>
    <div class="content px-4">
        @Body
    </div>
</div>

 

image

When you run the project, you can now log in using Azure Active Directory.

 

Links

Blazor.net

Authentication for serverside Blazor (How to use IHttpContextAccessor)

Blazor Security/Authorization

A Demonstration of Simple Server-side Blazor Cookie Authentication

active-directory-dotnet-webapp-openidconnect-v2

Quickstart: Add sign-in with Microsoft to an ASP.NET Core web app

 

Download

The project is available at http://lightswitchhelpwebsite.com/Downloads.aspx

You must have Visual Studio 2017 (or higher) installed to run the code.

13 comment(s) so far...


Gravatar

Re: Azure Active Directory Authentication in Server Side Blazor

Hey Michael, nice article. This may be a dumb question but how do you establish authorization? I didn't see anything about roles.

Thanks

By Dave Vorgang on   1/23/2019 10:36 AM
Gravatar

Re: Azure Active Directory Authentication in Server Side Blazor

@Dave Vorgang - You basically just call a method from the Microsoft.AspNetCore.Authentication.AzureAD.UI assembly, passing in your Azure ClientID and it handles the login for you. You can access any roles the user has in their account you created for them in Azure Active Directory. See: https://bit.ly/2Fv6Zxp for a full explanation of what the Microsoft.AspNetCore.Authentication.AzureAD.UI assembly is doing 'behind the scenes'

By Michael Washington on   1/23/2019 10:41 AM
Gravatar

Re: Azure Active Directory Authentication in Server Side Blazor

Hi Michael,I have been trying to follow this to add AzureAD authentication to my Razor Components project (ASP.NET Core 3.0 Preview 2).I am stuck on...

By Ondrej on   2/15/2019 9:21 AM
Gravatar

Re: Azure Active Directory Authentication in Server Side Blazor

@Ondrej - Sorry this example was for a previous version of Blazor than the one your are using. All I can suggest is to download the sample code from the download page on this site and compare that code to your own.

By Michael Washington on   2/15/2019 9:25 AM
Gravatar

Re: Azure Active Directory Authentication in Server Side Blazor

I am using latest preview version of Blazor and AzureAd.UI. Authentication is working as expected. But the HttpContextAccessor in the Client project is coming as null. The HttpContext is not propagating to the client from the server.

Server Side app is working fine and not the client app. Other components on the client app are working as expected such as Counter and FetchData

I am using VS 2019
Blazor Version - 0.9.0-preview3-19154-02
Microsoft.AspNetCore.Authentication.AzureAD.UI - 2.1.1

By premchand on   3/22/2019 5:15 PM
Gravatar

Re: Azure Active Directory Authentication in Server Side Blazor

@premchand - My example is for Razor Components only (server side Blazor). Also, it was for an earlier version of even that and may now be 'broken'.

By Michael Washington on   3/22/2019 5:16 PM
Gravatar

Re: Azure Active Directory Authentication in Server Side Blazor

Michael, this is a great article. Could you please update it for the latest .Net Core Preview 3 version?

By Andy C on   3/27/2019 9:09 AM
Gravatar

Re: Azure Active Directory Authentication in Server Side Blazor

@Andy C - At this stage Microsoft is still making 'changes' to the relevant code, so I want to wait until they are done before revisiting this sample. Thanks.

By Michael Washington on   3/27/2019 9:11 AM
Gravatar

Re: Azure Active Directory Authentication in Server Side Blazor

Great article Michael;

Now that we are in Preview, I'd love to see a new blog to user the server side.
Can't wait.
Thanks!
..Ben

By Ben Hayat on   4/19/2019 3:55 PM
Gravatar

Re: Azure Active Directory Authentication in Server Side Blazor

Am I correct in assuming the the current preview 4 of .Net Core does not function correctly with code outlined in you post?
Is this something that we will have to wait for the next preview?
Thanks,
Great post.

By Mark M on   4/30/2019 2:23 PM
Gravatar

Re: Azure Active Directory Authentication in Server Side Blazor

@Mark M - Yeah they broke this sample big time when Blazor preview 4 came out! :) To see where they are going with authentication see: https://twitter.com/ADefWebserver/status/1122504410557554689

By Michael Washington on   4/30/2019 2:25 PM
Gravatar

Re: Azure Active Directory Authentication in Server Side Blazor

How do I access ClaimsPrincipal in Blazor Server side application using AddAzureAD.
In RazorPages the ClaimPrinicple was part of Page.
I have AzureAD working, I just need to know hot to access the ClaimsPrinciple for the logged on User.

Thanks for any help.
Mark

By Mark McPherson on   5/4/2019 3:52 PM
Gravatar

Re: Azure Active Directory Authentication in Server Side Blazor

@Mark McPherson - You will want to wait until the Microsoft team has released preview 6 before trying to make any auth code work.

By Michael Washington on   5/4/2019 3:55 PM

Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
CAPTCHA image
Enter the code shown above in the box below
Add Comment   Cancel 
Microsoft Visual Studio is a registered trademark of Microsoft Corporation / LightSwitch is a registered trademark of Microsoft Corporation