Question

I am writing a custom Owin middleware AuthenticationHandler for Hmac authentication.

In the overriden 'AuthenticateCoreAsync' method I need to read the http headers and the 'Body' (content) of the request.

I have everything working, claims / authentication ticket, etc.

But whenever I access or do something with IOwinRequest.Body my WebApi controllers break. They are called correctly, but all api method arguments are 'null'. As if the body content gets lost in the process somewhere.

I tried copying IOwinRequest.Body to a memory stream and use that, I tried resetting it using IOwinRequest.Body.Seek(0), etc. all to no avail.

I can't find any good example / documentation on how to work with the Body of IOwinRequest either.

Anyone can point me in the right direction?

In the code below, it works when I leave it commented. When I uncomment those lines to actually do authentication (and read from Body), my webapi methods receive all null arguments.

internal class HMacAuthenticationMiddleware : AuthenticationMiddleware<HMacAuthenticationOptions>
{
    private readonly ILogger logger;

    public HMacAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, HMacAuthenticationOptions options)
        : base(next, options)
    {
        this.logger = app.CreateLogger<HMacAuthenticationMiddleware>();
    }

    protected override AuthenticationHandler<HMacAuthenticationOptions> CreateHandler()
    {
        return new HMacAuthenticationHandler(this.logger);
    }
}

HMacAuthenticationHandler:

    protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
    {
        string[] apiKeys = null;
        AuthenticationTicket authenticationTicket = null;
        if (this.Request.Headers.TryGetValue(Configuration.ApiKeyHeader, out apiKeys))
        {
            ////var bufferedStream = await CreateStreamBuffer(Request);
            //var memStream = new MemoryStream();
            //await new StreamContent(Request.Body).CopyToAsync(memStream);
            //Request.Body = memStream;
            //memStream.Seek(0, SeekOrigin.Begin);
            //var httpMessage = Request.CreateRequestMessage(new StreamContent(memStream));
            //var authHandler = new HMacInnerAuthenticationHandler(httpMessage, Options);
            //var isAuthenticated = await authHandler.IsAuthenticated();
            //Request.Body.Seek(0, SeekOrigin.Begin);

            var isAuthenticated = true;
            if (isAuthenticated)
            {
                var userName = this.Options.UsernameLookup.GetByApiKey(apiKeys.First());
                var identity = new ClaimsIdentity(
                    new[]
                        {
                            new Claim(ClaimTypes.NameIdentifier, apiKeys.First(), XmlSchemaString, this.Options.AuthenticationType),
                            new Claim(ClaimTypes.Name, userName, XmlSchemaString, this.Options.AuthenticationType)
                        },
                    this.Options.AuthenticationType,
                    ClaimsIdentity.DefaultNameClaimType,
                    ClaimsIdentity.DefaultRoleClaimType);

                authenticationTicket = new AuthenticationTicket(identity, null);
            }
        }
        return authenticationTicket;
    }
Was it helpful?

Solution

Ok, so after digging around in the aspnetweb stack sources. Especially here: https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Http.Owin/WebApiAppBuilderExtensions.cs

I noticed that I did not use that method to register WebApi into owin.

I was still using "GlobalConfiguration.Configure(WebApiConfig.Register);" inside Global.asax.cs

I removed that, replacing it with:

        var httpConfig = new HttpConfiguration();
        WebApiConfig.Register(httpConfig);
        app.UseWebApi(httpConfig);

inside Startup.cs, along with the other middleware.

Now everything works, as the WebApi is now registered in the correct place in the pipeline I guess.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top