Enabling 401 Challenge responses when using WebAuthenticationConfiguration - c#

I have a very simple WCF4 Restful web service which uses WcfRestContrib to allow custom Basic Authentication. It works great when the client preemptively supplies a username and password but if they don't it returns a 400 Bad Request response rather than challenging the client to supply credentials (through a 401 response).
The service is using the declarative approach to implementing WcfRestContrib by decorating the necessary classes with Attributes. Here is my ServiceContract declaration:
// Standard attributes here
[WebAuthenticationConfiguration(typeof(WebBasicAuthenticationHandler),
typeof(SecurityValidator),
false,
"SearchService")
]
public class SearchService
Which only has one very simple operation:
// Standard attributes here
[OperationAuthentication]
SearchResponse Fetch(SearchRequest request)
And my UserNamePasswordValidator looks like (though it probably doesn't matter since it only gets called when credentials are passed):
public override void Validate(string userName, string password)
{
// TODO: Insert login validation logic here.
// To indicate login failure, throw a FaultException
// throw new FaultException("Unknown Username or Incorrect Password");
// Just return to indicate that the username and password are valid
return;
}
I tried to debug WcfRestContrib and found that the WebBasicAuthenticationHandler class is throwing a BasicAuthorizationException (which is only caught in framework code), however, for some reason it isn't transforming the exception into a 401.
Based on what I've read on the WcfRestContrib github site, there is an issue posted by its author (Mike O'Brian) saying that he'd like to see it return anything other than a 401 which I read as it currently returns the 401 challenge.
If this functionality is there then what am I missing, or am I doing something else wrong?
UPDATE
If there is not a way to do this with WcfRestContrib, is there an alternative way to achieve this (beyond using the standard Windows-based Basic Authentication in IIS)? I'm open to any (other) alternative solution.

I figured it out. I needed to decorate my ServiceContract with the ErrorHandlerAttribute:
// Standard attributes here
[WebAuthenticationConfiguration(typeof(WebBasicAuthenticationHandler),
typeof(SecurityValidator),
false,
"SearchService"),
ErrorHandler(typeof(WebErrorHandler))]
public class SearchService
Just by adding the [ErrorHandler(typeof(WebErrorHandler))] attribute to the service makes all of the magic happen. I knew it had to be something simple, just wish I would have seen it before flattening my forehead on my desk.

Related

ASP.NET Can I make a custom attribute to deal with authorizing scopes?

Sorry if this is very basic but I've never done something like this before.
In my app, we're using Azure AD B2C to take care of our users. When using an endpoint, we require a JWT token that contains a scope, say "TestScope" for now. Currently I have this code that works:
[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
// The web API will only accept tokens 1) for users, and 2) having the "TestScope" scope for this API
static readonly string[] scopeRequiredByApi = new string[] { "TestScope" };
[HttpGet]
public string Get()
{
HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
return "test";
}
}
My idea was that instead of using those two lines of code every time I make an endpoint, could I possibly make a custom attribute, like '[Authorise(Scope = "TestScope")]' to do the same thing? Or maybe a '[TestScope]' tag where I don't have to write the string and potentially make a spelling mistake, though I guess that would be pretty obvious quickly if I did.
Thanks for any advice! Appreciate it
Just think out loud here, it depends how often you want to do the check and this is how I will probably approach it.
If it is one off or maybe few calls you can go with Enums to avoid spelling
mistakes.
If you want to check every now and then maybe custom attribute/action
filter can be a good choice.
If its part of authorization check after user has logged in and authorized
can be used with authorization filter with custom attribute to ignore when
required.
If you want to check on every request authorized/unauthorized you can look
at making custom middleware.

How to correctly setup Policy Authorization for WEB API in .NET Core

I have this Web API project, with no UI. My appsettings.json file has a section listing tokens and which client they belong to. So the client will need to just present a matching token in the header. If no token is presented or an invalid one, then it should be returning a 401.
In ConfigureServices I setup authorization
.AddTransient<IAuthorizationRequirement, ClientTokenRequirement>()
.AddAuthorization(opts => opts.AddPolicy(SecurityTokenPolicy, policy =>
{
var sp = services.BuildServiceProvider();
policy.Requirements.Add(sp.GetService<IAuthorizationRequirement>());
}))
This part fires correctly from what I can see.
Here is code for the ClientTokenRequirement
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ClientTokenRequirement requirement)
{
if (context.Resource is AuthorizationFilterContext authFilterContext)
{
if (string.IsNullOrWhiteSpace(_tokenName))
throw new UnauthorizedAccessException("Token not provided");
var httpContext = authFilterContext.HttpContext;
if (!httpContext.Request.Headers.TryGetValue(_tokenName, out var tokenValues))
return Task.CompletedTask;
var tokenValueFromHeader = tokenValues.FirstOrDefault();
var matchedToken = _tokens.FirstOrDefault(t => t.Token == tokenValueFromHeader);
if (matchedToken != null)
{
httpContext.Succeed(requirement);
}
}
return Task.CompletedTask;
}
When we are in the ClientTokenRequirement and have not matched a token it returns
return Task.CompletedTask;
This is done how it is documented at
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.1
This works correctly when there is a valid token, but when there isnt and it returns Task.Completed, there is no 401 but an exception instead
InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
I have read other stackoverflow articles about using Authentication rather than Authorization, but really this policy Authorization is the better fit for purpose. So I am looking for ideas on how to prevent this exception.
Interestingly, I think this is just authentication, without any authorisation (at least not in your question). You certainly want to authenticate the client but you don't appear to have any authorisation requirements. Authentication is the process of determining who is making this request and authorisation is the process of determining what said requester can do once we know who it is (more here). You've indicated that you want to return a 401 (bad credentials) rather than a 403 (unauthorised), which I believe highlights the difference (more here).
In order to use your own authentication logic in ASP.NET Core, you can write your own AuthenticationHandler, which is responsible for taking a request and determining the User. Here's an example for your situation:
public class ClientTokenHandler : AuthenticationHandler<ClientTokenOptions>
{
private readonly string[] _clientTokens;
public ClientTokenHandler(IOptionsMonitor<ClientTokenOptions> optionsMonitor,
ILoggerFactory loggerFactory, UrlEncoder urlEncoder, ISystemClock systemClock,
IConfiguration config)
: base(optionsMonitor, loggerFactory, urlEncoder, systemClock)
{
_clientTokens = config.GetSection("ClientTokens").Get<string[]>();
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var tokenHeaderValue = (string)Request.Headers["X-TOKEN"];
if (string.IsNullOrWhiteSpace(tokenHeaderValue))
return Task.FromResult(AuthenticateResult.NoResult());
if (!_clientTokens.Contains(tokenHeaderValue))
return Task.FromResult(AuthenticateResult.Fail("Unknown Client"));
var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(
Enumerable.Empty<Claim>(),
Scheme.Name));
var authenticationTicket = new AuthenticationTicket(claimsPrincipal, Scheme.Name);
return Task.FromResult(AuthenticateResult.Success(authenticationTicket));
}
}
Here's a description of what's going on in HandleAuthenticateAsync:
The header X-TOKEN is retrieved from the request. If this is invalid, we indicate that we are unable to authenticate the request (more on this later).
The value retrieved from the X-TOKEN header is compared against a known list of client-tokens. If this is unsuccessful, we indicate that authentication failed (we don't know who this is - more on this later too).
When a client-token matches the X-TOKEN request header, we create a new AuthenticationTicket/ClaimsPrincipal/ClaimsIdentity combo. This is our representation of the User - you can include your own Claims instead of using Enumerable.Empty<Claim>() if you want to associate additional information with the client.
You should be able to use this as-is for the most part, with a few changes (I've simplified to both keep the answer short and fill in a few gaps from the question):
The constructor takes an instance of IConfiguration as the final parameter, which is then used to read a string[] from, in my example, appsettings.json. You are likely doing this differently, so you can just use DI to inject whatever it is you're currently using here, as needed.
I've hardcoded X-TOKEN as the header name to use when extracting the token. You'll likely be using a different name for this yourself and I can see from your question that you're not hardcoding it, which is better.
One other thing to note about this implementation is the use of both AuthenticateResult.NoResult() and AuthenticateResult.Fail(...). The former indicates that we did not have enough information in order to perform the authentication and the latter indicates that we had everything we needed but the authentication failed. For a simple setup like yours, I think you'd be OK using Fail in both cases if you'd prefer.
The second thing you'll need is the ClientTokenOptions class, which is used above in AuthenticationHandler<ClientTokenOptions>. For this example, this is a one-liner:
public class ClientTokenOptions : AuthenticationSchemeOptions { }
This is used for configuring your AuthenticationHandler - feel free to move some of the configuration into here (e.g. the _clientTokens from above). It also depends on how configurable and reusable you want this to be - as another example, you could define the header name in here, but that's up to you.
Lastly, to use your ClientTokenHandler, you'll need to add the following to ConfigureServices:
services.AddAuthentication("ClientToken")
.AddScheme<ClientTokenOptions, ClientTokenHandler>("ClientToken", _ => { });
Here, we're just registering ClientTokenHandler as an AuthenticationHandler under our own custom ClientToken scheme. I wouldn't hardcode "ClientToken" here like this, but, again, this is just a simplification. The funky _ => { } at the end is a callback that is given an instance of ClientTokenOptions to modify: we don't need that here, so it's just an empty lambda, effectively.
InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
The "DefaultChallengeScheme" in your error message has now been set with the call to services.AddAuthentication("ClientToken") above ("ClientToken" is the scheme name).
If you want to go with this approach, you'll need to remove your ClientTokenRequirement stuff. You might also find it interesting to have a look through Barry Dorrans's BasicAuthentication project - it follows the same patterns as the official ASP.NET Core AuthenticationHandlers but is simpler for getting started. If you're not concerned about the configurability and reusability aspects, the implementation I've provided should be fit for purpose.

Abort connection in case of Unauthorized JWT sent in http request to Web API .net Core 2

I am writing a .net Core 2.0 Web API controller that performs file upload using a multipart type http request and is based in the streaming technique described here.
At this point I have to say that I if you want you can skip the next two paragraphs that describe the reason that led me to the need for a solution to the problem that is described after the two paragraphs.
I initially thought of authenticating the user by sending authentication data in the first section of the multipart request and validating the user as soon as the user data are read, by contacting the database and performing the proper request. However, I thought that since this is a streaming request, any delay in authenticating the user using the database, would delay reading the stream with the file. This would cause the TCP receive buffer to fill with data (possibly also increase its size) and would defeat the purpose of streaming the file (instead of buffering), since memory consumption for this connection would increase.
In order to get rid of this issue I thought of using a 2 step authentication using JWTs. The Web API user will first perform a request and ask for a JWT. Then it would use this JWT in the upload request. As I understand it, JWT authentication should be much faster than a database request since it is performed by validating the JWT using the key stored in the server, so the previous issue should not exist.
I implemented the JWT authentication for the upload request following this very good description from Auth0 and it worked just fine. More specifically the controller has an [Authorize] attribute that forces Web API to to authenticate the user by validating the JWT before the controller is executed.
The problem I am facing is that with the above proposed solution when an unauthorized user tries to upload a file the Controller action is never called. The Authentication engine returns an Unathorized (401) response to the user and lets the user continue sending file data. The last part is my problem. I would like unauthorized users, which are probably attackers, to receive the 401 response and then have their connection terminated.
So, what I want is to keep the authentication/authorization part as it already works and also terminate the user connection after sending the 401 response. I know (and have also tested it) that from inside a controller action method an http connection can be terminated by calling
HttpContext.Abort();
I suspect that by using a filter, I could do what I want but I am not very familiar with filters so that is why I am asking.
We can achieve that by using an IAuthorizationFilter.
Inside it, we gonna set an special ActionResult called AbortUnauthorizedConnectionResult and in that we set the Status Code to 401 and Content-Length to 0 and by calling Response.Body.Flush() we make sure it's sent to client before we call Abort().
Here we have an AuthorizationFilter called AbortUnauthorizedConnections:
class AbortUnauthorizedConnections : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
if (context.HttpContext.User?.Identity == null || !context.HttpContext.User.Identity.IsAuthenticated)
{
// by setting this we make sure the pipe-line will get short-circuited.
context.Result = new AbortUnauthorizedConnectionResult();
}
}
}
And because we have inherited from Attribute we can use it on the upload action like this:
[Authorize]
[AbortUnauthorizedConnections]
public async Task<IActionResult> UploadFile()
{
// we do whatever we want.
}
Here is the code for AbortUnauthorizedConnectionResult:
class AbortUnauthorizedConnectionResult : StatusCodeResult
{
public AbortUnauthorizedConnectionResult() : base(401)
{
}
public override async Task ExecuteResultAsync(ActionContext context)
{
await base.ExecuteResultAsync(context);
context.HttpContext.Response.Headers.Add("Content-Length", "0");
context.HttpContext.Response.Body.Flush();
context.HttpContext.Abort();
}
}
Now if an unauthorized user try to access this controller will get 401 and it's connection gets aborted.
This is the solution I actually implemented due to its simplicity, following #Tratcher's advice:
First, I deleted the [Authorize] attribute from my Controller Action method. Then I wrote the beginning of my Controller Action method as follows:
public async Task<string> UploadFile()
{
if (!(await HttpContext.AuthenticateAsync()).Succeeded)
{
HttpContext.Response.StatusCode = 401; //Unauthorized
HttpContext.Response.Headers.Add("Content-Length", "0");
HttpContext.Response.Body.Flush();
HttpContext.Abort();
return null;
}
...
}

WordPress WooCommerce ASP.net API WebHookHandler: The WebHook request must contain an entity body formatted as HTML Form Data

I am trying to create a WebHookHandler for Webhooks send from WordPress WooCommerce in ASP.NET C#.
I started with creating a ASP.NET C# Azure API App WebApplication Project and adding the relevant references (Microsoft.AspNet.WebHooks.Common, Microsoft.AspNet.WebHooks.Receivers, Microsoft.AspNet.WebHooks.Receivers.WordPress). Added the WebHookConfig, WordPressWebHookHandler and registered the WebHookConfig in the GlobalAsax.
I then published the application as an Azure App Service.
My WordPressWebHookHandler is still the default of the examples and looks like this:
public class WordPressWebHookHandler : WebHookHandler
{
public override Task ExecuteAsync(string receiver, WebHookHandlerContext context)
{
// make sure we're only processing the intended type of hook
if("WordPress".Equals(receiver, System.StringComparison.CurrentCultureIgnoreCase))
{
// todo: replace this placeholder functionality with your own code
string action = context.Actions.First();
JObject incoming = context.GetDataOrDefault<JObject>();
}
return Task.FromResult(true);
}
}
When testing a User Creation WebHook in WooCommerce I can see the request in the log as below.
But unfortunately it is never received while debugging and I see below error.
I am thinking maybe I need a custom WebHook instead of the WordPress specific one as this is a WooCommerce Webhook. Or possibly it is handled wrong in the routing and ends up in another controller.
Any help is much appreciated.
Your WebHookReceiver is wrong
There is a mismatch of expecting HTML Form Data, when in fact it should be expecting JSON.
WordPressWebHookHandler is still the default
This is what is causing your error. If you look at the WordPressWebHookReceiver, the ReceiveAsync() method implementation, calls out to ReadAsFormDataAsync() method, which is not what you want, as your Content-Type is json. So, you want to be doing ReadAsJsonAsync().
Solution: Don't use the WordPressWebHookReceiver and switch it to another one that will call ReadAsJsonAsync().
Looking at the code
I am thinking maybe I need a custom WebHook instead of the WordPress specific one as this is a WooCommerce Webhook.
You had the right idea, so I dug up some of the code to explain exactly why this was happening.
The code block below is the ReceiveAsync() method that is overridden in the WordPressWebHookReceiver. You can see that it is calling the ReadAsFormDataAsync() which is not what you want...
public override async Task<HttpResponseMessage> ReceiveAsync(
string id, HttpRequestContext context, HttpRequestMessage request)
{
...
if (request.Method == HttpMethod.Post)
{
// here is what you don't want to be called
// you want ReadAsJsonAsync(), In short, USE A DIFFERENT RECEIVER.
NameValueCollection data = await ReadAsFormDataAsync(request);
...
}
else
{
return CreateBadMethodResponse(request);
}
}
A quick search through the repository for classes that call the ReadAsJsonAsync() method, shows that the following recievers implement it:
DynamicsCrmWebHookReceiver
ZendeskWebHookReceiver
AzureAlertWebHookReceiver
KuduWebHookReceiver
MyGetWebHookReceiver
VstsWebHookReceiver
BitbucketWebHookReceiver
CustomWebHookReceiver
DropboxWebHookReceiver
GitHubWebHookReceiver
PaypalWebHookReceiver
StripeWebHookReceiver
PusherWebHookReceiver
I assumed that the CustomWebHookReceiver would fit your requirements, so can grab the NuGet here. Otherwise you can implement your own, or derive it from this class, etc.
Configuring a WebHook Recevier
(Copied from the Microsoft Documentation)
Microsoft.AspNet.WebHooks.Receivers.Custom provides support for
receiving WebHooks generated by ASP.NET WebHooks
Out of the box you can find support for Dropbox, GitHub, MailChimp,
PayPal, Pusher, Salesforce, Slack, Stripe, Trello, and WordPress but
it is possible to support any number of other providers
Initializing a WebHook Receiver
WebHook Receivers are initialized by registering them, typically in
the WebApiConfig static class, for example:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
...
// Load receivers
config.InitializeReceiveGitHubWebHooks();
}
}
There is a problem with the data format that you send in your request. You must use format of HTML Form as your error message said.
Proper POST data format is described here: How are parameters sent in an HTTP POST request?
Don't forget to set Content-Length header and correct Content-Type if your library doesn't do it. Usually the content type is application/x-www-form-urlencoded.
I would like to make some additions to Svek's answer as I now got my Proof-of-concept completed and understand a bit more about the receivers.
His answer pointed me in the right direction, but needs a little addition.
WordpressWebHookReceiver
Can take in Wordpress Webhooks of type HttpPost. This does not work with Woocommerce as Woocommerce sends Json Webhook messages and will fail the HttpPost validation which is build into the WordpressWebHookReceiver class.
CustomWebHookReceiver
Can take in custom ASP.NET Webhooks. The custom ASP.NET webhooks have a specific partner for validation which includes but is not limited to the 'ms-signature'. Even adding the header will not suffice as the signature is also used in a different way from out of the box Woocommerce to encrypt the message. Basically coming to a point that you can't integrate Woocommerce with the CustomWebHookReceiver without changing the Webhook classes of Woocommerce.
GenericWebHookReceiver
This is the receiver you want, which accepts basically a generic set of Json data and will be able to use the "code" query parameter to verify the secret which you can add in the web.config of your asp.net api application. I used this receiver to finish the Proof-of-concept and got both the signature validation as well as the deciphering of the message working right of the bat.
My basic class which I will start to build into a real solution can be viewed below and changes the JObject into a dynamic object in the methods I call from the class. As you can see I have two methods currently added, one for the customer create and one for the order create to call the respective methods which do an insert into Dynamics 365 (former CRM).
public class GenericJsonWebHookHandler : WebHookHandler
{
public GenericJsonWebHookHandler()
{
this.Receiver = "genericjson";
}
public override Task ExecuteAsync(string generator, WebHookHandlerContext context)
{
var result = false;
try
{
// Get JSON from WebHook
var data = context.GetDataOrDefault<JObject>();
if(context.Id != "crcu" && context.Id != "cror")
return Task.FromResult(true);
if (context.Id == "crcu")
{
result = WoocommerceCRMIntegrations.Entities.Contact.CreateContactInCRM(data);
}
else if (context.Id == "cror")
{
result = WoocommerceCRMIntegrations.Entities.Order.CreateOrderInCRM(data);
}
}
catch (Exception ex)
{
result = false;
}
return Task.FromResult(result);
}
}

Is there an WCF extension point that is called before UserNamePasswordValidator?

I need something to be run before UserNamePasswordValidator.Validate(username, password) is called for each WCF request.
I already tried to use an IDispatchMessageInspector, but the inspector is called after UserNamePasswordValidator.Validate.
Is there an WCF extension point that is called before UserNamePasswordValidator?
I once ran into a similar situation. What I ended up doing was implement a custom username/password validator class. I ran the code that I had to, then ran the good ol' username/password validator.
public override void Validate(string userName, string password)
{
// your custom code here...
// then...
base.Validate(userName, password);
}
As per what you're describing, retrieving configuration values per request could very well be done in this way.
Check out this MSDN page to see how to implement a custom username/password validator.

Categories

Resources