简体   繁体   中英

User.Claims is empty for every page outside of Areas/Identity

Authorization doesn't work on Razor pages outside of Areas/Identity, cannot figure out why:

Steps to reproduce:

  • Fresh app. ASP.NET Core Blazor WASM Hosted

  • Create page: Areas/Identity/Index2.cshtml

  • Create page: Areas/NewArea/NewPage.cshtml

  • Register and Log in.

  • Access Index2 and NewPage and break in OnGet method while debugging.

    • Index2 : property User.Claims contains multiple claims .
    • NewPage : property User.Claims is empty

Why the authorization is not "propagated" into NewPage ?

When I set @page "/Identity/NewPage" it works so it is somehow related to Identity Area (??).

EDITS:

(Placing page into Pages folder of the server project results the same (no auth))

Reason why do I care: For quick loading speed I want my index page (Index.cshtml) to be razor page (not Blazor wasm). I need to display login status on index page.

More research: I have found exactly the same (unresolved, but closed) issue in aspnetcore repo. https://github.com/dotnet/aspnetcore/issues/34080

This issue describe the same scenario I have: Authorise normal Razor Pages in a Blazor WebAssemby App? and there is also a solution:

services.AddAuthentication(options =>
{
   options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
    options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
})

Another same issue on SO: Blazor WebAssembly with server side Razor page authorize problem

But I still don't understand why is this happening.

EDIT - Solution

The solution with changing DefaultAuthenticateScheme didn't work well after all - it break authorization for api controllers and wasm app. This may be caused by my setup - I am not actually using server app for "hosting" the Blazor. Blazor app is just SPA using server app for auth and api.

Anyway: Instead of changing default schema for "everything" I change it only for particular .cshtml pages:

[AllowAnonymous]
[Authorize(AuthenticationSchemes = "Identity.Application")]

With that ↑ everything works. But you have to paste this into every .cshtml page... Or - you can configure it in Program.cs :

//add policy which uses ApplicationScheme
services.AddAuthorization(config =>
{
    config.AddPolicy("RazorPagesAuthPolicy",
            policyBuilder =>
            {
policyBuilder.AddAuthenticationSchemes(IdentityConstants.ApplicationScheme);
policyBuilder.RequireAssertion(context => true);//has to be here
            });
}

//...
services.AddAuthentication()//keep this
.AddIdentityServerJwt();
//...

services.AddRazorPages(cfg =>
{
    cfg.Conventions.AddFolderApplicationModelConvention("/", mo =>
    {//add authorize and allowAnonymous filters
        mo.Filters.Add(new AuthorizeFilter("RazorPagesAuthPolicy"));
        mo.Filters.Add(new AllowAnonymousFilter());
    }
        );
}
);

which I am not sure if it is way to go

As I've previously mentioned, you can either use cookies authentication or Jwt authentication in order to access the ClaimsPrincipal from the Server project. The solution proposed is fine, and it is used to configure the cookies middleware used by the identity system. Incidentally, it should be:

services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
                options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
            }).AddIdentityServerJwt();

But I still don't understand why is this happening

Do you mean why the code above works ?

The code above works because it configures the cookies middleware to authenticate the user, tells the Identity System what scheme to use, and how to challenge the user. Understand this, the Server side of your app is not authenticated, unless you configure it to perform authentication, which differs from the authentication performed when you are re-directed from the WebAssembly Blazor App. I'll try to clarify this by giving you this example:

If you created a Blazor Server App with individual accounts, and then added client SignalR to enable a chat system in your app, and you want to secure your hub object with the Authorize attribute, an authenticated user to your Blazor Server App won't be able to access secured end points on the hub, unless he is authenticated on the hub as well. How to authenticate the user ? Simply by passing the application cookie to the hub. This is more or less simialr to the issue under discussion; that is, though the Identity folder and its content are authenticated (and authorized), the hosting Server App is not.

Note: Its hard to internalized this on first shot, but rest assured that the proposed code not only works but is also 100% kosher.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM