How to Configure IIS to Accept Client Certificates Without Browser Prompts in Mixed Web Application Scenarios


5 views

When implementing client certificate authentication for WCF services within a web application, we often face an awkward situation where browsers unnecessarily prompt users for certificate selection - even when only specific endpoints require authentication. This occurs because browsers detect the Accept (rather than Require) SSL setting and proactively offer certificate selection.

First, verify your IIS SSL settings:

// Web.config snippet for SSL settings
<system.webServer>
    <security>
        <access sslFlags="Ssl, SslNegotiateCert" />
    </security>
</system.webServer>

This configuration tells IIS to accept but not require certificates at the application level.

Create a separate <location> tag for your WCF endpoint:

<location path="YourService.svc">
    <system.webServer>
        <security>
            <access sslFlags="Ssl, SslRequireCert" />
        </security>
    </system.webServer>
</location>

For Chrome/Edge, implement this meta tag on regular pages:

<meta http-equiv="X-UA-Compatible" content="certificate=no-prompt" />

For Firefox, create a configuration file (firefox.cfg) with:

//
lockPref("security.default_personal_cert", "Select Automatically");

Ensure your WCF binding explicitly requires certificates:

<bindings>
    <wsHttpBinding>
        <binding name="CertBinding">
            <security mode="TransportWithMessageCredential">
                <transport clientCredentialType="Certificate"/>
                <message clientCredentialType="Certificate"/>
            </security>
        </binding>
    </wsHttpBinding>
</bindings>

Here's how to programmatically check certificates in your service:

public class YourService : IYourService
{
    public string SecureOperation()
    {
        var cert = OperationContext.Current.ServiceSecurityContext
                   .AuthorizationContext.ClaimSets
                   .OfType<X509CertificateClaimSet>()
                   .FirstOrDefault()?.X509Certificate;
        
        if (cert == null) 
            throw new SecurityException("Certificate required");
        
        // Certificate validation logic here
        return "Authorized operation";
    }
}

For complex scenarios, use URL Rewrite to modify SSL requirements:

<rewrite>
    <outboundRules>
        <rule name="AddCertHeader" patternSyntax="Wildcard">
            <match serverVariable="RESPONSE_X_RequireCert" pattern="*"/>
            <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                <add input="{URL}" pattern="/YourService.svc*"/>
            </conditions>
            <action type="Rewrite" value="true"/>
        </rule>
    </outboundRules>
</rewrite>

When implementing client certificate authentication for WCF services in IIS, a common pain point occurs when browsers unexpectedly prompt users to select certificates - even for non-certificate-required parts of the web application. This behavior stems from how IIS and browsers handle SSL negotiation.

The key lies in IIS's SSL Settings configuration hierarchy:

  • Require: Forces client certificate and triggers browser prompt
  • Accept: Allows but doesn't require certificates (ideal for WCF)
  • Ignore: Disables client certificates completely

Here's how to properly configure your IIS site:

<system.webServer>
    <security>
        <access sslFlags="Ssl, SslNegotiateCert" />
    </security>
</system.webServer>

For your WCF service endpoint, use this binding configuration:

<bindings>
    <wsHttpBinding>
        <binding name="CertBinding">
            <security mode="Transport">
                <transport clientCredentialType="Certificate" />
            </security>
        </binding>
    </wsHttpBinding>
</bindings>

Different browsers handle SSL negotiations differently:

  • Chrome: Will prompt if any client cert exists in store
  • Firefox: Respects IIS "Accept" setting better
  • Edge: Similar to Chrome but allows more configuration

For stubborn cases, implement URL rewriting to only require certificates for specific paths:

<rewrite>
    <rules>
        <rule name="RequireCertForWCF" patternSyntax="Wildcard">
            <match url="*/YourService.svc*" />
            <serverVariables>
                <set name="SSL_REQUIRE_CERT" value="true" />
            </serverVariables>
            <action type="None" />
        </rule>
    </rules>
</rewrite>

Use this PowerShell command to verify certificate behavior:

Test-NetConnection -ComputerName yourserver.com -Port 443 -InformationLevel Detailed