I have a 3 layer application - 1) The UI layer is a ASP .NET MVC application 2) The Business layer is a class library 3) The Data Access layer is a class library. I used role based authorization based on claims. I set the claims in the Application_AuthenticateRequest event handler
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
var authenticationCookie =
HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
var ticket = FormsAuthentication.Decrypt(authenticationCookie.Value);
FormsIdentity formsIdentity = new FormsIdentity(ticket);
ClaimsIdentity claimsIdentity = new ClaimsIdentity(formsIdentity);
// Get the roles from database
...
var role = GetUserRole();
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, role));
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
Thread.CurrentPrincipal = claimsPrincipal;
}
Now I have two ways to access the role in business layer
1) Directly access the role from the Thread principal
var principal = Thread.CurrentPrincipal as ClaimsPrincipal;
var claim = principal.Claims.FirstOrDefault(x => x.Type ==
ClaimTypes.Role);
Pros:
Every method can implicitly access the role.
Cons
Difficult to unit test since it is dependent on a static object
2) Pass the role as a parameter to the method needing it e.g.
public IUserService {
void CreateUser(User user, string role);
}
Pros
Easy to unit test since role is explicitly passed to the method
Cons
Every method needs to have a parameter
If authorization is changed from role based authorization to any form of authorization, it breaks the system
What is the alternative ? What is the standard way of implementing role based authorization in business layer?
There is a standard way of implementing externalized, fine-grained authorization (not just role-based) across multiple layers. The model is called abac and stands for attribute based access control. There is one major framework which implements it and that's called xacml. Another alternative is claims-based authorization in .Net specifically.
ABAC/XACML defines:
a policy language with which to defines authorization rules e.g. a manager can view documents they own.
an architecture which defines on the one hand
a. a Policy Decision Point (PDP) which processes authorization requests and produces decisions (Permit/Deny)
b. a Policy Enforcement Point (PEP) which protects your application, intercepts business requests and sends authorization requests to the PDP. This is the piece of code that would sit in your app either as a filter, interceptor, annotation or more...
The nice approach with externalized authorization is that you can apply the same consistent authorization across multiple layers in one go (from web SSO to business tier and even data tier).
HTH. There are great resources on Wikipedia. You can also check out this blog I try to keep up-to-date.
Related
I used Identity Server and protected endpoints with policies and roles. Those are reflected in the access token I'm distributing to the client. Today, I got the suggestion that instead of protecting a method like this:
[Authorize(Policy = "Elevated"), HttpGet("metadata")]
public IActionResult GetTenantMetadata() { ... }
we could skip the role section of the JWT and only do the following.
[Authorize, HttpGet("metadata")]
public IActionResult GetTenantMetadata() { ... }
Then, in the Startup.cs, we'd register a custom handler like this.
services.AddHttpContextAccessor();
services.AddTransient<IAuthorizationHandler, HeaderHandler>();
The actual security would be then performed in the handler based on the sub value from the token. No roles, no scopes no nothing. To me, it appears intuitively ill-advised putting a lot of responsibility on me to create the protective logic, instead of relying on IDS and people way smarter than me. All the solutions I've seen deal with security using claims and roles. So my gut feeling says it's a bad idea.
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
CustomRequirement requirement)
{
HttpRequest request = Accessor.HttpContext.Request;
string param = request.Headers.SingleOrDefault(a => a.Key == "param1");
string someAccess = dbContext.GetAccess(param, context.User.GetUserId());
bool authorized = SomeEvaluation(param, someAccess);
if (authorized) context.Succeed(requirement);
else context.Fail();
return Task.CompletedTask;
}
However, I wasn't able to motivate my choice (other than the guts and others' samples), so finally, I got unsure. I'm humble to understand that if I can't explain why, then maybe I'm wrong.
I googled claims based security and roles policies security etc. but I could come up with a name for the suggested alternative. Consequently, I haven't found any material discussing that matter.
What is such a custom authorization method called and what's the advantage/caveat of it?
It's still claims authorization, just not a good / practical one: you're only using the sub claim. There's no reason to throw away/not use perfectly good claims provided by a trusted identity provider.
First, letting clients send the user ID via a header is a security risk. How would you know if it's not tampered with, and the user is not pretending to be an admin? You can't, that's why you ask an ID provider to authenticate the user and sign a token for identification.
Second, cherry-picking the user ID and fetching other user claims at runtime from a database or API could work, but it may not always possible (app doesn't keep user data / delegates authentication to a 3rd party) or feasible (high traffic application, too many db lookups).
To authorize an action, you can declare policies with assertions. But for more advanced or imperative checks, you have the option to implement an AuthorizationHandler and imperatively allow/block an action.
It's useful for resource-based authorization, but an overkill for what would be a single line of code if the claims you need are already provided in the access token.
Further info
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/claims?view=aspnetcore-5.0
Background
I've been following the documentation for using IdentityServer4 with single-page-applications on ASP.NET-Core 3.1 and as such created a project via the dotnet new react -au Individual command.
This creates a project which uses the Microsoft.AspNetCore.ApiAuthorization.IdentityServer NuGet package.
So far it's been really great and it got token-based authentication for my ReactJS application working without any pain!
From my ReactJS application, I can access the user information populated by the oidc-client npm package such as the username.
Also, calls to my Web APIs with the [Authorize] attribute work as expected: only calls with a valid JWT access token in the request header have access to the API.
Problem
I'm now trying to access basic user information (specifically username) from within a GraphQL mutation resolver via an injected IHttpContextAccessor but the only user information I can find are the following claims under IHttpContextAccessor.HttpContext.User:
nbf: 1600012246
exp: 1600015846
iss: https://localhost:44348
aud: MySite.HostAPI
client_id: MySite
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier: (actual user GUID here)
auth_time: 1600012235
http://schemas.microsoft.com/identity/claims/identityprovider: local
scope: openid
scope: profile
scope: MySite.HostAPI
http://schemas.microsoft.com/claims/authnmethodsreferences: pwd
The same issue happens for Web API controllers as well.
Details
MySite is the namespace of my solution and is also what I have defined as a client in my appsettings.json file:
{
"IdentityServer": {
"Clients": {
"MySite": {
"Profile": "IdentityServerSPA"
}
}
}
}
My web application project's name is MySite.Host so MySite.HostAPI the name of the resource and scope that are automatically generated by calling AuthenticationBuilder.AddIdentityServerJwt().
... this method registers an <<ApplicationName>>API API resource with IdentityServer with a default scope of <<ApplicationName>>API and configures the JWT Bearer token middleware to validate tokens issued by IdentityServer for the app.
Research
According to a few answers on Stack Overflow, adding IdentityResources.Profile() resource via IIdentityServerBuilder.AddInMemoryIdentityResources() should do the trick but it looks like it's already available via the claims I posted above (scope: profile).
I nevertheless tried it but the result is that the authentication flow becomes broken: the redirect to the login page does not work.
All of the answers I've found also make a reference to a Config class like in this demo file which holds configurations that are mainly fed to IIdentityServerBuild.AddInMemory...() methods.
However, it seems that Microsoft.AspNetCore.ApiAuthorization.IdentityServer does most of this in its implementation and instead offers extendable builders to use.
From the IdentityServer documentation, I don't believe I need to add a Client because the access token already exists. The client ReactJS application uses the access_token from oidc-client to make authorised calls to my Web APIs.
It also doesn't appear like I need to add a Resource or Scope for the username information because I believe these already exist and are named profile. More to this point is that the documentation for "IdentityServerSPA" client profile states that:
The set of scopes includes the openid, profile, and every scope defined for the APIs in the app.
I also looked at implementing IProfileService because according to the documentation this is where additional claims are populated. The default implementation is currently being used to populate the claims that are being requested by the ProfileDataRequestContext.RequestedClaimTypes object and this mechanism already works because this is how the ReactJS client code receives them. This means that when I'm trying to get the user claims from ASP.NET-Core Identity, it's not properly populating ProfileDataRequestContext.RequestedClaimTypes or perhaps not even calling IProfileServices.GetProfileDataAsync at all.
Question
Considering that my project uses Microsoft.AspNetCore.ApiAuthorization.IdentityServer, how can I view the username from my ASP.NET-Core C# code, preferably with IHttpContextAccessor?
What you need to do is to extend the default claims requested by IdentityServer with your custom ones. Unfortunately, since you're using the minimalistic IdentityServer implementation by Microsoft, the correct way of making the client request the claims isn't easy to find. However, assuming you have only one application (as per the template), you could say that the client always wants some custom claims.
Very important first step:
Given your custom IProfileService called, say, CustomProfileService, after these lines:
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
you have to get rid of the implementation used in the scaffolded template, and use your own:
services.RemoveAll<IProfileService>();
services.AddScoped<IProfileService, CustomProfileService>();
Next, the actual implementation of the custom IProfileService isn't really hard if you start from Microsoft's version:
public class CustomProfileService : IdentityServer4.AspNetIdentity.ProfileService<ApplicationUser>
{
public CustomProfileService(UserManager<ApplicationUser> userManager,
IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory) : base(userManager, claimsFactory)
{
}
public CustomProfileService(UserManager<ApplicationUser> userManager,
IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory,
ILogger<ProfileService<ApplicationUser>> logger) : base(userManager, claimsFactory, logger)
{
}
public override async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
string sub = context.Subject?.GetSubjectId();
if (sub == null)
{
throw new Exception("No sub claim present");
}
var user = await UserManager.FindByIdAsync(sub);
if (user == null)
{
Logger?.LogWarning("No user found matching subject Id: {0}", sub);
return;
}
var claimsPrincipal = await ClaimsFactory.CreateAsync(user);
if (claimsPrincipal == null)
{
throw new Exception("ClaimsFactory failed to create a principal");
}
context.AddRequestedClaims(claimsPrincipal.Claims);
}
}
With those two steps in place, you can start tweaking CustomProfileService's GetProfileDataAsync according to your needs. Notice that ASP.NET Core Identity by default already has the email and the username (you can see these in the claimsPrincipal variable) claims, so it's a matter of "requesting" them:
// ....
// also notice that the default client in the template does not request any claim type,
// so you could just override if you want
context.RequestedClaimTypes = context.RequestedClaimTypes.Union(new[] { "email" }).ToList();
context.AddRequestedClaims(claimsPrincipal.Claims);
And if you want to add custom data, for example, the users first and last name:
// ....
context.RequestedClaimTypes = context.RequestedClaimTypes.Union(new[] { "first_name", "last_name" }).ToList();
context.AddRequestedClaims(claimsPrincipal.Claims);
context.AddRequestedClaims(new[]
{
new Claim("first_name", user.FirstName),
new Claim("last_name", user.LastName),
});
User information can be retrieved via the scoped UserManager<ApplicationUser> service which is set up by the project template. The users's claims contains "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" (ClaimTypes.NameIdentifier) whose value is the user identifier. UserManager<>.FindByIdAsync() can then be used to retrieve the ApplicationUser associated with the user and which contains additional user information.
Note that this contacts the user store each time it's invoked. A better solution would be to have the extra user information in the claims.
First, explicitly add the IHttpContextAccessor service if you haven't already by calling services.AddHttpContextAccessor();
From within an arbitrary singleton service:
public class MyService
{
public MyService(
IHttpContextAccessor httpContextAccessor,
IServiceProvider serviceProvider
)
{
var nameIdentifier = httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
using (var scope = serviceProvider.CreateScope())
{
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
var user = await userManager.FindByIdAsync(nameIdentifier);
// Can access user.UserName.
}
}
}
UserManager<ApplicationUser> can be accessed directly within Razor pages and Controllers because these are already scoped.
I am looking for a solution/suggestion that helps me creating permission based access to web api endpoints/controller actions.
Role based access is not suitable becuase I don't have fixed rules that I could use in code like Role("Admin") oder Role("Controller").
Claim based permissions is also not feasable because each user/client can have different permissions on each business object/entity (e.g. Read/Write-access to own tickets and read access to all ticket of his/her company or if its a technician of my company full access to all tickets of all customers. So each user would have 10s or even hundrets of claims which I would have to evaluate at each access of my API.
It is some kind of multi tenancy in just on database and the tenants are our customers with some kind of "master tenant" that has access to all of the tenant data.
I think that something like Visual Guard would satisfy my needs but it is pretty expensive and they don't support net core for now and their documentation seems pretty outdated.
I don't need a usable solution at once but some hints and tricks how I could achieve that would very much be apprieciated because I am looking and searching for some time now.
Details on "database permissions":
What I mean is in my frontend (Winforms app) I want to establish a security system where I can create and assign roles to users and in those roles is defined which actions a user can execute and which CRUD operations he/she can do on specific business objects. Each role can have n users and each role can have n permissions. Each permission on itself declares for exmaple Create:false, Read:true, Write:true and Delete:false. If a permission for a specific business object is not found CRUDs on that BO is denied totally.
So whenever an action in my API is called I have to check if that user and his/her rule allows him to do that specific action based on rules and permissions in my database.
Details an application structure:
Frontend will be a Winforms app which calls the API in the background by OData. I don't want to rely solely on security in the Winforms app because the API will be accessible from the internet and I can't be sure if a user would not try to access the api with his credentials just to see what is possblie without the "frontend filter". So the permissions lie in the API and if a user tries to access s.t. in the frontend app the app itself "asks" the API if that is possible.
Later on I want to create mobile clients that also use the Odata Web API.
The relevant API in asp.net core are:
IAuthorizationService
AuthorizationPolicy
IAuhtorizationRequirement
IAuthorizationHandler
The authorization pattern you are looking for is called Resource-based authorization
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/resourcebased?view=aspnetcore-2.2
Basically, you can define AuthorizationPolicy, and apply it to a instance of a resource:
var ticket = _ticketRepository.Find(ticketID);
var authorizationResult = await _authorizationService
.AuthorizeAsync(User, ticket, "EditTicketPolicy");
In the authorization handler, you can check if the user is the owner of the resource.
public class ResourceOwnerRequirement : IAuthorizationRequirement
{
}
public class ResourceOwnerHandler
: AuthorizationHandler<ResourceOwnerRequirement, MyBusinessObject>
//: AuthorizationHandler<ResourceOwnerRequirement> use this overload to handle all types of resources...
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
ResourceOwnerRequirement requirement,
MyBusinessObject resource)
{
int createdByUserId = resource.CreatedBy;
Claim userIdClaim = ((ClaimsIdentity)context.User.Identity).FindFirst("UserId");
if (int.TryParse(userIdClaim.Value, out int userId)
&& createdByUserId == userId)
{
context.Succeed(requirement);
}
}
}
//admin can do anything
public class AdminRequirementHandler : IAuthorizationHandler
{
public Task HandleAsync(AuthorizationHandlerContext context)
{
if (context.User.Claims.Any(c => c.Type == "Role" && c.Value == "Administator"))
{
while (context.PendingRequirements.Any())
{
context.Succeed(context.PendingRequirements.First());
}
}
return Task.CompletedTask;
}
}
BTW, this still can be called claims or role based authorization. Users with specific role can edit their own tickets, but users with admin role also other tickets. The difference is that you apply authorization to a resource, not just action
EDIT:
I've a SecurityService that has a AutoLogin method, that uses the ServiceSecurityContext to find out which windows identity is calling and then tries to find the related user account in the database. This is working fine when it is called from a web site that uses impersonation and requires integrated security in IIS. The call is using the stock NetPipeBinding.
I'd like to test the service as follows:
[TestMethod]
public void AutoLoginAsAnonymousFails()
{
using (var anonymousContext = WindowsIdentity.Impersonate(WindowsIdentity.GetAnonymous().Token))
{
ISecurityService securityService = ClientChannelManager.CreateSecurityServiceChannel();
var loginResponse = securityService.AutoLogin();
((ICommunicationObject)securityService).Close();
Assert.IsFalse(loginResponse.IsSuccessful);
}
}
On the service side the user in the securitycontext is always me - how to make it an anonymous user? I've already tried to impersonate the IntPtr.Zero but without success.
For reference the relevant part of the service method:
public ResponseMessage AutoLogin()
{
if (ServiceSecurityContext.Current.WindowsIdentity != null
&& !ServiceSecurityContext.Current.WindowsIdentity.IsAnonymous
&& !ServiceSecurityContext.Current.WindowsIdentity.IsGuest
&& ServiceSecurityContext.Current.WindowsIdentity.IsAuthenticated)
{
// find the user based on his windows identity and return success = true message
}
// return success = false message
}
This is a classic example of how separation of concerns could help you. Instead of relying directly on ServiceSecurityContext (something that one should, IMO, never do), make sure to configure your service so that security information is instead encapsulated in Thread.CurrentPrincipal.
IIRC, when you use Windows authentication and impersonation, it may even set this up for you automatically, but otherwise, you can always write a custom ServiceAuthorizationManager, that does this for you.
This will allow you to vary your security concerns independently of your domain logic. If you stick with the IPrincipal interface and resist the temptation of downcasting to WindowsPrincipal, your code will even be ready for the future of identity: Claims-based Identity, as implemented by the Windows Identity Foundation (WIF).
This also helps tremenduously with unit testing, because you can then just assign a GenericPrincipal to Thread.CurrentPrincipal before invoking your System Under Test (SUT).
Can anyone briefly explain what is the use of GenericIdentity and where to use it.
GenericIdentity and GenericPrincipal are the simplest way of describing a user as a "principal". This can be used for implementation-unaware security checking in an application - i.e. if the user logs in as "Fred" with the "User" and "Admin" permissions:
string[] roles = { "User", "Admin" };
Thread.CurrentPrincipal = new GenericPrincipal(
new GenericIdentity("Fred"), roles);
You might do this at the point of client login to a winform, or there are specific points to do this in WCF, ASP.NET, etc.
Then later code, without having to know how those permissions are handled, can check that permission - either via IsInRole, or declaratively:
[PrincipalPermission(SecurityAction.Demand, Role = "Admin")]
void SomeAdminFunction() { }
Some useful utility code here is null-safe wrappers around principal/identity:
public static string GetUsername() {
IPrincipal principal = Thread.CurrentPrincipal;
IIdentity identity = principal == null ? null : principal.Identity;
return identity == null ? null : identity.Name;
}
public static bool IsInRole(string role) {
IPrincipal principal = Thread.CurrentPrincipal;
return principal == null ? false : principal.IsInRole(role);
}
Then you might have some audit code in your DAL:
row.UpdatedBy = MyUtilityClass.GetUsername();
GenericPrincipal is useful for the simple cases of a plain username and set of known roles.
More sophisticated principal implementations might, for example, do "on demand" access checking - i.e. until you ask for the "Foo" role it doesn't know - it then finds out (by talking to a web-service, database, active-directory, etc) and caches the result for future access. This is useful when the list of potential roles is large, and the number of roles typically queried in reality is small.
You can also use a principal to store extra identity information that is only needed in certain contexts - for example, a security token. Callers might test the principal with as to see if it supports the extra data.
Using "principal" is useful because your logic processing code can talk about identity, without having to know whether this is winforms, ASP.NET, WCF, a windows service, etc - it is abstract. Additionally, some 3rd party code will also talk to the principal.
As another example - I wrote some example code here that shows how to use the principal to control access to winform controls via the designer (via an IExtenderProvider - which puts extra entries into the property grid in VS).
You can use GenericIdentity as a concrete implementation of Identity where you want to supply the details yourself, programmatically, about the current user. Pretty good if you have identified and authenticated the user yourself, through other channels.
GenericIdentity class can be used in conjunction with the GenericPrincipal class to create an authorization scheme that exists independent of a Windows domain.
GenericIdentity myIdentity = new GenericIdentity("MyUser");
Check out
http://msdn.microsoft.com/en-us/library/system.security.principal.genericidentity.aspx
You will find some examples up there. It represents a generic user.
Authentication and profile perissions.
GenericIdentity Class:-
The GenericIdentity class implements the IIdentity interface. It represents the identity of the user based on a custom authentication method defined by the application.
GenericPrincipal class:-
The GenericPrincipal class implements the IPrincipal interface. It represents users and roles that exist independent of Windows users and their roles. Essentially, the generic principal is a simple solution for application authentication and authorization.