Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions Sources/Panels/BrowserPanel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5993,16 +5993,27 @@ private class BrowserNavigationDelegate: NSObject, WKNavigationDelegate {
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
// WKWebView rejects all authentication challenges by default when this
// delegate method is not implemented (.rejectProtectionSpace). This
// breaks TLS client-certificate flows such as Microsoft Entra ID
// Conditional Access, which verifies device compliance via a client
// certificate stored in the system keychain by MDM enrollment.
// WKWebView's .performDefaultHandling does NOT search the system keychain
// for client identities the way Safari does — Safari's web content process
// has special entitlements that third-party apps lack. On MDM-enrolled Macs,
// Microsoft Entra ID (Conditional Access) issues a TLS client-certificate
// challenge to verify device compliance. Without an explicit keychain lookup,
// no certificate is sent and the user sees "this device needs to be under
// policy."
//
// By returning .performDefaultHandling the system's standard URL-loading
// behaviour takes over: the keychain is searched for matching client
// identities, MDM-installed root CAs are trusted, and any configured SSO
// extensions (e.g. Microsoft Enterprise SSO) can intercept the challenge.
// SecIdentityCopyPreferred runs in the app process (which has keychain
// access) and returns the preferred client identity matching the server's
// host and acceptable CA distinguished names — the same lookup Safari
// performs internally.
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate {
let host = challenge.protectionSpace.host
let issuers = challenge.protectionSpace.distinguishedNames as CFArray?
if let identity = SecIdentityCopyPreferred(host as CFString, nil, issuers) {
let credential = URLCredential(identity: identity, certificates: nil, persistence: .forSession)
completionHandler(.useCredential, credential)
return
}
}
completionHandler(.performDefaultHandling, nil)
}

Expand Down
15 changes: 13 additions & 2 deletions Sources/Panels/BrowserPopupWindowController.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import AppKit
import Bonsplit
import ObjectiveC
import Security
import WebKit

func browserPopupContentRect(
Expand Down Expand Up @@ -617,8 +618,18 @@ private class PopupNavigationDelegate: NSObject, WKNavigationDelegate {
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
// Parity with main browser: performDefaultHandling enables system keychain
// lookups, MDM client certs, and SSO extensions (e.g. Microsoft Entra ID).
// Parity with main browser: explicitly look up MDM client identity from
// the system keychain since WKWebView's .performDefaultHandling does not
// search the keychain the way Safari does.
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate {
let host = challenge.protectionSpace.host
let issuers = challenge.protectionSpace.distinguishedNames as CFArray?
if let identity = SecIdentityCopyPreferred(host as CFString, nil, issuers) {
let credential = URLCredential(identity: identity, certificates: nil, persistence: .forSession)
completionHandler(.useCredential, credential)
return
}
}
completionHandler(.performDefaultHandling, nil)
}

Expand Down