Response Content-Length mismatch: too few bytes written
Asked Answered
N

3

12

My ASP.NET Core app uses "out-of-box" external login authentication. What I want to implement - on facebook challenge I want to wrap redirect url and return it as json to consume in jquery frontend. But after request ends I see 500 error in browser and next error in application console:

fail: Microsoft.AspNetCore.Server.Kestrel[13] Connection id "0HLV651D6KVJC", Request id "0HLV651D6KVJC:00000005": An unhandled exception was thrown by the application. System.InvalidOperationException: Response Content-Length mismatch: too few bytes written (0 of 470).

My external login action, nothing special to look at

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public IActionResult ExternalLogin(string provider, string returnUrl = null)
{
    // Request a redirect to the external login provider.
    var redirectUrl = Url.Action(nameof(ExternalLoginCallback), "Account", new { returnUrl });
    var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
    return Challenge(properties, provider);
}

Facebook authentication configuration:

services.AddAuthentication().AddFacebook(facebookOptions =>
{
    facebookOptions.AppId = Configuration["Authentication:Facebook:AppId"];
    facebookOptions.AppSecret = Configuration["Authentication:Facebook:AppSecret"];

    facebookOptions.Events.OnRedirectToAuthorizationEndpoint =
        async (x) =>
        {

            UTF8Encoding encoding = new UTF8Encoding();
            var content = JsonConvert.SerializeObject(new { redirect_url = x.RedirectUri });
            byte[] bytes = encoding.GetBytes(content);

            x.Response.StatusCode = 200;
            x.Response.ContentLength = bytes.Length;
            x.Response.ContentType = "text/plain";
            x.Response.Body = new MemoryStream();

            await x.Response.WriteAsync(content);
            // at this point I see that x.Response.Body.Length == 470, but message states there are 0 of 470 written
        };
});

Is there any way I could make it work?

Nathalia answered 21/4, 2020 at 19:25 Comment(3)
did not manage to fix this, so had to build a lot of views and actions to make a custom pipeline for oauth popup as a workaround.Nathalia
Why are you setting the length and then assigning the body? Just write the bytes to the x.Response.Body without replacing it. It's saying 0 because you replaced the body and wrote nothing to the actual response (the original body)Ordinarily
@Ordinarily don't remember why, probably I used some other code snippet found here. but you are right, thanks.Nathalia
N
2

Changed code to write to original response stream and it works now.

facebookOptions.Events.OnRedirectToAuthorizationEndpoint =
    async (x) =>
    {
        var content = JsonConvert.SerializeObject(new { redirect_url = x.RedirectUri });

        x.Response.StatusCode = 200;
        x.Response.ContentType = "text/plain";

        await x.Response.WriteAsync(content);
    };
Nathalia answered 5/5, 2020 at 8:7 Comment(1)
Remove the conversion to bytes, you're not using it :D.Ordinarily
S
24

This can also happen when using new C# using syntax like this:

using var ms = new MemoryStream();
using var writer = new StreamWriter(ms);
writer.WriteLine("my content");
memoryStream.Position = 0;
return File(ms, "text/plain");

in this case, the MemoryStream is accessed before the StreamWriter is flushed. Either use old syntax for the StreamWriter:

using var ms = new MemoryStream();
using (var writer = new StreamWriter(ms, Encoding.UTF8, -1, true))
{
    writer.WriteLine("my content");
}
memoryStream.Position = 0;
return File(ms, "text/plain");

or flush the writer:

using var ms = new MemoryStream();
using var writer = new StreamWriter(ms);
writer.WriteLine("my content");
writer.Flush();
memoryStream.Position = 0;
return File(ms, "text/plain");
Schiff answered 15/1, 2021 at 7:31 Comment(1)
This did the trick for me writer.Flush(); memoryStream.Position = 0;Hove
N
2

Changed code to write to original response stream and it works now.

facebookOptions.Events.OnRedirectToAuthorizationEndpoint =
    async (x) =>
    {
        var content = JsonConvert.SerializeObject(new { redirect_url = x.RedirectUri });

        x.Response.StatusCode = 200;
        x.Response.ContentType = "text/plain";

        await x.Response.WriteAsync(content);
    };
Nathalia answered 5/5, 2020 at 8:7 Comment(1)
Remove the conversion to bytes, you're not using it :D.Ordinarily
E
2

You can use something like this:

var stream = new MemoryStream();
/// writing to the stream
if (stream.CanSeek)
{
   stream.Seek(0, SeekOrigin.Begin);
}
/// then read stream
Essy answered 27/6, 2022 at 20:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.