aspnet-contrib / AspNet.Security.OAuth.Providers

OAuth 2.0 social authentication providers for ASP.NET Core
Apache License 2.0
2.38k stars 538 forks source link

Issue with DeviantArt Production-only #752

Closed Mike-E-angelo closed 1 year ago

Mike-E-angelo commented 1 year ago

Describe the bug

This is in regard to this issue: https://github.com/wix-incubator/DeviantArt-API/issues/210

I am getting an exception with the DeviantArt provider that only occurs in production. It appears the JsonDocument being returned is malformed. In the issue above, there appears to be an HTML page that is returned as part of the error. Note that the issue above was in .NET6 using the v6 providers of this repository. Now it appears that after updating to .NET7 with all providers updated to 7.0.0 it is trying to read this returned HTML page and throwing accordingly.

Any guidance on how to track this down would be appreciated.

Steps To reproduce

Unfortunately, this is only occurring in production 😭 Local development I am able to log in without issue. I have tried to delete all cookies and the problem persists.

Expected behaviour

Successful login. :)

Actual behaviour

The following exception is thrown and a 500 server error is presented to the user:

System.Exception: An error was encountered while handling the remote login.
 ---> System.Text.Json.JsonReaderException: '<' is an invalid start of a value. LineNumber: 0 | BytePositionInLine: 0.
   at void System.Text.Json.ThrowHelper.ThrowJsonReaderException(ref Utf8JsonReader json, ExceptionResource resource, byte nextByte, ReadOnlySpan<byte> bytes)
   at bool System.Text.Json.Utf8JsonReader.ConsumeValue(byte marker)
   at bool System.Text.Json.Utf8JsonReader.ReadFirstToken(byte first)
   at bool System.Text.Json.Utf8JsonReader.ReadSingleSegment()
   at bool System.Text.Json.Utf8JsonReader.Read()
   at void System.Text.Json.JsonDocument.Parse(ReadOnlySpan<byte> utf8JsonSpan, JsonReaderOptions readerOptions, ref MetadataDb database, ref StackRowStack stack) x 4
   at OAuthTokenResponse Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler<TOptions>.PrepareFailedOAuthTokenReponse(HttpResponseMessage response, string body)
   at async Task<OAuthTokenResponse> Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler<TOptions>.ExchangeCodeAsync(OAuthCodeExchangeContext context)
   at async Task<HandleRequestResult> Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler<TOptions>.HandleRemoteAuthenticateAsync()
   at async Task<bool> Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler<TOptions>.HandleRequestAsync()
   --- End of inner exception stack trace ---
   at async Task<bool> Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler<TOptions>.HandleRequestAsync()
   at async Task Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at async Task<bool> Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT<TContext>.ProcessRequestAsync()

System information

Azure AppServices

Additional context

Happy Holidays out there :) 🎅🎄☃

martincostello commented 1 year ago

Discounting a bug on their side (e.g. presenting an HTML “whoops” page), I would suggest turning up logging and trying to trace what exact URL/headers/payload scenario makes them give you an HTML response instead of JSON.

You could also try intercepting the response before the handler reads it and consuming it to try and log out exactly what’s in the HTML you’re getting back.

If it is a bug on our end, I’m not sure what we could do to track it down without a reliable repro.

Mike-E-angelo commented 1 year ago

Thank you for the suggestions @martincostello they are appreciated. I am deploying debug logging now to see if that surfaces anything.

Do you have an example/resource of response interception by chance? I would be interested in trying this out.

Another interesting wrinkle that I have discovered: I have 3 environments on 2 AppService machines. 1 AppService machine is for testing and the other AppService machine is for staging/production (between slots). I tried just now to configure both staging and testing to enable DeviantArt and the testing works without problem, just like my local development. Both staging/production are on the same machine and are experiencing this issue. 🤔

Mike-E-angelo commented 1 year ago

Alright I have Verbose enabled here in staging and I see the call to https://www.deviantart.com/oauth2/authorize and this looks OK and performing a curl on the resource returns a 302. The error then seems this occurs on the callback/return in /signin-deviantart but I am not seeing any calls for a JsonDocument or anything in the logs regarding a call for a resource.

Interesting to note that the call stack has the following frame:

  at OAuthTokenResponse Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler<TOptions>.PrepareFailedOAuthTokenReponse(HttpResponseMessage response, string body)

That seems to imply the token response is failed but I am not seeing a request for one.

Mike-E-angelo commented 1 year ago

FWIW I believe I was able to make the "intercept" as suggested:

https://github.com/DragonSpark/Framework/blob/dc3eee84ab8cdfb5c90709e559122d33e8f7d1f1/DragonSpark.Identity.DeviantArt/ConfigureApplication.cs#L52

The problem is that indeed the content being returned from an HTTP call is HTML and not Json which is what is expected. v6.0 gracefully handled this and displayed the Html content. .NET7 does not appear to do this. Perhaps this is an issue to file with aspnetcore repository?

Here is the HTML returned by DeviantArt on the error:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD><BODY>
<H1>403 ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
Request blocked.
We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
<BR clear="all">
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
<BR clear="all">
<HR noshade size="1px">
<PRE>
Generated by cloudfront (CloudFront)
Request ID: zYAlgqB1vrICGK6dIvvgEAqIPcVc_dPdH29edN9pu1i9mxPJ8FZMYA==
</PRE>
<ADDRESS>
</ADDRESS>
</BODY></HTML>

This is not an AspNetContrib issue so I am closing it for now. It would be nice to have the above tracked in aspnetcore, but if not that's OK too (now that I know how to intercept for any future issues).

In any case Happy Holidays to everyone out there 🎅🎄☃