Wednesday 3 February 2016

Ignoring parameters in Swashbuckle

I’m working with a WebApi that is passed the HttpRequestMessage class which is always available in the ASP.NET WebApi pipeline. However, when the documentation is exposed in Swashbuckle, I get a textbox that shows up where it expects me to enter a value for this parameter.

The operation is defined as follows…

  public async Task Get(HttpRequestMessage request)

The problem is, when the documentation is emitted I end up with this ugly mess...

image

I set about finding a way to instruct Seashbuckle to ignore this parameter, and finding there isn’t anything in the box I set about writing a filter that would do this for me.

In Swashbuckle you can plug-in operation “filters” that can be used to alter the emitted data – the filter is passed the context of the operation being emitted, and you can monkey around with the data that pops out. All I had to do then was create a filter that would look for this datatype, and remove the corresponding data from the results. I ended up with this…

  public class IgnoreHttpRequestMessageOperationFilter : IOperationFilter
  {
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, 
                      ApiDescription apiDescription)
    {
      apiDescription.ParameterDescriptions
        .Where(desc => desc.ParameterDescriptor.ParameterType 
            == typeof(HttpRequestMessage))
        .ToList()
        .ForEach(param =>
        {
          var toRemove = operation.parameters
            .SingleOrDefault(p => p.name == param.Name);

          if (null != toRemove)
            operation.parameters.Remove(toRemove);
        });
    }
  }

With that class in place, I just needed to plug this in to the swagger config file as follows...

  c.OperationFilter();

And Bob's you're uncle...

image

Wednesday 6 January 2016

Auth0 and 401 Unauthorized

Today was One of those days™ where things just didn’t work well, or rather at all. I’ve been working on an ASP.NET WebAPI that was working fine against Auth0, but needed to update it to use OWIN. It took a while to update everything and get the appropriate packages installed, and then I went through the code and ripped out the old and replaced with the new.

The API is called using a bearer token that is retrieved from Auth0 – example code below…

  var uri = ConfigurationManager.AppSettings["auth0:Domain"];
  var client = new HttpClient { BaseAddress = new Uri(uri) };
  var data = new LoginRequest
  {
    Username = model.Username,
    Password = model.Password,
    ClientId = ConfigurationManager.AppSettings["auth0:ClientId"],
    Connection = ConfigurationManager.AppSettings["auth0:Connection"],
    GrantType = "password",
    Scope = "openid"
  };

  var response = await client.PostAsJsonAsync<LoginRequest>("oauth/ro", data);

  if (response.IsSuccessStatusCode)
  {
    var result = await response.Content.ReadAsAsync<LoginResponse>();

    // Now use the response token...
  }

I’d written this code myself to call the Auth0 API as I couldn’t get the provided Auth0 code to work at all – the response I was getting back was always a null. The LoginRequest and LoginResponse objects simply mimicked the request and response data sent to and received from the Auth0 API that I was calling.

When I called this I received a token back – but then the problems started. Whenever I called my API, all I got back was a 401 Unauthorized response. It took me a long time to sort this, I even went to the bother of creating an entirely new solution with two web apps included, one to “login” to Auth0 and get a token, the other being the API I wished to call, so that I could raise a support incident with Auth0. I had a feeling that it must still be something I was doing wrong as I’ve used Auth0 for a while and to be fair it had previously been working fine until I did this upgrade.

It got to the point where I needed to get some help, and that came in the form of Reflector. I’m sure that Red-Gate should send me a free copy the number of times I’ve mentioned it in my blog. Not that I mind paying for it, it’s a fantastic tool. Anyhow, after some spelunking through the code I found that the Owin pipeline has a logging component, and also found out how to enable it. I added the following to my web.config (this was in the callee project)…

  <system.diagnostics>
    <switches>
      <add name="Microsoft.Owin" value="Verbose" />
    </switches>
  </system.diagnostics>

With that in place I then debugged my application and saw this in the debug output…

Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationMiddleware Error: 0 : Authentication failed
System.IdentityModel.Tokens.SecurityTokenException: Issuer not known.
   at Microsoft.Owin.Security.Jwt.JwtFormat.Unprotect(String protectedText)
   at Microsoft.Owin.Security.Infrastructure.AuthenticationTokenReceiveContext.DeserializeTicket(String protectedData)
   at Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationHandler.<AuthenticateCoreAsync>d__0.MoveNext()

Finally I had something to go on! I've worked a fair bit with JWT tokens over the last couple of years so happened to know that the issuer was one of the fields within the JWT, so then I knew that something was either not sending the right issuer, or when decoding the token on the API side the wrong issuer was being used. Now, just to be clear, I had made ZERO changes to my web.config between the old (non OWIN) version that worked, and this new version that didn't work. So I was suspicious to say the least.

So, I grabbed a copy of the token I was using and headed off to jwt.io which has a handy JWT decoder. You paste in the token and it will decode it for you, I saw this when I did mine…

HEADER:ALGORITHM & TOKEN TYPE

{
  "typ": "JWT",
  "alg": "HS256"
}

PAYLOAD:DATA

{
  "iss": "https://redacted.com/",
  "sub": "auth0|568bf53557c21c88287d9b03",
  "aud": "P7H7jdzZ4pSc9ifPU2XS7y03HXfcOYHG",
  "exp": 1452069269,
  "iat": 1452033269
}

The important part was that the issuer looked correct to me, so I then went looking at the API project to see what it was defined as there. In the web.config (which was simply copied from the Auth0 sample) I had this...

  <add key="auth0:ClientId" value="....." />
  <add key="auth0:ClientSecret" value="....." />
  <add key="auth0:Domain" value="redacted.com" />
  <add key="auth0:Connection" value="....." />

BINGO!

The auth0Domain value did not include the scheme or trailing slash, and this was being used by the Auth0 code as the issuer. With this knowledge I simply updated the web.config to https://redacted.com/ and sure enough my code started working again.

Hopefully this helps someone else, it took me quite a while to solve!