Where should I plugin the Authorization in Asp.net WebAPI? - security

As I see I have 3 possible places to plug my stuff in the pipeline
1) AuthorizationFilters
2) Action Filters
3) DelegatingHandler
The most obvious one is AuthorizationFilters , where I can decorate my actions/ controllers with my custom authorization attribute . say .. MyCustomAuthorizationAttribute .
Since HTTP message handlers are in the first stage in the processing pipeline. Does it make any sense to put it in there ?
Authorization for me right now simply means checking a token in the header which is given to the client after authentication.

Update July 2014
My original answer covered WebApi 1. with WebApi 2 there were some changes i.e. there is now an IAuthenticationFilter meaning you can move authentication logic out of the DelegatingHandler which is a little more elegant.
There is a Nuget project here that offers an implementation of IAuthenticationFilter and also explains some background to its introduction.
OWIN middleware is now perhaps the best place to implement your authentication logic - there is an example of Certificate Authentication here and Basic Authentication OWIN Middleware here in this blog post the former example is the preferred one as it demonstrates the use of the base AuthenticationHandler class.
The advice on AuthorizationFilters remains largely unchanged.
End Update
Typically...
Use DelegatingHandler to carry out Authentication... i.e. who someone is. Use this to set the Principle of the Thread and User context, add claims etc. You can place authorisation logic here too but on a fairly global scale. I would personally always use AuthorizationFilters for authorisation.
Use AuthorizationFilters to restrict controllers and actions to specific people. These are used when you can extrapolate their permission with the information in claims, principal, url or the http request parameters. The default authorisation filter can be used to restrict access to anonymous users or by roles (if set in something like a delegating handler) - obviously you can implement your own AuthorizationFilters too if you need it.
Occasionally use ActionFilters when you need to make the decision over authorisation using the message content e.g. you need access to a property on the entity to decide whether they have access (obviously be careful with this(!)).
Note:
The AuthorizationFilters are called before the content of the body is read therefore they do not have access to the message body to make authorization decisions this is why the ActionFilters specifically the OnActionExecuting is used to occasional raise authentication errors.
So
In your scenario I would put a simple DelegatingHandler to take your header and set the principal.
public class CustomAuthenticationMessageHandler : DelegatingHandler
{
public CustomAuthenticationMessageHandler ()
{
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
Authenticate(request);
return base.SendAsync(request, cancellationToken);
}
protected virtual void Authenticate(HttpRequestMessage request)
{
var authorisationHeader = request.Headers.Authorization;
if (authorisationHeader == null)
{
return;
}
//Ensure you are happy with the header contents then
{
var principal = new GenericPrincipal(//new Identity , //Roles);
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;
}
}
}
Then use AuthorizationFilters to restrict access:
[Authorize]
public string Get()
{
}
[Authorize(Roles = "Admin")]
public string GetAdminOnly()
{
}
To register the global Authentication
config.MessageHandlers.Add(new CustomAuthenticationMessageHandler());
This will mean that in every request the principal will be set to either null or a valid identity. It won't handle authorisation i.e. wont deny access to any controllers or actions.
To start protecting resources
Either target protected controllers and actions with the standard or custom [Authorize] attributes. Or register globally:
config.Filters.Add(new AuthorizeAttribute());
And only white list the controllers and actions you want unsecured using the [AllowAnonymous] attribute.
If you only want authentication on some routes
Then you can modify your DelegatingHandler a little to set the InnerHandler to route to the correct controller e.g.
public CustomAuthenticationMessageHandler(HttpConfiguration configuration)
{
InnerHandler = new HttpRoutingDispatcher(configuration);
}
And then you can specify this handler on your routes like so:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "myurl",
defaults: new {},
constraints: new {},
handler: new CustomAuthenticationHandler(config)
);

Related

Identity Server 4 asp.net 2.1 - IProfileService or alternative

I will buy anyone a beer who can solve my problem!
As a piece of work I need to update our Identity Server to use an implicit login flow, it was currently using Bearer Token access only. As a part of our architecture we are using Multi-tenancy.
For security reaosns we need to check the tenant header to verify that the user is not impersonating another tenant. To do this from a client perspective we use a custom IProfileService. This gets triggered in the middleware of Identity Server, meaning all is good!
However if I was a user and I wanted to use some form of functionality on Identity Server itself and not an actual client of it, then IProfileService will not be triggered. An example of this would be to revoke access to clients, or even log out.
The GetProfileDataAsync Method on IProfileService is Invoked when the client request additional claims for the user.
germansak on Github Issue here had a similar issue and it was never quite answered (https://github.com/IdentityServer/IdentityServer4/issues/1643)
Leading to my question, how has anyone been able to verify a Tenant on Identity Server itself when they are not going through a Client, but instead Identity Server. If I can't trigger IProfileService I feel as if I'm beat!
Both logout and grants functionality is not part of the identity server 4 core package and they are simply implemented as ASP.NET Core razor views outside of the oauth2 flows.
There are few ways to validate headers therefore, I guess the easiest in my opinion would be to add another middleware.
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<TenantHeaderValidationMiddleware>();
...Your other config
}
public class TenantHeaderValidationMiddleware
{
private readonly RequestDelegate _next;
public RequestCultureMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
string tenantId = Request.Headers["YourTenantIdHeader"];
//do the checking
...Validation logic
//pass request further if correct
await _next(context);
}
}

Get Azure AD Groups Before Building Authorization Policies

We're developing an application that uses a back-end built on .Net Core 2.2 Web API. Most of our controllers merely require the [Authorize] attribute with no policy specified. However, some endpoints are going to require the user to be in a particular Azure AD Security Group. For those cases, I implemented policies like this in the Startup.cs file:
var name = "PolicyNameIndicatingGroup";
var id = Guid.NewGuid; // Actually, this is set to the object ID of the group in AD.
services.AddAuthorization(
options =>
{
options.AddPolicy(
name,
policyBuilder => policyBuilder.RequireClaim(
"groups",
id.ToString()));
});
Then, on controllers requiring this type of authorization, I have:
[Authorize("PolicyNameIndicatingGroup")]
public async Task<ResponseBase<string>> GroupProtectedControllerMethod() {}
The problem is that our users are all in a large number of groups. This causes the Graph API to return no group claims at all, and instead a simple hasGroups boolean claim set to true. Therefore, no one has any groups, and thus cannot pass authorization. This no-groups issue can be read about here.
This string-based policy registration, lackluster as it may be, seems to be what the .Net Core people are recommending, yet it falls flat if the groups aren't populated on the User Claims. I'm not really seeing how to circumnavigate the issue. Is there some special way to set up the AppRegistration for my API so that it does get all of the groups populated on the User Claims?
Update:
In the solution, I do have a service that calls Graph to get the user's groups. However, I can't figure out how to call it before it's too late. In other words, when the user hits the AuthorizeAttribute on the controller to check for the policy, the user's groups have not yet been populated, so the protected method always blocks them with a 403.
My attempt consisted of making a custom base controller for all of my Web API Controllers. Within the base controller's constructor, I'm calling a method that checks the User.Identity (of type ClaimsIdentity) to see if it's been created and authenticated, and, if so, I'm using the ClaimsIdentity.AddClaim(Claim claim) method to populate the user's groups, as retrieved from my Graph call. However, when entering the base controller's constructor, the User.Identity hasn't been set up yet, so the groups don't get populated, as previously described. Somehow, I need the user's groups to be populated before I ever get to constructing the controller.
I found an answer to this solution thanks to some tips from someone on the ASP.NET Core team. This solution involves implementing an IClaimsTransformation (in the Microsoft.AspNetCore.Authentication namespace). To quote my source:
[IClaimsTransformation] is a service you wire into the request pipeline which will run after every authentication and you can use it to augment the identity as you like. That would be where you’d do your Graph API call [...]."
So I wrote the following implementation (see an important caveat below the code):
public class AdGroupClaimsTransformer : IClaimsTransformation
{
private const string AdGroupsAddedClaimType = "adGroupsAlreadyAdded";
private const string ObjectIdClaimType = "http://schemas.microsoft.com/identity/claims/objectidentifier";
private readonly IGraphService _graphService; // My service for querying Graph
private readonly ISecurityService _securityService; // My service for querying custom security information for the application
public AdGroupClaimsTransformer(IGraphService graphService, ISecurityService securityService)
{
_graphService = graphService;
_securityService = securityService;
}
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
var claimsIdentity = principal.Identity as ClaimsIdentity;
var userIdentifier = FindClaimByType(claimsIdentity, ObjectIdClaimType);
var alreadyAdded = AdGroupsAlreadyAdded(claimsIdentity);
if (claimsIdentity == null || userIdentifier == null || alreadyAdded)
{
return Task.FromResult(principal);
}
var userSecurityGroups = _graphService.GetSecurityGroupsByUserId(userIdentifier).Result;
var allSecurityGroupModels = _securityService.GetSecurityGroups().Result.ToList();
foreach (var group in userSecurityGroups)
{
var groupIdentifier = allSecurityGroupModels.Single(m => m.GroupName == group).GroupGuid.ToString();
claimsIdentity.AddClaim(new Claim("groups", groupIdentifier));
}
claimsIdentity.AddClaim(new Claim(AdGroupsAddedClaimType, "true"));
return Task.FromResult(principal);
}
private static string FindClaimByType(ClaimsIdentity claimsIdentity, string claimType)
{
return claimsIdentity?.Claims?.FirstOrDefault(c => c.Type.Equals(claimType, StringComparison.Ordinal))
?.Value;
}
private static bool AdGroupsAlreadyAdded(ClaimsIdentity claimsIdentity)
{
var alreadyAdded = FindClaimByType(claimsIdentity, AdGroupsAddedClaimType);
var parsedSucceeded = bool.TryParse(alreadyAdded, out var valueWasTrue);
return parsedSucceeded && valueWasTrue;
}
}
Within my Startup.cs, in the ConfigureServices method, I register the implementation like this:
services.AddTransient<IClaimsTransformation, AdGroupClaimsTransformer>();
The Caveat
You may have noticed that my implementation is written defensively to make sure the transformation will not be run a second time on a ClaimsPrincipal that has already undergone the procedure. The potential issue here is that calls to the IClaimsTransformation might occur multiple times, and that might be bad in some scenarios. You can read more about this here.
You can use the Microsoft Graph API to query the user's groups instead:
POST https://graph.microsoft.com/v1.0/directoryObjects/{object-id}/getMemberGroups
Content-type: application/json
{
"securityEnabledOnly": true
}
Reference: https://learn.microsoft.com/en-us/graph/api/directoryobject-getmembergroups?view=graph-rest-1.0&tabs=http
The scenario will be:
Your client app will acquire access token (A) for accessing your back-end Web API.
Your Web API application will acquire access token (B) for accessing the Microsoft Graph API with the access token (A) using OAuth 2.0 On-Behalf-Of flow. Access token (B) will be used to get the user's groups.
Web API validates the user's group using a policy (recommended) or custom attribute.
The protocol diagram and sample request are listed in this article using the Azure AD V2.0 Endpoint. This article is for the V1.0 endpoint. Here are code samples for .Net Core.

How does [Authorize] attribute enhance Azure App Service (web app) authentication/authorization

I published a web app to Azures App Services. I used the App Service's Authentication/Authorization feature to provide security. I successfully added Active Directory features to my web service (and desktop client). It seemed to work very well. Couldn't access data from a browser or desktop client without signing in to the AD.
This was all before I added the [Authorize] attribute to any of the controllers in the API!
So, what will [Authorize] do (or add) to security in my web api. It seems to already be locked up by configuring the Authentication/Authorization features of the web app in Azure.
So, what will [Authorize] do (or add) to security in my web api.
Using ILSpy, you could check the source code about AuthorizeAttribute under System.Web.Mvc.dll. The core code for authorization check looks like this:
protected virtual bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated)
{
return false;
}
if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
{
return false;
}
if (_rolesSplit.Length > 0)
{
string[] rolesSplit = _rolesSplit;
IPrincipal principal = user;
if (!rolesSplit.Any(principal.IsInRole))
{
return false;
}
}
return true;
}
The main process would check httpContext.User.Identity.IsAuthenticated, then check whether the current user name, user role is authorized or not when you specifying the allowed Users,Roles.
For Authentication and authorization in Azure App Service(Easy Auth) which is implemented as a native IIS module. Details you could follow Architecture of Azure App Service Authentication / Authorization.
It seemed to work very well. Couldn't access data from a browser or desktop client without signing in to the AD.
This was all before I added the [Authorize] attribute to any of the controllers in the API!
Based on your description, I assumed that you set Action to take when request is not authenticated to Log in with Azure Active Directory instead of Allow Anonymous requests (no action) under your Azure Web App Authentication/Authorization blade.
Per my understanding, you could just leverage App Service Authentication / Authorization which provides built-in authentication and authorization support for you without manually adding middleware in your code for authentication. App service authentication would validate the request before your code can process it. So, for additional custom authorization check in your code, you could define your custom authorize class which inherits from AuthorizeAttribute to implement your custom processing.
public class CustomAuthorize : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//TODO:
}
protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
{
//TODO:
}
}
Then, decorate the specific action(s) or controller(s) as follows:
[CustomAuthorize]
public class UsersController : Controller
{
//TODO:
}
App Service's Authentication/Authorization feature is Based on IIS Level. [Authorize] attribute is based on our code level. Both of this can do Authentication, if you used both of them, it means that there are two levels of authentication in your web app.
Here is a picture that helps you understand them:

How to implement Authorization in ASP.NET Web API using Windows Azure

I have sample ASP.NET Web API with get method, I have prefixed a [Authorize] attribute on top of the method. Can I please know how should I call this method from browser or fiddler? Also, I am hosting these API's on Windows Azure
public class ValuesController : ApiController
{
// GET api/values
[Authorize]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
Depending on the type of authorization you are using there might be different ways. But if you are using default routing you could call your method at the following url:
/api/values
You might of course need to pass additional headers depending on the authorization mechanism you choose. The [Authorize] attribute doesn't do anything unless you have configured some authorization. You may take a look at the following article for an example of how you could use tokens to authenticate your users.

How do I secure REST resources so that only a single user of a role can access it?

I have succesfully created a REST web service with Jersey and secured it via java security annotations.
It looks something like this
GET /users/ // gives me all users
GET /users/{id} // gives the user identified by {id}
POST /users/ // creates user
PUT /users/{id} // updates user identified by {id}
DELETE /users/{id} // delete user
I also have setup a realm with two roles: user and admin
I secured all methods so that only admins can access them.
Now i want to give free the PUT /users/{id} and GET /users/{id} methods, so that users can access their own and only their own resources.
Example:
// user anna is logged in and uses the following methods
GET /users/anna // returns 200 OK
GET /users/pete // returns 401 UNAUTHORIZED
Since i could not find a way to configure this through annotations, I am thinking of passing the HTTP request to the corresponding method to check if the user is allowed to access the resource.
It would look something like this for the GET /users/{id} method:
#GET
#Path("/users/{id}")
#RolesAllowed({"admin","user"})
#Produces(MediaType.APPLICATION_JSON)
public Response getUser(
#PathParam("id") String id,
#Context HttpServletRequest req
) {
HttpSession session = request.getSession(false);
if (session != null && session.getValue("userID").equals(id))
return getObject(User.class, id);
return Response.status(Status.UNAUTHORIZED).build();
}
I don't like this aproach because i think i would have to add the userID manualy to the session.
Do you know a more elegant way to solve this?
If not how do you add the userid to the session while using form authentication?
EDIT
Thank you Will and Pavel :) Here is my final solution:
#Context
private SecurityContext security;
// ...
#GET
#Path("/users/{id}")
#RolesAllowed({"admin","user"})
#Produces(MediaType.APPLICATION_JSON)
public Response getUser(#PathParam("id") String id){
if (security.isUserInRole("user"))
if (security.getUserPrincipal().getName().equals(id))
return getObject(User.class, id);
else
return Response.status(Status.UNAUTHORIZED).build();
else
return getObject(User.class, id);
}
In the HttpServletRequest, you can call getRemoteUser() or getUserPrincipal() to get the identity of the logged in user. You would then continue like you are doing in specifically allowing or denying them access to the particular resource.
Blessed Geek is referring more specifically to the aspect of REST regarding stateless transactions and the use of HTTP authentication. While this is an important point in the larger scope of a REST architecture, it's less relevant to your specific question since you don't specify the type of authentication mechanism you're using against your Java EE app, especially since authentication is a container issue in Java EE, not an application issue.
If you're using basic authentication, then you are using HTTP headers to manage authentication and authorization. If you're using form based authentication, then the container is managing this for you via the servlet session, making the service stateful (since sessions are a stateful artifact).
But this has no bearing on your specific question.
One of the most important aspects of deploying REST is understanding the role of http headers and cookies.
For REST to be practical, you need to deploy an authentication framework.
Read
GWT and Google Docs API.
GWT-Platform login + session management
Read up on Google Federated Login, OAuth and OpenID.
Some of my explanations may be outdated, if they were posted before OAuth 2.0.

Resources