redoc01
redoc01

Reputation: 2297

Blazor WebAssembly - How to create Policy-Based Authorization

I have been trying to add Authorization and permissions to my project.

I have managed to generate the database tables as such:

 AspNetRoleClaims
 AspNetUserClaims
 AspNetRoles
 AspNetUsers
 ApsNetUserRoles

These tables got generated using PMC, I committed these tables after the blazer web assembly template was used.

In the PMC I entered:

 update-database

Which generated those tables described above.

So when i use:

         <AuthorizeView Roles="Admin"> 
        <div class="wrapper">

            <ContentLayout Title="@_greeting">
                <Card>
                    <CardContent>
                        Hi @context.User.Identity!.Name

                    </CardContent>
                </Card>

            </ContentLayout>

        </div>

        <div>

        </div>

    </AuthorizeView>

Works great and only Admin can view the content.

Now what my problem is how do I add Policy-Based Authorisation, I have searched to find a solution but I tried examples but no luck.

What I'm trying to do is find a way of adding Policy-Based Authorization without any logic so it's built-in with this table, is this possible?

Or can someone please share how I can achieve Policy-Based Authorization?

These are the data in the tables:

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

And what is was trying for Policies:

<AuthorizeView Policy="CanBuy">
    <div>hello</div>

</AuthorizeView>

But i get error:

enter image description here

Upvotes: 3

Views: 5376

Answers (3)

Canica
Canica

Reputation: 2738

The accepted answer is NOT good practice.

Here's a better solution that follows the recommended approach for ASP.NET Core and Blazor applications:

Option 1: validating within bootstrapping code; suggest using this with simple validation only.

services.AddAuthorizationCore(options => {    
    options.AddPolicy("CanBuyPolicy", policy => 
          policy.RequireClaim("permission.canbuy", "CanBuy"));
});

OR

services.AddAuthorizationCore(options => {    
    options.AddPolicy("CanBuyPolicy", policy => 
          policy.RequireRole("admin", "user"));
});

Option 2: setup policy handlers, and bootstrap them;

Create a Policy class that does the Authorization Handling of your requirement:

public class UserCanBuyPolicy : IAuthorizationHandler
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context)
    {
        //claim-based validation
        if (context.User.HasClaim("permission.canbuy", "CanBuy"))
                context.Succeed(requirement);

        //role-based validation
        if (context.User.IsInRole("admin") || context.User.IsInRole("user"))
                context.Succeed(requirement);

        return Task.CompletedTask;
    }

}

Then you need to register your policy in your application bootstrapping:

services.AddScoped<IAuthorizationHandler, UserCanBuyPolicy>();

services.AddAuthorizationCore(options => {
    options.AddPolicy("CanBuyPolicy", policy => policy.Requirements.Add(UserCanBuyRequirement));
});

The latter option offers more flexibility and can be scaled to include more custom logic as needed.

HTH

Upvotes: 6

Goran
Goran

Reputation: 6518

You solution makes no sense, since each @if checks for @context.User..., and if that is not null, then user it authenticated, and AuthorizeView is redundand (without policy attribute).

I have just researched this myself, and come up with a solution (add this code in client startup):

    services.AddApiAuthorization();
    services.AddAuthorizationCore(options =>
    {
        options.AddPolicy(
            "CanBuy",
            policy => policy.RequireClaim(
                claimType,
                claimValue));
    });

Putting policy on client only makes no sense, so you would need to do the same on the server side.

Upvotes: 2

Sras
Sras

Reputation: 2274

I made it work with this.

<AuthorizeView>
    @if (@context.User.Claims.Count(c => c.Value == Permissions.Products.Create) != 0)
    {
        <a class="btn btn-success" href="product/create">Create</a>
    }
    @if (@context.User.Claims.Count(c => c.Value == Permissions.Products.Edit) != 0)
    {
        <a class="btn btn-warning">Edit</a>
    }
    @if (@context.User.Claims.Count(c => c.Value == Permissions.Products.View) != 0)
    {
        <a class="btn btn-primary" href="product-list">View</a>
    }
    @if (@context.User.Claims.Count(c => c.Value == Permissions.Products.Delete) != 0)
    {
        <a class="btn btn-danger">Delete</a>
    }
</AuthorizeView>

Please find the repo for your reference. https://github.com/chhinsras/PermissionBlazorApp/blob/master/PermissionBlazorApp/Client/Pages/Product/ProductPage.razor

Upvotes: 1

Related Questions