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

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");

Related

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

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.

Change LoginPath without ASP.NET Core Identity

Use cookie authentication without ASP.NET Core Identity
The login file is located at Pages/Account/Login.cshtml.
I wish to change to Pages/Index, but I am not able to change the LoginPath after trying various methods that I found using Google.
Try it with adding the following snippet inside the ConfigureServices method. It has to be placed behind your call of AddAuthentication().
services.ConfigureApplicationCookie(cookie =>
{
cookie.LoginPath = "/Index";
});

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.

result of policy based authorization with requirements on failure

We are using policy based authorization with requirements like so:
[Authorize(Policy = "IsUser")]
When authorization fails we just get a white page with status 200.
How can we control what happens if authorization fails in ASP.NET core.
We would like something like forbidden.
Note that for authorization to work in asp.net core, your authentication mechanism should be configured correctly. Microsoft's documentation : "authorization requires an authentication mechanism".
It is not clear whether you are using your own code for authentication or using an asp.net core provided service (Cookie authentication / Identity / etc). Several schemes and relevant code can be found here.
To answer your question, you need to configure the authentication options - for example the "LoginPath". This path would correspond to the "page" that would be used to redirect to when an Authorization fails.
One example (while using Cookie based authentication) is below:
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
}).AddCookie(o => {
o.LoginPath = "/Account/Login";
});
In the above example (code added to the "ConfigureServices" method), Cookie based authentication is added. Default authentication and Default "Challenge" schemes are set. Further the "LoginPath" option is set for the Cookie authentication option.
Hope this helps.