OWIN-hosted web api: using windows authentication and allow anonymous access - c#

I have a WebApi project self-hosted using OWIN.
I want to enable Windows Authentication on some of the controller's actions, but allow other actions to be called anonymously.
So, following some examples I found online, I setup my WebApi like this in my Statrup class:
public void Configuration(IAppBuilder appBuilder)
{
HttpListener listener = (HttpListener)appBuilder.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication | AuthenticationSchemes.Anonymous; //Allow both WinAuth and anonymous auth
//setup routes and other stuff
//...
//Confirm configuration
appBuilder.UseWebApi(config);
}
Then, in my controller, I created two actions:
[HttpGet]
[Authorize]
public HttpResponseMessage ProtectedAction()
{
//do stuff...
}
[HttpGet]
[AllowAnonymous]
public HttpResponseMessage PublicAction()
{
//do stuff...
}
This, however, does not work.
Calling the action marked AllowAnonymous works as expected, but calling the one marked Authorize always returns a 401 error and the following message:
{
"Message": "Authorization has been denied for this request."
}
even if the caller supports windows authentication, tested on browsers (Chrome and Edge) and Postman.
What am I missing here?

Well, I found a workaround for this in another question.
Instead of specifying multiple auth modes (which doesn't work), you can chose the auth mode for each request at runtime, by setting up an AuthenticationSchemeSelector method like this:
public void Configuration(IAppBuilder app)
{
HttpListener listener = (HttpListener)appBuilder.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemeSelectorDelegate = new
AuthenticationSchemeSelector(GetAuthenticationScheme);
}
private AuthenticationSchemes GetAuthenticationScheme(HttpListenerRequest httpRequest)
{
if(/* some logic... */){
return AuthenticationSchemes.Anonymous;
}
else{
return AuthenticationSchemes.IntegratedWindowsAuthentication;
}
}
While not ideal (you have to manually check the request URL or some other parameter of the request to decide which method to use) it works.

Since your description about the question is bit limited I have set-up a demo app, where I implemented OAuthAuthorizationServerProvider as Provider for OAuthAuthorizationServerOptions and override GrantResourceOwnerCredentials and ValidateClientAuthentication
public void Configuration(IAppBuilder app)
{
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new ApplicationOAuthBearerAuthenticationProvider()
});
app.Use<AuthenticationResponseMiddleware>();
var options = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/xxxx"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new OwinAuthorisationProvider()
};
app.UseOAuthAuthorizationServer(options);
}
also tried to have a custom AuthorizeAttribute and added as filters in the configuration class .Filters.Add(new AuthorizeAttribute());
In AuthenticationResponseMiddleware i inherited OwinMiddleware and in the public override async Task Invoke(IOwinContext context) method please inspect the flow of the request.
It hits OAuthBearerAuthenticationProvider first in RequestToken method then to OwinMiddleware class, before going to any DelegatingHandler pipelines,
mostly your authentication is implemented in this layer.
Please comment your findings after this check, parallelly I too modify the API and update you, hope it can help you.

Related

how to authorize SignalR Hub in WebApi?

I create a ASP.NET WebApi project, it use [Authorize] attribute to authorize controllers, such as:
[System.Web.Http.Authorize(Roles="Users, Admins")]
public ValueController : ApiController
{
[HttpGet]
public string GetValue()
{
return "Hello World !";
}
}
now I want to add Signalr to this project. This is what I did:
In App_Start/Startup.Auth.cs, add following codes in ConfigureAuth() function to start SignalR.
app.Map(signalr, map =
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
};
hubConfiguration.EnableDetailedErrors = true;
map.RunSignalR(hubConfiguration);
});
Then I add a MessageHub, trying to allow authorized user to send messages to all clients:
[Microsoft.AspNet.SignalR.Authorize]
public class MessageHub : Hub
{
public void SendMessage(string message)
{
Clients.All.SendMessage(message);
}
}
if I start this project, type http://localhost:4000/signalr/hubs in browser, I can receive a hubs js.
Q1 - For me that means SignalR is working on my WebApi project, right?
now I need to test SendMessage, first thing I did is login through http://localhost:4000/Token, then get an access_token. In webApi I usually put this token in Header:
Authorization : Bearer access_token
to access APIs.
Q2 - now what should I do with this token to access MessageHub?

ASP.NET Web API 2 -> Windows Authentication -> Impersonate Windows user on Azure

We are developing an application with Windows Authentication that is used internally at a company. We have looked at ADFS but at the moment this is not an option. The problem is our test servers are entirely cloud based on Azure. I have been trying to find a way to activate a user but have not found a good solution.
My first idea was to turn off authentication completely. This works good but we have some resources that checks for user roles so I had to abandon that idea.
<system.web>
<authentication mode="None" />
</system.web>
Example method that returns 401 Unauthorized with authentication mode="None", obviously:
[Authorize(Roles = "Administrator")]
[HttpGet]
[Route("TestMethod")]
public IHttpActionResult TestMethod()
{
return Ok("It works!");
}
My second thought was to edit the WebApiConfig and try to add authentication headers in every request server side. However when I started looking at the NTLM Authentication Scheme for HTTP and the 4-way handshake I realized this would probably be impossible.
NTLM Authentication Scheme for HTTP
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Other code for WebAPI registerations here
config.MessageHandlers.Add(new AuthenticationHandler());
}
}
class AuthenticationHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Add authentication to every request...
return base.SendAsync(request, cancellationToken);
}
}
Since there is no Owin (Katana) I can not edit the standard App_Start -> Startup.Auth.cs -> public void ConfigureAuth(IAppBuilder app) and try something there. I don't know how I would build up the "user object" anyway.
Is there anything we can do about this or do we have to test everything locally? If we could impersonate one user to be logged in for every request this would be fine in the test environment.
In terms of faking the authentication and authorisation you should be able to set a generic user principal with the appropriate roles using a FilterAttribute.
public class TestIdentityFilter : FilterAttribute, IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
filterContext.Principal = new GenericPrincipal(
new GenericIdentity(),
new string [] {"Administrator"});
}
}
You will need to set <authentication mode="None" /> as you did previously otherwise this code will never be hit in your test environment.
Adding this as a Global filter will override any other existing authentication system (for example if you deploy it to an authenticated environment by mistake). Obviously you will need to be very careful about only using this in your test system.
This example is based on MVC, I think there are some very small differences with WebApi but the basic principal applies.
Big thanks to #ste-fu for pointing me in the right direction. Complete code:
public class AppSettingsDynamicRolesAuthorizeAttribute : AuthorizeAttribute
{
public AppSettingsDynamicRolesAuthorizeAttribute(params string[] roleKeys)
{
List<string> roles = new List<string>(roleKeys.Length);
foreach (var roleKey in roleKeys)
{
roles.Add(WebConfigurationManager.AppSettings[roleKey]);
}
this.Roles = string.Join(",", roles);
}
public override void OnAuthorization(HttpActionContext filterContext)
{
if (Convert.ToBoolean(WebConfigurationManager.AppSettings["IsTestEnvironment"]))
{
filterContext.RequestContext.Principal = new GenericPrincipal(
new GenericIdentity("Spoofed-Oscar"),
new string[] { WebConfigurationManager.AppSettings[Role.Administrator] });
}
base.OnAuthorization(filterContext);
}
}
public static class Role
{
public const string Administrator = "Administrator";
public const string OtherRole = "OtherRole";
}
Can then be used like this:
[AppSettingsDynamicRolesAuthorize(Role.Administrator, Role.OtherRole)]
[HttpGet]
[Route("Test")]
public IHttpActionResult Get()
{
var userName = RequestContext.Principal.Identity.Name;
var user = HttpContext.Current.User.Identity;
return Ok("It works!");
}

Owin SelfHosted WebApp does not fulfill HEAD requests

I'm self hosting a web app using Microsoft.Owin.Hosting.WebApp, but after making a HEAD request to the server, it throws a 500 error. When trying to pull a JSON file, the error changes to 504.
I've seen many solutions, but none applying to WebApp. If hosting with NancyFX, I could set AllowChunkedEncoding to false to make it work. But that doesn't seems like a good option.
Code snippet:
var options = new StartOptions("http://localhost:8080")
{
ServerFactory = "Microsoft.Owin.Host.HttpListener"
};
WebApp.Start<Startup>(options);
Implementation of Startup:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseNancy();
}
}
Both calling the browser or using Fiddle causes a failure:
I haven't added the Nancy Module implementation here because it's not where the problem should be fixed, as I also want to serve static content, but allowing HEAD request on them.
Does anyone knows how to serve HEAD verbs from a Self Hosted OWIN?
I just ran into a very similar issue like this. I learned that HEAD method responses should be identical to GET responses but with no content.
Here's the relevant RFC: https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
Example I have for my self-hosted Web api app:
[HttpHead]
[HttpGet]
[ResponseType(typeof(string))]
public HttpResponseMessage LiveCheck(HttpRequestMessage request)
{
HttpResponseMessage response;
response = request.CreateResponse(HttpStatusCode.OK);
if (request.Method == HttpMethod.Get)
{
response.Content = new StringContent("OK", System.Text.Encoding.UTF8, "text/plain");
}
return response;
}
I had a similar issue with a self-hosted SignalR app where HEAD requests caused an app crash and returned error code 500. The solution I found was to write a custom OWIN middleware layer to intercept HEAD requests and return code 200.
Create a new class in your project called HeadHandler.cs
using Microsoft.Owin;
using System.Threading.Tasks;
namespace YourProject
{
public class HeadHandler : OwinMiddleware
{
public HeadHandler(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
if (context.Request.Method == "HEAD")
{
context.Response.StatusCode = 200;
}
else
{
await Next.Invoke(context);
}
}
}
}
In your OWIN Startup class, add a line before mapping any other middleware to use the new HeadHandler middleware.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use<HeadHandler>();
//The rest of your original startup class goes here
//app.UseWebApi()
//app.UseSignalR();
}
}

Self-Hosted SignalR app refusing CORS requests

I am working on an MVC 5 application that uses a windows service to perform some processing; I am using signal R so that I can show if the windows service is working on the UI and also allow the user to manually start processing (as opposed to running on a schedule).
On the server side I have the following config:
public class SignalRStartup
{
public static IAppBuilder App = null;
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableDetailedErrors = true,
};
map.RunSignalR(hubConfiguration);
});
}
}
Which is used like so:
SignalR = WebApp.Start<SignalRStartup>(_settings.LoaderServiceUrl);
Right now the loader service url is: http://localhost:8080
Then on the client side:
var adminHubProxy = $.connection.adminHub;
adminHubProxy.client.updateProcessing = function(status) {
if (status === true) {
$('#processing').show();
} else {
$('#processing').hide();
}
};
$.connection.hub.url = 'http://localhost:8080/signalr';
$.connection.hub.start();
$('#startProcessingLink').on('click', function() {
adminHubProxy.server.startProcessing();
});
And if it matters the code that includes the generated proxy:
<script src="http://localhost:8080/signalr/hubs"></script>
So the problem I'm having is that when I trigger the startProcessing function the server throws back this error message:
XMLHttpRequest cannot load http://localhost:8080/signalr/send?transport=serverSentEvents&connectionTok…Pp7JqCJOnkJEA%3D%3D&connectionData=%5B%7B%22name%22%3A%22adminhub%22%7D%5D.
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
From all the reading I've done my configuration should be resolving this issue by allowing all CORS requests but it isn't and I can't see why.
Edit
After some more debugging I pulled up the details of the response on the negotiate call and am seeing the following headers:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:http://localhost
I'm not sure why the credentials header is being added at all, but the origin header again makes me believe that this should be working.
I figured out the problem, first off the error message has absolutely nothing to do with what is going on.
TL;DR;
The problem was that the AdminHub could not be resolved on the server side because of my dependency injection setup
I am using Castle Windsor for dependency injection and originally the AdminHub looked like this:
public class AdminHub : Hub
{
private readonly IMyService _myService;
public AdminHub(IMyService myService)
{
_myService= myService;
_myService.OnProcessingUpdate += (sender, args) => UpdateProcessingStatus();
}
public void UpdateProcessingStatus()
{
Clients.All.updateProcessing(_myService.IsProcessing);
}
public void GetProcessingStatus()
{
Clients.Caller.updateProcessing(_myService.IsProcessing);
}
public void StartProcessing()
{
_myService.Process();
}
}
The default dependency resolver cannot resolve this as it requires a parameterless constructor. This answer both served to point out what was happening and provide the basis for a solution.

Integration Test Web Api With [Authorize]

So I've found bits and pieces that have enlightened me some on the [Authorize] tag, but nothing that solves my problem.
My scenario is that I have Web Api methods that I want to hit with integration tests using RestSharp. However RestSharp is getting my login page, instead of the results of the call.
[Authorize]
public Item GetItem([FromBody] int id)
{
return service.GetItem(id);
}
The product uses a custom login system, and what I would REALLY like would be a way to disable the [Authorize] badge only for integration tests. However I read that you can allow anonymous users and it would 'disable' the badge, so in the solution, I have an integration tests project, and in that project I have an App.config file. In that file I put:
<location>
<system.web>
<authorization>
<allow users="?"/>
</authorization>
</system.web>
</location>
But this doesn't appear to be working either. Any explanation as to what's going on, why it's not working and what can be done to get this working would be greatly appreciated.
I have attempted to set a Thread.CurrentPrincipal but that didn't work (maybe I did it wrong - can you set "anything" to be authorized in the code?). Authentication is handled in an httpmodule if that helps at all.
I realise that this question is about firing 'real' requests from RestSharp at the webapi endpoints so this suggestion is not immediately applicable to the OPs scenario.. BUT:
I'm using in-memory Web Api tests using HttpConfiguration, HttpServer and HttpMessageInvoker (much like Badri's suggestion I believe). In this way, I don't need listeners or ports open since I can test the full stack (end to end test) in memory - really handy on a build server, Heroku instance, etc.
Using in-memory tests, here is how you could set the Thread.CurrentPrincipal.. I have a helper on my test base class like this:
protected void AuthentateRequest()
{
Thread.CurrentPrincipal = new AuthenticatedPrincipal(Thread.CurrentPrincipal);
}
Which uses this:
public class AuthenticatedPrincipal : IPrincipal
{
private readonly IPrincipal _principalToWrap;
private readonly IIdentity _identityToWrap;
public AuthenticatedPrincipal(IPrincipal principalToWrap)
{
_principalToWrap = principalToWrap;
_identityToWrap = new AuthenticatedIdentity(principalToWrap.Identity);
}
public bool IsInRole(string role)
{ return _principalToWrap.IsInRole(role); }
public IIdentity Identity
{
get { return _identityToWrap; }
private set { throw new NotSupportedException(); }
}
}
public class AuthenticatedIdentity : IIdentity
{
private readonly IIdentity _identityToWrap;
public AuthenticatedIdentity(IIdentity identityToWrap)
{
_identityToWrap = identityToWrap;
}
public string Name
{
get { return _identityToWrap.Name; }
private set { throw new NotSupportedException(); }
}
public string AuthenticationType
{
get { return _identityToWrap.AuthenticationType; }
private set { throw new NotSupportedException(); }
}
public bool IsAuthenticated
{
get { return true; }
private set { throw new NotSupportedException(); }
}
}
It may seem like overkill to stub the IPrincipal manually but I tried with my mocking framework and it blew up in some of my test runners (Resharper and TeamCity, but not NCrunch - something about serialising over AppDomains I think).
This will set Thread.CurrentPrincipal inside the ApiController action method and therefore fool the AuthorizeAttribute into believing you are authenticated.
Here is how you should set the Thread.CurrentPrincipal. Add a message handler like this to your Web API project and add the handler in the Register method of WebApiConfig.cs like so: config.MessageHandlers.Add(new MyTestHandler());.
public class MyTestHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var local = request.Properties["MS_IsLocal"] as Lazy<bool>;
bool isLocal = local != null && local.Value;
if (isLocal)
{
if (request.Headers.GetValues("X-Testing").First().Equals("true"))
{
var dummyPrincipal = new GenericPrincipal(
new GenericIdentity("dummy", "dummy"),
new[] { "myrole1" });
Thread.CurrentPrincipal = dummyPrincipal;
if (HttpContext.Current != null)
HttpContext.Current.User = dummyPrincipal;
}
}
return await base.SendAsync(request, cancellationToken);
}
}
This handler sets an authenticated principal to make all your [Authorize] happy. There is an element of risk with this approach. Only for testing, you should plug this handler into the Web API pipeline. If you plug this handler in to the pipeline (intentional or otherwise) in your production code, it basically defeats your authentication mechanism. To mitigate the risk to some extent (hoping API is not accessed locally), I check to ensure the access is local and that there is a header X-Testing with a value of true.
From RestSharp, add the custom header.
var request = new RestRequest(...);
request.AddHeader("X-Testing", "true");
BTW, for integration testing, I'd much rather use in-memory hosting, instead of web-hosting. That way, Web API runs in the same testing project and you can do whatever you want with it, without the fear of breaking something in production. For more info on in-memory hosting, see this and this.
Set the authenticator for your RestClient:
RestClient.Authenticator = new HttpBasicAuthenticator(username, password);
Using the authenticator that your custom login system actually accepts ... Basic, NTLM, OAuth, Simple ...
It is kind of documented in the second line of the example at http://restsharp.org/

Categories