0

We have a homegrown webapp A and a 3rd party webapp B. Both are relying parties within our on-prem ADFS 4.0 server on a Windows 2019 Datacenter.

Webapp A uses WS-Federation and webapp B probably uses SAML 2.0 but not 100% sure. Webapp A has no signature certificate. Webapp B has a valid signature certificate.

A user can sign into webapp A and webapp B and sign out without any issues as long as this occurs in different browser sessions.

But if users are in webapp A and open another browser tab to go to webapp B, and try to sign out from webapp A, they get an error "MSIS7054: The SAML logout did not complete properly."

And ADFS Event Viewer shows the below exception:

The Federation Service encountered an error while processing the SAML authentication request. 

Additional Data 
Exception details: 
Microsoft.IdentityModel.Protocols.XmlSignature.SignatureVerificationFailedException: ID4037: The key needed to verify the signature could not be resolved from the following security key identifier 'SecurityKeyIdentifier
    (
    IsReadOnly = False,
    Count = 1,
    Clause[0] = Microsoft.IdentityServer.Tokens.MSISSecurityKeyIdentifierClause
    )
'. Ensure that the SecurityTokenResolver is populated with the required key.
   at Microsoft.IdentityModel.Protocols.XmlSignature.EnvelopedSignatureReader.ResolveSigningCredentials()
   at Microsoft.IdentityModel.Protocols.XmlSignature.EnvelopedSignatureReader.OnEndOfRootElement()
   at Microsoft.IdentityModel.Protocols.XmlSignature.EnvelopedSignatureReader.Read()
   at System.Xml.XmlReader.ReadEndElement()
   at Microsoft.IdentityServer.Protocols.Saml.SamlProtocolSerializer.ReadLogoutRequest(XmlReader reader)
   at Microsoft.IdentityServer.Protocols.Saml.HttpSamlBindingSerializer.ReadProtocolMessage(String encodedSamlMessage)
   at Microsoft.IdentityServer.Protocols.Saml.Contract.SamlContractUtility.CreateSamlMessage(MSISSamlBindingMessage message)
   at Microsoft.IdentityServer.Web.Protocols.Saml.SamlProtocolManager.Logout(HttpSamlMessage logoutMessage, String sessionState, String logoutState, Boolean partialLogout, Boolean isUrlTranslationNeeded, HttpSamlMessage& newLogoutMessage, String& newSessionState, String& newLogoutState, Boolean& validLogoutRequest)

Webapp A is a dotnet core MVC app. Here is the sign out code:

[Authorize]
public async Task SignOut()
{
    //redirect to /signoutcallback after signout
    await SignOutCustom("/signoutcallback");
}

[Authorize]
public async Task SignOutCustom(string redirectUri)
{
    await HttpContext.SignOutAsync("Cookies");
    var prop = new AuthenticationProperties { RedirectUri = redirectUri };

    //redirect to provided target
    await HttpContext.SignOutAsync("WsFederation", prop);
}

[AllowAnonymous]
public ActionResult SignOutCallback()
{
    if (User.Identity.IsAuthenticated)
    {
        // Redirect to home page if the user is authenticated.
        return RedirectToAction("Index", "Home");
    }

    return View();
}

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
    })
    .AddWsFederation(options =>
    {
        options.Wtrealm = Configuration["Federation:idaWtrealm"];
        options.MetadataAddress = Configuration["Federation:idaADFSMetadata"];
    })
    .AddCookie();

    Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;

    services.AddControllersWithViews(options =>
    {
        var policy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();
        options.Filters.Add(new AuthorizeFilter(policy));
    });

    services.AddRazorPages();
    services.AddRouting(options => options.LowercaseUrls = true);
    services.AddHttpContextAccessor();

    // Add functionality to inject IOptions<T>
    services.AddOptions();

    // Add our Config object so it can be injected
    services.Configure<EndpointOptions>(Configuration.GetSection("Endpoints"));
}
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/home/error");
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}");
            endpoints.MapRazorPages();
        });
    }
joym8
  • 4,014
  • 3
  • 50
  • 93

1 Answers1

0

Make sure the certificate from Webapp B is installed to the certificate store on the sever Webapp A runs. In the Web.config for Webapp A add a certificate reference (update the values in the curly braces):

<serviceCertificate>
        <certificateReference x509FindType="FindByThumbprint" findValue="{{WebappB_Cert_Thumbprint}}" storeLocation="{{LocalMachine}}" storeName="{{My}}" />
</serviceCertificate>

https://learn.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/windows-identity-foundation/certificatereference

vvvv4d
  • 3,881
  • 1
  • 14
  • 18
  • 1
    Webapp A is an Azure app service, so there is no server. I can try editing web.config. Webapp B is hosted in AWS VM. – joym8 Sep 03 '20 at 12:54
  • @joym8 you can add a certificate to Azure App Services, use these steps: https://learn.microsoft.com/en-us/azure/app-service/configure-ssl-certificate. Then you can update the Web.config. – vvvv4d Sep 08 '20 at 17:03