Client Certificate Authentication?

Product: PowerShell Universal
Version: 2.3.0

How does one use the Client Certificate Authentication after enabling it?

That’s a miss on my part. I’ll get this added today.

Can you point me in the direction of some Cmdlets?

Would really appreciate any information you could provide on this feature.

It looks like something is wrong with this functionality and it may require a 2.3.2 release to resolve it. I’ll update this thread when the functionality is available and documented.

2 Likes

Any updates on this? Even some information on how the feature will work or be configured would be useful.

I’m currently designing, and testing, a workflow and API for certificate issuance and management and want to make sure it will be compatible with the PSU Client Authentication design.

Check out the doc here: Client Certificate - PowerShell Universal

Hi guys! I’m just starting to learn about client certificate authentication as I suspect it’s something I need to know about. :slight_smile:

In the documentation, the authenticating user has a bunch of certificates as claims, but our users don’t have any certificates as claims. Just the standard claims and the Azure AD groups they’re a member of, since we enabled that.

Do certs only show as claims if you have ClientCertificate set to true in the settings? Or do they only show as claims if you’ve uploaded a certificate to the Azure App Registration? Or am I missing something else?

Cheers,
Matt

They will only have claims if ClientCertificate is set to true and the require cert setting is set. When user connects through a browser, it will prompt for a cert to provide the server. If you are using something like Invoke-WebRequest you can provide a certificate as a parameter to the web request.

Certificate authentication takes place in the HTTP stack before it reaches the server so it behaves a little differently than other authentication types. For example, you can use anonymous endpoints because there’s no way to validate that before the HTTP connection is made.

What if you want to make Client Certificate Authentication optional? Is that not supported?

Yeah I have the same question around making certificate auth optional.

We are playing with Adobe Sign webhooks, and the only security they support is certificates. So we’d want to be able to authenticate those calls with a cert, but just use normal OIDC auth with no cert for daily use. Is that even possible?

Example: Module ngx_http_ssl_module

https://datatracker.ietf.org/doc/html/rfc5246#section-7.4.6

I hate ASP.NET sometimes:
Configure certificate authentication in ASP.NET Core | Microsoft Docs
-I’m use to other stacks where ones arm isn’t held behind their backs by annoying middleware.

What I envisaged was optional certificate authentication and a separation of authentication and authorisation with the authorisation of a route being done after authentication which, in this case, would be done after TLS negotiation when HTTP is available.

I could have sworn I’ve done this before as described in the ‘alternatives’ section of Mutual TLS Authentication (Client Certificate Authentication) · Issue #315 · ironmansoftware/issues · GitHub

It seems different sockets are used in this example:

Yeah it’s a bit weird because we are treating client cert auth like any other authentication mechanism but since the auth happens before anything else, it returns that the user is authenticated and the other auth providers don’t kick in.

I’ll have to look into it more because now I understand a bit more what you are after. It’s really that the client cert should be optional allowing just authorization rather than merely for authentication.

2 Likes

@adam
We’ve also been evaluating Pode, and it seems to support this scenario, so I looked into how:

Example Pode server:

Start-PodeServer {
    Add-PodeEndpoint -Address '127.0.0.1' -Port 8443 -Protocol Https -SelfSigned -AllowClientCertificate

    New-PodeAuthScheme -ClientCertificate | Add-PodeAuth -Name 'CertLogin' -Sessionless -ScriptBlock {
        Param(
            $Certificate,
            $Errors
        )
        # check if the client's cert is valid
        if($Certificate.Thumbprint -eq 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF') {
            return @{
                User = @{
                    ID = 'M0R7Y302'
                    Name = 'Morty'
                    Type = 'Human'
                }
            }
        } else {
            return @{ Message = 'Invalid Certificate supplied' }
        }
    }

    Add-PodeRoute -Method Get -Path '/api/public' -ScriptBlock {
        Write-PodeJsonResponse -Value @{ 'success' = $true }
    }

    Add-PodeRoute -Method Get -Path '/api/private' -Authentication 'CertLogin' -ScriptBlock {
        Write-PodeJsonResponse -Value @{ 'success' = $true }
    }
}

Test Client:

Invoke-RestMethod -Uri 'https://127.0.0.1:8443/api/private' -SkipCertificateCheck -CertificateThumbprint 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'
Invoke-RestMethod -Uri 'https://127.0.0.1:8443/api/public' -SkipCertificateCheck

Both succeed with a single socket.

Pode’s Server TLS revolves around SslStream.AuthenticateAsServerAsync

The actual AuthenticateAsServerAsync(X509Certificate, Boolean, Boolean) method is basically just a wrapper around SSPI SChannel, which is far as I went.

If I get time, I’ll setup an interception proxy because one might assume SChannel achieves this config by simply renegotiating via HelloRequest, like a number of other implementations do, but this is not recommended.