Route passkey button through browser auth

This commit is contained in:
bulava92
2026-10-28 15:39:27 +03:00
parent 4bc1a9a213
commit 1556470bcb
3 changed files with 58 additions and 18 deletions
+56 -2
View File
@@ -13,6 +13,19 @@ private final class WeakScriptMessageHandler: NSObject, WKScriptMessageHandler {
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "shdDesktopAuth" {
let origin = message.frameInfo.securityOrigin
let trustedHosts = ["shd.xyz.su", "dev-shd.xyz.su"]
guard message.frameInfo.isMainFrame,
origin.protocol.lowercased() == "https",
trustedHosts.contains(origin.host.lowercased()) else {
return
}
let environment: MainWindowController.Environment = origin.host.lowercased() == "dev-shd.xyz.su" ? .dev : .production
controller?.signInWithPasskeyInBrowser(for: environment)
return
}
if message.name == "shdNativePrint" {
controller?.printCurrentPage()
}
@@ -123,7 +136,43 @@ final class MainWindowController: NSWindowController, NSWindowDelegate, WKNaviga
"""
let userScript = WKUserScript(source: darkScript, injectionTime: .atDocumentStart, forMainFrameOnly: false)
config.userContentController.addUserScript(userScript)
let passkeyBridgeScript = """
(function() {
document.addEventListener('click', function(event) {
var path = window.location.pathname;
while (path.length > 1 && path.endsWith('/')) {
path = path.slice(0, -1);
}
if (path !== '/login' && path !== '/login/two-factor') {
return;
}
var target = event.target instanceof Element
? event.target
: (event.target && event.target.parentElement);
if (!target || !target.closest('#btnPasskeyLogin, #twoFactorPasskey')) {
return;
}
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
var bridge = window.webkit && window.webkit.messageHandlers
? window.webkit.messageHandlers.shdDesktopAuth
: null;
if (bridge) {
bridge.postMessage('passkey-login');
}
}, true);
})();
"""
config.userContentController.addUserScript(
WKUserScript(source: passkeyBridgeScript, injectionTime: .atDocumentStart, forMainFrameOnly: true)
)
config.userContentController.add(WeakScriptMessageHandler(controller: self), name: "shdNativePrint")
config.userContentController.add(WeakScriptMessageHandler(controller: self), name: "shdDesktopAuth")
let container = NSView()
container.translatesAutoresizingMaskIntoConstraints = false
@@ -199,14 +248,18 @@ final class MainWindowController: NSWindowController, NSWindowDelegate, WKNaviga
updateTitle()
}
func signInWithPasskeyInBrowser() {
func signInWithPasskeyInBrowser(for environment: Environment) {
guard authSession == nil else { return }
guard environment == .dev else {
presentDesktopAuthError("Вход по passkey в приложении пока подключён только для DEV.")
return
}
do {
let verifier = try Self.randomBase64URLString(byteCount: 32)
let state = try Self.randomBase64URLString(byteCount: 32)
let challenge = Self.base64URLString(for: Data(SHA256.hash(data: Data(verifier.utf8))))
let environment = currentEnv
guard let authorizationURL = makeAuthorizationURL(
environment: environment,
@@ -469,6 +522,7 @@ final class MainWindowController: NSWindowController, NSWindowDelegate, WKNaviga
authSession?.cancel()
authSession = nil
webView.configuration.userContentController.removeScriptMessageHandler(forName: "shdNativePrint")
webView.configuration.userContentController.removeScriptMessageHandler(forName: "shdDesktopAuth")
AppDelegate.shared?.removeWindowController(self)
}
-14
View File
@@ -118,19 +118,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
viewMenuItem.submenu = viewMenu
mainMenu.addItem(viewMenuItem)
// Account Menu
let accountMenuItem = NSMenuItem()
let accountMenu = NSMenu(title: "Учётная запись")
let browserLoginItem = accountMenu.addItem(
withTitle: "Войти по passkey в системном браузере…",
action: #selector(onBrowserPasskeyLogin),
keyEquivalent: "l"
)
browserLoginItem.keyEquivalentModifierMask = [.command, .shift]
browserLoginItem.target = self
accountMenuItem.submenu = accountMenu
mainMenu.addItem(accountMenuItem)
// Environment Menu
let envMenuItem = NSMenuItem()
let envMenu = NSMenu(title: "Контур")
@@ -213,7 +200,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
@objc private func onGoForward() { currentActiveController?.goForward() }
@objc private func onSelectProd() { currentActiveController?.navigateToEnvironment(.production) }
@objc private func onSelectDev() { currentActiveController?.navigateToEnvironment(.dev) }
@objc private func onBrowserPasskeyLogin() { currentActiveController?.signInWithPasskeyInBrowser() }
@objc private func onShowInspector() { currentActiveController?.showInspector() }
@objc private func onShowConsole() { currentActiveController?.showConsole() }
}