Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ICurrentUser.IsAuthenticated = false on Blazor WASM after upgrade to 8.2 RC3 #19965

Closed
1 task done
gdunit opened this issue Jun 2, 2024 · 9 comments
Closed
1 task done
Labels

Comments

@gdunit
Copy link

gdunit commented Jun 2, 2024

Is there an existing issue for this?

  • I have searched the existing issues

Description

Following upgrade of my Blazor WASM solution from 8.0 to 8.2 RC3, I am encountering an issue with authentication.

I am using subdomain tenant resolution, including on the Blazor WASM app.

I am logging into a tenant (also tried using the host) via the admin account.
I can login just fine, but after login and the redirect back to Blazor, the ICurrentUser's IsAuthenticated is set to false and any post-auth content accordingly cannot be viewed.

In app.razor:

@context.User.Identity?.IsAuthenticated = returns true (context refers to AuthenticationState)
Also, claims can be printed out for the user.

@currentUser.IsAuthenticated = returns false (as does CurrentPrincipalAccessor)
Claims for the user are empty and the user's properties are null.

I thought this might be related to #18552 but adding the line suggested to my httpapi.host project in that issue does not resolve the issue:

options.MapInboundClaims = false;

If I try the other solution in the identityserver module in preconfigureservices, I get an error:

options.UpdateAbpClaimTypes = false;

The database migrations have been applied and the normalised tenancy names applied to existing tenants per the migration guide. DBMigrator has been run after upgrading.

The AuthServer site's page shows the user logged in as expected.

Is there anything else which has changed between 8.0 and 8.2 RC3 which I should check?

Reproduction Steps

No response

Expected behavior

No response

Actual behavior

No response

Regression?

No response

Known Workarounds

No response

Version

8.2 RC3

User Interface

Blazor

Database Provider

EF Core (Default)

Tiered or separate authentication server

Separate Auth Server

Operation System

Windows (Default)

Other information

No response

@gdunit gdunit added the bug label Jun 2, 2024
@younessiysa
Copy link

I am experiencing same issue with ABP version 8.1.1. Specifically, CurrentUser.IsAuthenticated always returns false.

@maliming
Copy link
Member

maliming commented Jun 3, 2024

hi

Please create a template project and compare the authentication code.

@younessiysa
Copy link

You have created a project from scratch using version 8.1.3, but I am encountering the same problem.
scren

@younessiysa
Copy link

`using GFCAPI.EntityFrameworkCore;
using GFCAPI.Localization;
using GFCAPI.MultiTenancy;
using GFCAPI.Web.Menus;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.OpenApi.Models;
using System;
using System.IO;
using System.Linq;
using Volo.Abp;
using Volo.Abp.Account.Web;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Autofac;
using Volo.Abp.AutoMapper;
using Volo.Abp.Identity.Web;
using Volo.Abp.Modularity;
using Volo.Abp.OpenIddict;
using Volo.Abp.Security.Claims;
using Volo.Abp.SettingManagement.Web;
using Volo.Abp.Swashbuckle;
using Volo.Abp.TenantManagement.Web;
using Volo.Abp.UI.Navigation;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.VirtualFileSystem;

namespace GFCAPI.Web;

[DependsOn(
typeof(GFCAPIHttpApiModule),
typeof(GFCAPIApplicationModule),
typeof(GFCAPIEntityFrameworkCoreModule),
typeof(AbpAutofacModule),
typeof(AbpIdentityWebModule),
typeof(AbpSettingManagementWebModule),
typeof(AbpAccountWebOpenIddictModule),
typeof(AbpAspNetCoreMvcUiLeptonXLiteThemeModule),
typeof(AbpTenantManagementWebModule),
typeof(AbpAspNetCoreSerilogModule),
typeof(AbpSwashbuckleModule)
)]
public class GFCAPIWebModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration();

    context.Services.PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options =>
    {
        options.AddAssemblyResource(
            typeof(GFCAPIResource),
            typeof(GFCAPIDomainModule).Assembly,
            typeof(GFCAPIDomainSharedModule).Assembly,
            typeof(GFCAPIApplicationModule).Assembly,
            typeof(GFCAPIApplicationContractsModule).Assembly,
            typeof(GFCAPIWebModule).Assembly
        );
    });

    PreConfigure<OpenIddictBuilder>(builder =>
    {
        builder.AddValidation(options =>
        {
            options.AddAudiences("GFCAPI");
            options.UseLocalServer();
            options.UseAspNetCore();
        });
    });

    if (!hostingEnvironment.IsDevelopment())
    {
        PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
        {
            options.AddDevelopmentEncryptionAndSigningCertificate = false;
        });

        PreConfigure<OpenIddictServerBuilder>(serverBuilder =>
        {
            serverBuilder.AddProductionEncryptionAndSigningCertificate("openiddict.pfx", "4d28a500-2ddd-4028-a157-374638e594b8");
        });
    }
}

public override void ConfigureServices(ServiceConfigurationContext context)
{
    var hostingEnvironment = context.Services.GetHostingEnvironment();
    var configuration = context.Services.GetConfiguration();

    ConfigureAuthentication(context, configuration);
    ConfigureUrls(configuration);
    ConfigureBundles();
    ConfigureAutoMapper();
    ConfigureVirtualFileSystem(hostingEnvironment);
    ConfigureNavigationServices();
    ConfigureAutoApiControllers();
    ConfigureSwaggerServices(context.Services);
    ConfigureCors(context, configuration);
}

private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
    //context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
    //context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
    //{
    //    options.IsDynamicClaimsEnabled = true;
    //});

    context.Services.AddAuthentication(options =>
    {
        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";
    })
        .AddCookie("Cookies", options =>
        {
            options.ExpireTimeSpan = TimeSpan.FromDays(365);
            options.CheckTokenExpiration();
        })
        .AddAbpOpenIdConnect("oidc", options =>
        {
            options.Authority = configuration["AuthServer:Authority"];
            options.RequireHttpsMetadata = configuration.GetValue<bool>("AuthServer:RequireHttpsMetadata");
            options.ResponseType = OpenIdConnectResponseType.CodeIdToken;

            options.ClientId = configuration["AuthServer:ClientId"];
            options.ClientSecret = configuration["AuthServer:ClientSecret"];

            options.UsePkce = true;
            options.SaveTokens = true;
            options.GetClaimsFromUserInfoEndpoint = true;

            options.Scope.Add("roles");
            options.Scope.Add("email");
            options.Scope.Add("phone");
            options.Scope.Add("GFCAPI");
        });


    context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
    {
        options.IsDynamicClaimsEnabled = true;
    });

}

private void ConfigureUrls(IConfiguration configuration)
{
    Configure<AppUrlOptions>(options =>
    {
        options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
    });
}

private void ConfigureBundles()
{
    Configure<AbpBundlingOptions>(options =>
    {
        options.StyleBundles.Configure(
            LeptonXLiteThemeBundles.Styles.Global,
            bundle =>
            {
                bundle.AddFiles("/global-styles.css");
            }
        );
    });
}

private void ConfigureAutoMapper()
{
    Configure<AbpAutoMapperOptions>(options =>
    {
        options.AddMaps<GFCAPIWebModule>();
    });
}

private void ConfigureVirtualFileSystem(IWebHostEnvironment hostingEnvironment)
{
    if (hostingEnvironment.IsDevelopment())
    {
        Configure<AbpVirtualFileSystemOptions>(options =>
        {
            options.FileSets.ReplaceEmbeddedByPhysical<GFCAPIDomainSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}GFCAPI.Domain.Shared"));
            options.FileSets.ReplaceEmbeddedByPhysical<GFCAPIDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}GFCAPI.Domain"));
            options.FileSets.ReplaceEmbeddedByPhysical<GFCAPIApplicationContractsModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}GFCAPI.Application.Contracts"));
            options.FileSets.ReplaceEmbeddedByPhysical<GFCAPIApplicationModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}GFCAPI.Application"));
            options.FileSets.ReplaceEmbeddedByPhysical<GFCAPIWebModule>(hostingEnvironment.ContentRootPath);
        });
    }
}

private void ConfigureNavigationServices()
{
    Configure<AbpNavigationOptions>(options =>
    {
        options.MenuContributors.Add(new GFCAPIMenuContributor());
    });
}

private void ConfigureAutoApiControllers()
{
    Configure<AbpAspNetCoreMvcOptions>(options =>
    {
        options.ConventionalControllers.Create(typeof(GFCAPIApplicationModule).Assembly);
    });
}

private void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration)
{
    context.Services.AddCors(options =>
    {
        options.AddDefaultPolicy(builder =>
        {
            builder
                .WithOrigins(
                    configuration["App:CorsOrigins"]
                        .Split(",", StringSplitOptions.RemoveEmptyEntries)
                        .Select(o => o.RemovePostFix("/"))
                        .ToArray()
                )
                .WithAbpExposedHeaders()
                .SetIsOriginAllowedToAllowWildcardSubdomains()
                .AllowAnyHeader()
                .AllowAnyMethod()
                .AllowCredentials();
        });

    });

}

private void ConfigureSwaggerServices(IServiceCollection services)
{
    services.AddAbpSwaggerGen(
        options =>
        {
            options.SwaggerDoc("v1", new OpenApiInfo { Title = "GFCAPI API", Version = "v1" });
            options.DocInclusionPredicate((docName, description) => true);
            options.CustomSchemaIds(type => type.FullName);
        }
    );
}

public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
    var app = context.GetApplicationBuilder();
    var env = context.GetEnvironment();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseAbpRequestLocalization();

    if (!env.IsDevelopment())
    {
        app.UseErrorPage();
    }

    app.UseCorrelationId();
    app.UseStaticFiles();
    app.UseRouting();
    app.UseCors();
    app.UseAuthentication();
    app.UseAbpOpenIddictValidation();

    if (MultiTenancyConsts.IsEnabled)
    {
        app.UseMultiTenancy();
    }

    app.UseUnitOfWork();
    app.UseDynamicClaims();
    app.UseAuthorization();

    app.UseSwagger();
    app.UseAbpSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/swagger/v1/swagger.json", "GFCAPI API");
    });

    app.UseAuditing();
    app.UseAbpSerilogEnrichers();
    app.UseConfiguredEndpoints();
}

}
`

@maliming
Copy link
Member

maliming commented Jun 4, 2024

@gdunit
Copy link
Author

gdunit commented Jun 10, 2024

Update: I've tried to map all of the code from the startup template for the authserver and httpapi.host modules, and the issue still exists.

I am going to try and upgrade one of my other test solutions that also has subdomain tenant resolution to see if I can create a repro that I can share.

@gdunit
Copy link
Author

gdunit commented Jun 18, 2024

Further update:

  • I have tried to re-create this on a startup template with similar setup (tiered / separate authserver, subdomain tenant resolution) and cannot.
  • I also tried to add a new Blazor-Webapp front end to my main project. This displays the principal information correctly when rendering in server mode - but then does not display the information (isAuth = false) when the render mode switches to WASM. This suggests to me that the blazor WASM code is not at fault, since it is the same problem between Blazor-webapp in WASM mode and pure WASM client.

My main solution is a lot more complex than the startup template, with additional modules, custom theme, etc. I fear this may be a case of trial and error, removing / changing components in my main solution until I find the culprit. I will report back once I do.

In the meantime are there any pointers of what kind of situation might cause this, or any areas where you would recommend looking to try and find what is happening?

@gdunit
Copy link
Author

gdunit commented Jun 18, 2024

For context here are a couple of screenshots. These are taken from the blazor-webapp showing it switching from server mode to WASM (as per the standard InteractiveAuto render mode) and illustrating the issue.

server claims

wasm claims

@gdunit
Copy link
Author

gdunit commented Jun 18, 2024

OK, finally found the issue:

Upon further inspection of the application traces, the tokens were being rejected as invalid:

"IDX14100: JWT is not well formed, there are no dots (.). The token needs to be in JWS or JWE Compact Serialization Format. (JWS)"

This is the same as reported in #19893 and dotnet/aspnetcore#52286

The reason this worked in the new solution but not in my older one was because these three packages were missing from the httpapi.host csproj file on my older, upgraded solution:

<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="7.5.1" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.5.1" /> 
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="7.5.1" />

Per the reported issue, adding these seems to overcome some problems in the way that the MS libraries validate the tokens.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants