Search code examples
c#asp.net-coreidentityserver4openid-connectasp.net-core-2.1

Unable to read requestbody - identityserver callback url


For my backend I would like only to allow access to login-page via a 'secret' url otherwise just redirect the user to the webui app. For this I have Created following middleware and implementation.

MiddleWare

namespace App.Middleware
{
    public class DefaultRedirectRequest
    {
        private readonly RequestDelegate _next;
        private readonly IIdentityServerService _identityServerService;

        public DefaultRedirectRequest(RequestDelegate next, IIdentityServerService identityServerService)
        {
            _next = next;
            _identityServerService = identityServerService;
        }

    public async Task InvokeAsync(HttpContext context)
    {
        var request = context.Request;
        var response = context.Response;
        var user = context.User.Identity;

        if (!user.IsAuthenticated)
        {
            // if the Request comes from base (this App)
            if (request.Headers[HeaderNames.Origin].Count == 0)
            {
                if (request.Path.StartsWithSegments(new PathString("/secreturl")))
                {
                    await _next(context);

                    // Create url and redirect
                    response.Headers[HeaderNames.Location] = _identityServerService.CreateAuthorizeUrl();
                    response.StatusCode = StatusCodes.Status302Found;
                    response.Redirect(response.Headers[HeaderNames.Location]);
                }
                else if (request.Path.StartsWithSegments(new PathString("/signin-oidc")))
                {
                     // Sign-in..
                }
                else
                {
                    // Redirect to client app
                    response.Headers[HeaderNames.Location] = "http://localhost:5555";
                    response.StatusCode = StatusCodes.Status301MovedPermanently;

                    response.Redirect(response.Headers[HeaderNames.Location]);
                }
            }
        }
        else
        {
            await context.ChallengeAsync();
        }
    }
}

public static class DefaultRedirectRequestExtension
{
    public static IApplicationBuilder UseDefaultRedirectRequest(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<DefaultRedirectRequest>();
    }
}

}

IdentityServerService

public class IdentityServerService : IIdentityServerService
{
    public HttpClient Client { get; }

    public IdentityServerService(HttpClient client)
    {
        client.BaseAddress = new Uri("http://localhost:5000/");
        Client = client;
    }

    public string CreateAuthorizeUrl()
    {
        var request = new RequestUrl(Client.BaseAddress.AbsoluteUri + "connect/authorize");

        var url= request.CreateAuthorizeUrl(
                            clientId: "client.id",
                            responseType: "code id_token",
                            responseMode: "form_post",
                            redirectUri: "http://localhost:4444/signin-oidc",
                            state: Guid.NewGuid().ToString("N"),
                            nonce: Guid.NewGuid().ToString("N"),
                            scope: "openid profile roles offline_access");

        return url;
    }
}

Startup

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient<IIdentityServerService, IdentityServerService>();

    services
        .AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
        .AddCookie(options =>
        {
            options.SlidingExpiration = true;
        })
        .AddOpenIdConnect(options =>
        {
            options.Authority = "http://localhost:5000";
            options.RequireHttpsMetadata = false;
            options.GetClaimsFromUserInfoEndpoint = true;
            options.SaveTokens = true;
            options.ClientId = "client.id";
            options.ClientSecret = "secret";
            options.ResponseType = "code id_token";

            options.Scope.Add("api1");
            options.Scope.Add("offline_access");
            options.Scope.Add("profile");
            options.Scope.Add("roles");

            options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
            options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;

            options.Events.OnAuthorizationCodeReceived = async context =>
            {
                // Handle Here?
            };
            })
}
public void Configure(IApplicationBuilder app)
{
    app.UseDefaultRedirectRequest();
    app.UseAuthentication();
    // static files, mvc etc..
}

With this I get redirected to client app on all Urls exept the 'secret' one which direct me to idsrver. So all good so far. I can log in to idsrver but when redirecting back I just get this error message in the console

info: Microsoft.AspNetCore.Server.Kestrel[32]
      Connection id "0HLO6R5VCNBTC", Request id "0HLO6R5VCNBTC": the application completed without reading the entire request body.

It dosent hit any breakpoints so I have used fiddler for some further debugging. Here is the raw output returned from idsrver.

HTTP/1.1 200 OK
Date: Fri, 12 Jul 2019 15:32:53 GMT
Content-Type: text/html; charset=UTF-8
Server: Kestrel
Cache-Control: no-store, no-cache, max-age=0
Pragma: no-cache
Transfer-Encoding: chunked
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Set-Cookie: .AspNetCore.Identity.Application=long string.. path=/; httponly
Content-Security-Policy: default-src 'none'; script-src 'sha256-orD0/VhH8hLqrLxKHD/HUEMdwqX6/0ve7c5hspX5VJ8='
X-Content-Security-Policy: default-src 'none'; script-src 'sha256-orD0/VhH8hLqrLxKHD/HUEMdwqX6/0ve7c5hspX5VJ8='
Referrer-Policy: no-referrer

6bf
<html><head><base target='_self'/></head><body><form method='post' action='http://localhost:4444/signin-oidc'><input type='hidden' name='code' value='dda3afcefd7609714c85a49723904d7b8e6bdd3063980e6fd472231cce3ccfc8' />
<input type='hidden' name='id_token' value='eyJhbGciOiJSUzI1NiIsImtpZCI6Ijk4NDM3OUUyM0Y4MTdBMkQ1RDhGMjIwMDNGQTU5RUUyQjVFMkU1MjQiLCJ0eXAiOiJKV1QiLCJ4NXQiOiJtRU41NGotQmVpMWRqeUlBUDZXZTRyWGk1U1EifQ.eyJuYmYiOjE1NjI5NDU1NzMsImV4cCI6MTU2Mjk0NTg3MywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjoiZmVsZm9yZ2UuY29yZSIsIm5vbmNlIjoiNTUwN2EyM2ZiYTUzNDdmZDg4NzNiZDkyMTM1NzQwYTEiLCJpYXQiOjE1NjI5NDU1NzMsImNfaGFzaCI6IjZlRmVpclktSG80NjR0c2ZkRGdKRnciLCJzaWQiOiIyYTdmYzk0YmE2MTM4Mjc4ZmM0YmNmYzgwYWJjOTRlMCIsInN1YiI6ImZkNWEyZjVkLWRiY2YtNGZmMS1iYmQxLTI3ZjIwMDc0YmY4NiIsImF1dGhfdGltZSI6MTU2Mjk0NTU3MywiaWRwIjoibG9jYWwiLCJyb2xlIjpbIkFkbWluaXN0cmF0b3IiLCJGZWxmb3JnZVVzZXIiLCJGZWxmb3JnZUFkbWluaXN0cmF0b3IiXSwiYW1yIjpbInB3ZCJdfQ.l4wwJHndOaeLxQp_v7bYsoHXMbCAxjYvtCO2JT3mzfMJ6P-NSLbjI9A6yV0eO34fUwKI4uFz9TS5MYsugCS-GTT2vPSqA4uGrZsUu4QJcPsJeI9v1ljOkEC2oB-opsXWxbNvFNiXn7oMx0HUlC74gZile5eb8bc6M0qcFWAjZ5bMfiwIVwB3PSdGya7ZVPb523CgjU3nTEApS4XXEoQOdGJ0L2HU_taHcZJC8k2xUimPya2RqQbokDCfkQBPuynHhHcX661sJblGyTukxKKZO_pTESRUay1UWmnoyNy2bQXjZemTBLaNaATKPOAuHLdPT7cGnhqAeVb3l1ivo_rvWw' />
<input type='hidden' name='scope' value='openid profile roles offline_access' />
<input type='hidden' name='state' value='2c2518ebcbd540c9bfa567264a372754' />
<input type='hidden' name='session_state' value='FWpR9DfCVp2ggyGN9L-HX5sAxTpmWIxlKk9qZE5IDSw.30c9a96a37ef1dec9b19e44aba11b114' />
<noscript><button>Click to continue</button></noscript></form> 
<script>window.addEventListener('load', function() 
{document.forms[0].submit();});</script></body></html>
0

I have a branch that use the standard setup just hitting the [Authorize] attribute and the reponse looks similar and the app handles the response.

So I guess the startup (or middleware?) are unable to parse the raw requestbody? What am I missing here? how can this flow be implemented?


Solution

  • Why don't you use the OnRedirectToIdentityProvider event?

    .AddOpenIdConnect("oidc", "Open Id connect", options =>
    {
        // This event is fired when the user is about to be redirected to the login page.
        options.Events.OnRedirectToIdentityProvider = context =>
        {
            var validUrl = context.Request.Path.StartsWithSegments(new PathString("/secreturl"));                
            if (!validUrl)
            {
                context.Response.Redirect("http://localhost:5555");                            
                context.HandleResponse();
            }
            return Task.CompletedTask;
        };