We have a Windows based desktop application which connects to a WCF REST service. This service supports Basic auth (over HTTPS), Bearer token, and SPNEGO. SPNEGO is what the desktop application uses so that the users do not have enter in credentials when running the application.
We are looking for ways around using SPNEGO. For instance, is there a way that the Windows client can get some kind of signed token from the system or domain controller using the logged in credentials, and then pass that token to custom endpoint on our service which could use that token to lookup the user and then send back an application bearer token?
A couple of the reasons we want to depreciate SPNEGO:
We are looking for ways around using SPNEGO. For instance, is there a way that the Windows client can get some kind of signed token from the system or domain controller using the logged in credentials, and then pass that token to custom endpoint on our service which could use that token to lookup the user and then send back an application bearer token?
The only kind of "signed token" that Active Directory natively supports is Kerberos. It might not necessarily be used as HTTP SPNEGO – e.g. you could directly call SSPI InitSecurityContext(), then ship the Kerberos ticket as part of HTTP request header or even payload – but it's unavoidably going to begin with Kerberos.
Other than that, your plan is reasonable; it's not unheard of to have SPNEGO only enabled for the single "exchange for a bearer token" endpoint, which could be provided by a completely separate service if needed.
In fact, I believe Windows already has such a service for web-apps – that's ADFS, which allows you to exchange an HTTP SPNEGO request for a signed SAML2.0 assertion. Although SAML2.0 is maybe overly complicated for your use case, but the fundamental idea is there. (Yes, I suppose ADFS is part of Active Directory, so the answer to your question would be "yes, it's called ADFS". Though I've also set up SimpleSAMLphp for my private use to do the same, and several other SAML2 and OAuth2 IdPs such as Keycloak can do this.)
You can use HTTP SPNEGO behind load balancers, as long as all backend services share the same service key – e.g. with a Linux-based web server or anything else that uses keytabs instead of Windows 'native' SSPI, you could issue the service keytab once and distribute it across all of your web servers. (With Windows clients it works very much like TLS, where the service principal name only depends on the URL.
And if you begin to use SSPI or GSS-API directly, you're in full control of the service principal that Kerberos will be told to expect (again, much like with TLS libraries). So I'd say you could avoid most of your problems by directly going to SSPI (or the .NET API that wraps it) and having it issue the Kerberos tokens, then sending them in the 'WWW-Authenticate: Negotiate' header; that's really all it takes to implement the client side of Kerberos authentication for HTTP.
import spnego
ctx = spnego.client(hostname="foo.example.com",
service="HTTP",
protocol="negotiate")
token = b64encode(ctx.step())
resp = requests.get("https://foo.example.com/auth",
headers={"WWW-Authenticate": f"Negotiate {token}"})