locked
Configuring a Blazor Server app with Azure AD authentication on an on premises load balanced environment? RRS feed

  • Question

  • User1329658757 posted

    I have an intranet Blazor Server application created using the Visual Studio template with the Work or School Accounts authentication option. Everything was working beautifully when running on my local machine and when the app was published to our development environment. However, once I moved the app to our staging environment, the application would sometimes crash after authenticating the user in Azure.

    After troubleshooting the issue, I believe the problem to be that our on-premises staging environment is load balanced (mimicking production). Our dev environment is not load balanced. I think what was occurring was that once authenticated in Azure and redirected back to the application, the user doesn't always land on the same server due to the load balancer. This breaks the Signal-R circuit and caused the application to crash. This also explains why the error was random; happening maybe 2 out of every 10 logon attempts. To test this, I removed Azure AD authentication from the application and allowed anonymous access to every page. The crashes stopped.

    My question is if anyone knows of any workaround to get Blazor Server with Azure AD authentication working with an on-premises load balancer. I searched all over the web and the only workaround I found was to use sticky sessions with Azure Signal R service. We are not hosting apps on the cloud yet. Is switching to Blazor Webassembly the only option if I want to use Blazor with authentication in my environment? Someone at work suggested switching the application to use our on premises ADFS server. However, wouldn't that encounter the same issue?

    For reference, here is the code in startup.cs ConfigureServices method that sets up the Azure authentication in the application:

    services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
                .AddAzureAD(options => Configuration.Bind("AzureAd", options));
    
    
            services.AddControllersWithViews(options =>
            {
                var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
                
    
                options.Filters.Add(new AuthorizeFilter(policy));
            });

    Friday, March 5, 2021 1:08 AM

All replies

  • User1329658757 posted
    0
    <button class="js-vote-down-btn grid--cell s-btn s-btn__unset c-pointer" data-controller="s-tooltip" data-s-tooltip-placement="right" aria-pressed="false" aria-label="Down vote" data-selected-classes="fc-theme-primary" aria-describedby="--stacks-s-tooltip-t2rl0n68"><svg aria-hidden="true" class="m0 svg-icon iconArrowDownLg" width="36" height="36" viewbox="0 0 36 36"><path d="M2 10h32L18 26 2 10z"></path></svg></button><button class="js-accept-answer-btn grid--cell s-btn s-btn__unset c-pointer" aria-pressed="false" data-selected-classes="fc-green-500" data-title-accept="Accept this answer if it solved your problem or was the most helpful in finding your solution" data-title-unaccept="You accepted this answer (select to undo)" aria-label="Accept answer" data-s-tooltip-placement="right" data-controller="null s-tooltip" aria-describedby="--stacks-s-tooltip-4cyocrn7"><svg aria-hidden="true" class="m0 svg-icon iconCheckmarkLg" width="36" height="36" viewbox="0 0 36 36"><path d="M6 14l8 8L30 6v8L14 30l-8-8v-8z"></path></svg></button><svg aria-hidden="true" class="mln2 mr0 svg-icon iconHistory" width="19" height="18" viewbox="0 0 19 18"><path d="M3 9a8 8 0 113.73 6.77L8.2 14.3A6 6 0 105 9l3.01-.01-4 4-4-4h3L3 9zm7-4h1.01L11 9.36l3.22 2.1-.6.93L10 10V5z"></path></svg>

    I found the solution to this and am posting it here in case anyone else is facing a similar issue.

    It turns out the problem wasn't SignalR or anything specific to Blazor Server. After enabling the developer exception page on the load balanced environment, I saw that the error was "Unable to unprotect the message.State". The application state is encrypted by middleware before the user is authenticated by Azure AD. When Azure AD posts back, it includes that encrypted state which is then in turn decrypted on the client side by the middleware.

    The key needed to decrypt is by stored on the web server. When in a load balanced environment, if you land on a different server than where you started, the middleware will then be attempting to decrypt state with the wrong key. This of course results in an error.

    To fix this you have to store the keys on a central location like a file share instead of on the server itself. Implementing the fix is actually simple. Include the following line in ConfigureServices in startup.cs:

    services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"));

    There are also options to store keys on Azure if that is preferred.

    This post by Kevin Dockx is what finally gave me the answer:

    https://www.kevindockx.com/solving-correlation-failed-state-property-not-found-errors-openid-connect-middleware-asp-net-core/

    Friday, March 19, 2021 12:59 PM