Duende.IdentityServer.Admin icon indicating copy to clipboard operation
Duende.IdentityServer.Admin copied to clipboard

Adding Consumer in Project Causes SSL Partial Chain Failure

Open darthmolen opened this issue 2 years ago • 2 comments

Question

Using Docker. Admin works with certificates installed.

When adding a consumer to the project, we get a "SSL Partial Chain Error". identity provider base url is set to "https://sts.skoruba.local".

As mentioned, admin can be logged into.

image

darthmolen avatar Jun 12 '22 18:06 darthmolen

I got past this doing a number of steps:

Found this article on stackoverflow that clued me in on what was going on. Went through the sts code and found the following 2 events:

  • OnRedirectToIdentityProvider
  • OnRedirectToIdentityProviderForSignOut

added those in.

.AddOpenIdConnect("oidc", options =>
{
    options.Authority = identityOptions.OpenId.Authority;
    options.ClientId = identityOptions.OpenId.ClientId;
    options.ClientSecret = identityOptions.OpenId.ClientSecret;
    options.ResponseType = identityOptions.OpenId.ResponseType;
    options.SaveTokens = identityOptions.OpenId.SaveTokens;
    options.RequireHttpsMetadata = identityOptions.OpenId.RequireHttpsMetadata;

    options.GetClaimsFromUserInfoEndpoint = true;

    // https://github.com/AzureAD/microsoft-identity-web/issues/115#issuecomment-618984571
    options.Events = new OpenIdConnectEvents
    {
        OnRedirectToIdentityProvider = context => OnRedirectToIdentityProvider(context, identityOptions),
        OnRedirectToIdentityProviderForSignOut = context => OnRedirectForSignOut(context, identityOptions)
    };
});

/// ...

static Task OnRedirectToIdentityProvider(RedirectContext context, IdentityServerOptions adminConfiguration)
{
    if (!string.IsNullOrEmpty(adminConfiguration.OpenId.SignInRedirectUri))
    {
        var fullUri = $"https://{context.HttpContext.Request.Host}{adminConfiguration.OpenId.SignInRedirectUri}";
        context.ProtocolMessage.RedirectUri = fullUri;
    }

    return Task.CompletedTask;
}

static Task OnRedirectForSignOut(RedirectContext context, IdentityServerOptions adminConfiguration)
{
    if (!string.IsNullOrEmpty(adminConfiguration.OpenId.SignOutRedirectUri))
    {
        var fullUri = $"https://{context.HttpContext.Request.Host}{adminConfiguration.OpenId.SignOutRedirectUri}";
        context.ProtocolMessage.PostLogoutRedirectUri = fullUri;
    }

    return Task.CompletedTask;
}

Also had to go source the DockerHelper code to import the ca cert

    public class DockerHelpers
    {
        public static void UpdateCaCertificates()
        {
            "update-ca-certificates".Bash();
        }

        public static void ApplyDockerConfiguration(IConfiguration configuration)
        {
            DockerConfiguration dockerConfiguration = configuration.GetSection("DockerConfiguration").Get<DockerConfiguration>();
            if (dockerConfiguration != null && dockerConfiguration.UpdateCaCertificate)
            {
                UpdateCaCertificates();
            }
        }
    }

Also had to be sure that the certs were the certs generated by mkcert and were mapped via volumes.

  pakilti.identityserver.customer.admin:
    image: ${DOCKER_REGISTRY-}pakiltiidentityservercustomeradmin
    build:
      context: .
      dockerfile: src/Pakilti.IdentityServer.Customer.Admin/Dockerfile
    environment:
      - VIRTUAL_HOST=customer-admin.skoruba.local
      - DockerConfiguration__UpdateCaCertificate=true
    container_name: pakilti-identity-server-customer-admin
    volumes:
      - './shared/nginx/certs/cacerts.crt:/usr/local/share/ca-certificates/cacerts.crt'
    networks:
      identityserverui:
        aliases:
          - customer-admin.skoruba.local

darthmolen avatar Jun 13 '22 21:06 darthmolen

New problem though:

warn: Microsoft.AspNetCore.Http.ResponseCookies[1]
      The cookie '.AspNetCore.OpenIdConnect.Nonce.CfDJ8Ev4r73UydVDoDKIdodzhZzaMQzXuSYN7LxmRWFmWKzvUqeBmkeCYmynh32jRw-9I8ZKzBJm4WahAP-jDHPFnBCHyubvXjzpyix1yujzklhOR3dTiH9N_euhDD1GeyF4CjD_xn1HdylMj2S-rD-7qjNRsUxtOWXIZX6czvqbM5MTyxJzQMiakG5uMx_oCNfpFCNdvb2GnxSZmnnhHluPL2u4tjeNJn-jEDTAS15lwE1KhxhysyMJUXBQH0TZ2unQAH5yEo_Cg738SgoyHoXBEPI' has set 'SameSite=None' and must also set 'Secure'.
warn: Microsoft.AspNetCore.Http.ResponseCookies[1]
      The cookie '.AspNetCore.Correlation.Vumn2KQaKvh3Pk48WKw8hgGVDL7wL2jpMcfoyZS2oYc' has set 'SameSite=None' and must also set 'Secure'.
warn: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[15]
      '.AspNetCore.Correlation.Vumn2KQaKvh3Pk48WKw8hgGVDL7wL2jpMcfoyZS2oYc' cookie not found.
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
      An unhandled exception has occurred while executing the request.
      System.Exception: An error was encountered while handling the remote login.
       ---> System.Exception: Correlation failed.
         --- End of inner exception stack trace ---
         at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.HandleRequestAsync()
         at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

even after importing the authentication helpers for cookies.

builder.Services.Configure<CookiePolicyOptions>(options =>
{
    //options.Secure = CookieSecurePolicy.Always;
    options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
    options.Secure = CookieSecurePolicy.SameAsRequest;
    options.OnAppendCookie = cookieContext =>
        AuthenticationHelpers.CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
    options.OnDeleteCookie = cookieContext =>
        AuthenticationHelpers.CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
});

They seriously make working in a container almost impossible....

What did I miss @skoruba ?

darthmolen avatar Jun 13 '22 21:06 darthmolen

I resolved this by implementing the docker helpers in my own code and also setting the OpenID stuff appropriately.

builder.Services.Configure<CookiePolicyOptions>(options =>
{
    //options.Secure = CookieSecurePolicy.Always;
    options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
    options.Secure = CookieSecurePolicy.Always;
    options.OnAppendCookie = cookieContext =>
        AuthenticationHelpers.CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
    options.OnDeleteCookie = cookieContext =>
        AuthenticationHelpers.CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
});

var identityOptions = builder.Configuration.GetSection("IdentityServer").Get<IdentityServerOptions>();
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = AuthenticationConsts.OidcAuthenticationScheme;

    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultForbidScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => {
    options.Cookie.Name = identityOptions.CookieName;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
})
//.AddJwtBearer(options => {
//    options.Authority = identityOptions.OpenId.Authority;
//    options.RequireHttpsMetadata = identityOptions.OpenId.RequireHttpsMetadata;
//    options.Audience = "pakilticustomeradmin";
//});
.AddOpenIdConnect(AuthenticationConsts.OidcAuthenticationScheme, options =>
{
    options.Authority = identityOptions.OpenId.Authority;
    options.ClientId = identityOptions.OpenId.ClientId;
    options.ClientSecret = identityOptions.OpenId.ClientSecret;
    options.ResponseType = identityOptions.OpenId.ResponseType;
    options.RequireHttpsMetadata = identityOptions.OpenId.RequireHttpsMetadata;
    options.NonceCookie.SecurePolicy = CookieSecurePolicy.Always;
    options.CorrelationCookie.SecurePolicy = CookieSecurePolicy.Always;
    options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;

    options.Scope.Clear();
    foreach (var scope in identityOptions.OpenId.Scopes)
    {
        options.Scope.Add(scope);
    }

    options.ClaimActions.MapJsonKey(identityOptions.OpenId.TokenValidationClaimRole, identityOptions.OpenId.TokenValidationClaimRole, identityOptions.OpenId.TokenValidationClaimRole);

    options.SaveTokens = identityOptions.OpenId.SaveTokens;

    options.GetClaimsFromUserInfoEndpoint = true;

    options.TokenValidationParameters = new TokenValidationParameters
    {
        NameClaimType = identityOptions.OpenId.TokenValidationClaimName,
        RoleClaimType = identityOptions.OpenId.TokenValidationClaimRole
    };

    // https://github.com/AzureAD/microsoft-identity-web/issues/115#issuecomment-618984571
    options.Events = new OpenIdConnectEvents
    {
        OnMessageReceived = context => OnMessageReceived(context, identityOptions),
        OnRedirectToIdentityProvider = context => OnRedirectToIdentityProvider(context, identityOptions),
        OnRedirectToIdentityProviderForSignOut = context => OnRedirectForSignOut(context, identityOptions)
    }; 
});

builder.Services.AddAuthorization(options =>
{
    // By default, all incoming requests will be authorized according to the default policy.
    options.FallbackPolicy = options.DefaultPolicy;
});

DockerHelpers.ApplyDockerConfiguration(builder.Configuration);
var app = builder.Build();

app.UseForwardedHeaders();

darthmolen avatar Aug 26 '22 01:08 darthmolen