Working model · desktop-to-web SSO · no customer-side infrastructure

On-Prem Windows AD → HubSpot Knowledge Base, One Login

A concrete architecture for carrying an already-authenticated Windows user from a .NET WPF application into HubSpot private content (Private – SSO required with Segments) — with no AD FS, no Entra, no agents, no gateways, and nothing installed at customer sites. The demo further down mints real, verifiable tokens in your browser.

Prepared by Yinka Aderibigbe, August 2026.

01The constraint everything hinges on

Kerberos and NTLM — the protocols behind Windows Integrated Authentication — terminate at the customer's network boundary. HubSpot's private-content SSO (SAML or OIDC; HubSpot sunset JWT SSO for new apps in Feb 2025) can only talk to an identity provider it can reach over the internet, and the customer's on-prem AD can never be that IdP directly. The standard bridges — AD FS, Entra Connect, customer-side agents — are all excluded by your constraints. That leaves exactly one compliant trust path: the WPF application itself, which already holds the authenticated Windows identity, asserts that identity to a centrally hosted service under your control.

CUSTOMER SITE (unchanged) INTERNET On-premAD WPF app(ClickOnce) HubSpot KBPrivate+Segments WIA (Kerberos) AD FS / Entra / agents signed assertion, TLS Central IdP (OIDC, yours) OIDC trust
The banned bridges (crossed out) all try to make AD reachable from the cloud. The compliant path inverts it: identity travels outward from the already-authenticated app to a central OIDC IdP you control, and HubSpot trusts only that IdP.

02The handoff, end to end

WPF APP CENTRAL IdP BROWSER HUBSPOT 1. read UPN from Windows session 2. site-key-signed assertion {upn, site, nonce} 3. one-time token (60s, single use) 4. launch system browser → idp/start?ott=… 5. redeem OTT → IdP session cookie 7. open KB URL 8. OIDC authorize → IdP (silent, has session) 9. code → id_token(email) → KB session 6. 302 → KB with authenticated IdP session no second login OpenIddict / OIDC default browser segments enforce access
One Windows login, zero prompts after it. The user never sees a password, magic link, or registration email; HubSpot receives a standard OIDC id_token whose verified email claim matches the contact record, then applies segment permissions exactly as configured.

Run the handoff

This generates real cryptographic material in your browser — an HMAC-signed site assertion, a one-time token, and an RS256-signed OIDC id_token you can paste into any JWT debugger. Nothing is transmitted anywhere.

03Segments do the authorization — including same-domain, different-site

Your spec is explicit that email domain cannot determine site access. In this model the domain is never used for authorization at all: the central service holds the UPN → contact → customer/site mapping, writes it to HubSpot contact properties via the CRM API, and segment membership derives from those properties. Two users at the same customer domain can hold different site permissions, one user can sit in several segments, and removing a contact from a segment revokes access on their next request. Try it:

Segment simulator

Pick a signed-in user. The table shows the knowledge base exactly as HubSpot would render it — unauthorized articles are absent entirely, titles included.

Knowledge base viewAuthorized segmentsVisible to this user

04What changes in the WPF application

One small client, shipped through your existing ClickOnce channel. No customer AD is touched — the app only reads the identity Windows already established.

// Identity: read, never re-authenticate
var upn  = UserPrincipal.Current?.UserPrincipalName
           ?? WindowsIdentity.GetCurrent().Name;

// Assertion: signed with this site's provisioned key (rotatable, revocable)
var payload = new { upn, site = Config.SiteId, nonce = Guid.NewGuid(), iat = Now() };
var assertion = Sign(payload, Config.SiteKey);          // HMAC-SHA256 or per-site cert

// Exchange for a one-time token, then hand off to the default browser
var ott = await idp.PostAsync("/handoff/token", assertion);
Process.Start(new ProcessStartInfo {
    FileName = $"{Config.IdpUrl}/start?ott={ott}&return={KbUrl}",
    UseShellExecute = true                              // system browser, not embedded
});

05The central service (yours, nothing at customer sites)

06Mapped to your acceptance criteria

This page is a working model prepared for the project discussion: the flows, code, and token formats are the real proposed design; the demo above executes the cryptographic steps locally in your browser so the mechanism can be inspected, not just described. HubSpot protocol facts (SAML/OIDC support, segment-gated private content, JWT sunset) verified against HubSpot's current documentation, August 2026.