How to give "admin" role access to all pages in .net core web project? - asp.net-core

I have a .net core/5 razor pages project.
I'm using Authorize tags and Policy requirements to restrict access to pages. I would like to ensure that Admin has access to all pages by default without needing to add that to every policy or on every page using Authorize(Roles = "Admin").
services.AddAuthorization(o =>
{
o.AddPolicy("AtLeastStore", policy => policy.Requirements.Add(new StoreOrGreaterRequirement()));
o.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});

I'm using Authorize tags and Policy requirements to restrict access to
pages. I would like to ensure that Admin has access to all pages by
default without needing to add that to every policy or on every page
using Authorize(Roles = "Admin").
You could set the Razor Pages authorization conventions, put the admin related page in an admin folder, then, use the following AuthorizePage convention to add an AuthorizeFilter to the page at the specified path:
services.AddRazorPages(options =>
{
// specify an authorization policy for the admin folder.
options.Conventions.AuthorizeFolder("/admin", "AtLeast21");
//Besides, you can also use the AuthorizePage method to specify an authorization policy for the admin related pages.
//specify an authorization policy for the page.
//options.Conventions.AuthorizePage("/Contact", "AtLeast21");
});
More detail information, see AuthorizePage, AuthorizeFolder and Razor Pages authorization conventions in ASP.NET Core.

Related

How to get same claims in .cshtml page and in API in a hosted Blazor WA?

Hosted Blazor WA app with IdentityServer4 - standard template.
Scaffolded Identity pages.
Custom ProfileService which adds several claims.
Everything works as expected in strictly Blazor and API realms.
However inspecting User claims in Pages in Identity pages I notice that my custom claims added in ProfileService are missing.
My current configuration in Program.cs looks like this:
builder.Services.AddAuthentication()
.AddIdentityServerJwt();
I have tried extending config in AddApiAuthorization to add custom claim, but it did not help.
builder.Services.AddIdentityServer()
.AddApiAuthorization<User, ApplicationDbContext>(options =>
{
options.IdentityResources["openid"].UserClaims.Add("name");
options.ApiResources.Single().UserClaims.Add("name");
options.IdentityResources["openid"].UserClaims.Add("role");
options.ApiResources.Single().UserClaims.Add("role");
options.IdentityResources["openid"].UserClaims.Add("mn");
options.ApiResources.Single().UserClaims.Add("mn");
})
.AddSigningCredentials();
How to configure app to be able to access all claims added in ProfileService in .cshtml?

How to use the Authorize attribute for RBAC with Azure AD?

I have an ASP.NET Core 5 MVC hosted in an Azure AppService.
I've use Azure AD to limit access to the app to specific set of users.
This has been working fine without any Authentication code or configuration in the app.
Now I'm trying to use Azure AD App Roles to limit functionality per roles. I defined App Roles in Azure AD, and assigned them to different users.
Running the App, and going to ./auth/me, I get the user access token the roles are showing as expected.
Now I thought I can only use the [Authorize(Roles = "SomeRole")] on top of ASP.NET controller actions to control access.
First trial, I get the following error when invoking the controller action with the [Authorize] attribute:
InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
I can solve this error by adding the following call in the ConfigureServices method:
services.AddMicrosoftIdentityWebApiAuthentication(Configuration);
But now trying to invoke the controller action I get the following error:
Access Denied, you are not Authorized.
So apparently I'm missing something to link the [Authorize] attribute with the access token received, which has the correct roles.
Please advise.
This has been working fine without any Authentication code or
configuration in the app
Then I'm afraid you are using azure APP service easy auth. You only need to register an Azure AD application and set it in your azure web app instance. And then all the users in your tenant can sign in with their account.
Your requirement now is that you also want to allow specific users to access your app, so you come up with an idea that you can assign roles to some of the users and let your application to validate the role. So you add services.AddMicrosoftIdentityWebApiAuthentication and [Authorize] in your code. But to be honest, that's not the easiest way to do it because you already enabled easy auth and it's better to not adding any code as well. So I'm afraid you can take a look at this document. It allows you to set a list of users who can sign in your app which configure to use the Azure ad app. If users aren't assigned to the Aad app try to sign in, they will get error information.
The steps to do it can be summarized as:
Going to Azure Active Directory -> Enterprise applications -> choose
the app you used in your app -> Properties -> Assignment required? set
to yes then save
Switch from Properties blade to Users and groups blade -> click add
user/group -> select users which you allow them to sign in your app ->
click assign
What you have done is implement by codes. You integrated Azure AD authentication into your MVC project. It can't validate user roles, and the user roles are used for api authorization. Here's a blog introducing it. The roles defined in Azure AD app is different from the asp.net core identity roles.
=====================================================
Ok I got your point, you want to set attributes like [Authorize(Policy = "mypolicy")] for different Controller method in your MVC project to restrict specific users to specific method. You want to use roles to differ the users.
Here's the solution:
you have to create azure ad app roles and assign app roles to users. The app roles is different from default Azure ad user roles, app roles are used for your scenario.
modify your MVC application following this sample or do what I shared below:
var builder = WebApplication.CreateBuilder(args);
// This is required to be instantiated before the OpenIdConnectOptions starts getting configured.
// By default, the claims mapping will map claim names in the old format to accommodate older SAML applications.
// 'http://schemas.microsoft.com/ws/2008/06/identity/claims/role' instead of 'roles'
// This flag ensures that the ClaimsIdentity claims collection will be built from the claims in the token
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration);
builder.Services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme,options =>
{
// The claim in the Jwt token where App roles are available.
options.TokenValidationParameters.RoleClaimType = "roles";
});
// Adding authorization policies that enforce authorization using Azure AD roles.
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("mypolicy", policy => policy.RequireRole("Tiny.AccessEndpoint"));
});
builder.Services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI();
Then in the Controller, adding [Authorize] and adding [Authorize(Policy = "mypolicy")] action method. The test with a user who doesn't assign the app role will show error below.

Setting multiple global authorization policies on ASP.Net Core controller by route?

Working with an ASP.Net Core API. There is one route (api/) for the regular application, another one (admin/) for the admin api's. Is there a way to set different global policies based on the URL?
This article Setting global authorization policies using the DefaultPolicy and the FallbackPolicy in ASP.NET Core 3.x talks about how to set global authorization on different parts of the application (MapRazorPages, MapHealthChecks, etc).
The MapHealthChecks has a parameter for the routes the policy is to be applied. MapControllers() does not take any parameters. Is it possible to do the same things on a controller?
The custom policy can be specified separately for each area:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ApiPolicy", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim("scope", "api");
});
});
...
app.MapControllerRoute(
name: "Api",
pattern: "api/{controller=Home}/{action=Index}/{id?}")
.RequireAuthorization("ApiPolicy");

Asp .net core - Authorize access to all files in a folder via roles

I'm upgrading some initial razor code into asp .net razor pages with .net core 5.0. I've been through many examples on the microsoft site, but it seems that I have to set attributes in all of my .cshtml.cs files. that feels just sloppy and error prone because something will be forgotten somewhere.
In .net 4.x razor, I have an _PageStart.cshtml file, I check the user's role, and I redirect them to the login page if they are not in a particular role. I'd like to do the same in asp .net core using a single file or configuration. I don't want to put an attribute on every pagemodel file, that just seems sloppy. I imagine that I would do something like:
options.Conventions.AuthorizeFolder("/Club", "ClubAdmin");
where ClubAdmin is a role in the application and Club is a folder that contains a bunch of razor pages and sub folders. Is this possible?
TIA
To do this, you can define a policy in your Startup.cs file that checks for a role and then configure razor pages to Authorize that folder for that specific policy:
//define the admin policy
services.AddAuthorization(options =>
{
options.AddPolicy("AdminPolicy", policy => policy.RequireRole("Administrator"));
});
services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/Admin", "AdminPolicy");
});
The RequireRole extension method injects a RolesAuthorizationRequirement handler that will validate for the given role during authorization

Attempting Authentication via Azure AD B2C and Authorization via groups from AAD

So I followed the below examples:
Hosted Blazor Web Assembly AAD B2C: here
Azure Active Directory groups and roles : here
I first implemented Hosted Blazor Web Assembly and got that working fine. Went to implement the Group and Roles parts and began to have issues.
Everything is word for word as in the examples but not sure I merged or setup the Program.cs right in the client. When attempting the call I get a "Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed."
Unfortunately none of my breakpoints work so I figured I would reach out and see if any one has any advice.
This is built from the scaffolding for Blazor.
Here is my program.cs in my client app setup.
builder.Services.AddHttpClient("<Server Project Name>", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
// Supply HttpClient instances that include access tokens when making requests to the server project
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("KeeperLife.UI.ServerAPI"));
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("https://<Full path to >/API.Access ");
});
builder.Services.AddScoped<GraphAPIAuthorizationMessageHandler>();
builder.Services.AddHttpClient("GraphAPI",
client => client.BaseAddress = new Uri("https://graph.microsoft.com"))
.AddHttpMessageHandler<GraphAPIAuthorizationMessageHandler>();
builder.Services.AddMsalAuthentication<RemoteAuthenticationState,
CustomUserAccount>(options =>
{
builder.Configuration.Bind("AzureAd",
options.ProviderOptions.Authentication);
//Originally this was "..." but it seemed to break base config so i added the same as above and that worked but then tested with it commented out and it still worked so left it commented out.
//options.ProviderOptions.DefaultAccessTokenScopes.Add("https://<Url to full API PAth>/API.Access");
options.ProviderOptions.AdditionalScopesToConsent.Add(
"https://graph.microsoft.com/Directory.Read.All");
})
.AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, CustomUserAccount,
CustomUserFactory>();
builder.Services.AddAuthorizationCore(options =>
{
options.AddPolicy("SiteAdmin", policy =>
policy.RequireClaim("group", "<The Object ID of the group>"));
});
Not sure why your breakpoints don't work. But as far as I know, AAD B2C does not provide an Out-of-the-box RBAC functionality.
In Azure AD we can implement it by modifying the "groupMembershipClaims" field in application manifest: "groupMembershipClaims": "SecurityGroup". But it's not available in Azure AD B2C.
There is a workaround. Add a new claim type 'groups' into the custom policy and call the Microsoft Graph to get user's groups. Here is an example for your reference.
Vote this user voice post will be helpful.