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!

4 comments:

jatin tyagi said...

Very good write-up. I definitely appreciate this morganskinner.com Blog . Continue the good work!
Devops Online Training

nasreen basu said...

we are offering best devops online training with job support and high quality training facilities and well expert faculty .
to Register you free demo please visit ,devops training in hyderabad

ramesh rams said...
This comment has been removed by the author.
ramesh rams said...

thank you for the post , good write up. keep posting related articles. you may also like this post devops online course