10

I have an existing Blazor (Server) app addressing .NET Core 3.1 preview 2.

I need to retrospectively add on-prem ADFS (not Azure) security. I've been trying to follow Microsoft's Authenticate users with WS-Federation in ASP.NET Core and it's stubbornly ignoring the security. The article is of course written for ASP.NET, not Blazor...

What I've done so far is:

public static void ConfigureServices(IServiceCollection services)
{
    services.AddIdentity<IdentityUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddAuthentication()
        .AddWsFederation(options =>
        {
            options.MetadataAddress = "https://adfs.Server.com/FederationMetadata/2007-06/FederationMetadata.xml";
            options.Wtrealm = "https://localhost:44323/";
        });

    services.AddAuthorization();

    services.AddRazorPages();
    services.AddServerSideBlazor();

    ....

One thing of concern - the DB currently has tables in it supporting an earlier authentication pattern (membership?) (used by the application we're re-writing). It has the tables [AspNetRoles] [AspNetUserClaims] [AspNetUserLogins] [AspNetUserRoles] and [AspNetUsers]. Will any of this get overwritten?

public class ApplicationDbContext : IdentityDbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }    

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (optionsBuilder != null)
        {
            if (optionsBuilder.IsConfigured == false)
            {
                IConfigurationRoot configuration = new ConfigurationBuilder()
                    .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
                    .AddJsonFile($"appsettings.{Startup.CurrentEnvironment}.json")
                    .Build();

                optionsBuilder
                    .UseSqlServer(configuration.GetConnectionString("MyDatabase"), 
                           providerOptions => providerOptions.CommandTimeout(60));
            }
        }

        base.OnConfiguring(optionsBuilder);
    }
}

In the Configure method, I've added (though I'm not clear on whether I needed to):

app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();

In the App.razor, I have:

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly">
        <Found Context="routeData">
            @*<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />*@
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />>
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>

And in one of my razor pages (MyPage.razor), I've got:

@page "/myRoute"    
@attribute [Authorize]

When I browse to the page with the Autorize attribute, I get the message:

Not authorized

So it's not calling out to my ADFS server. Shouldn't it just do this automatically - the user shouldn't have to click a "log me in" button.

I've referenced the following NuGet packages:

<PackageReference Include="Microsoft.AspNetCore.Authentication.WsFederation" Version="3.1.0-preview2.19528.8" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.0-preview2.19528.8" />

I have even followed Microsoft's example for Use WS-Federation without ASP.NET Core Identity, but with no luck.

DrGriff
  • 4,394
  • 9
  • 43
  • 92

1 Answers1

4

This I an older post, but still pops up first in Google for me so..

I had more or less arrived at the same issue. The OP's post in this thread helped me:

Blazor - Securing using ADFS with local DB repository: how/when to hook into SQL

More specifically, the middleware added to Configure() method made a difference. I ended up with this in my solution:

app.UseAuthentication();
app.UseAuthorization();

app.Use(async (context, next) =>
{
    ClaimsPrincipal user = context.User;

    if (!user.Identities.Any(x => x.IsAuthenticated))
    {
       await context.ChallengeAsync(WsFederationDefaults.AuthenticationScheme).ConfigureAwait(false);
    }

    if (next != null)
    {
       await next().ConfigureAwait(false);
    }
});            
Dalton Cézane
  • 3,672
  • 2
  • 35
  • 60
Alistair
  • 41
  • 2
  • 1
    This concept worked great. To make it work with latest Blazor version as of today, I had to do _ChallengeRequest_ like this *await Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.ChallengeAsync(context, WsFederationDefaults.AuthenticationScheme).ConfigureAwait(false);* – vvvv4d Apr 15 '22 at 21:46