diff --git a/README.md b/README.md index b1ce552b..c6089f1a 100644 --- a/README.md +++ b/README.md @@ -224,6 +224,22 @@ The application writes logs to - `~/Library/Containers/eu.web-eid.web-eid-safari/Data/Library/Application\ Support/RIA/web-eid-safari/web-eid-safari.log` of Safari in macOS - `C:/Users//AppData/Local/RIA/web-eid/web-eid.log` in Windows. +The Safari extension and its containing app also emit structured log messages via the macOS unified logging system. To stream them live: + + log stream --predicate 'subsystem BEGINSWITH "eu.web-eid.web-eid-safari"' --level debug + +To query past messages (e.g. the last hour): + + log show --predicate 'subsystem BEGINSWITH "eu.web-eid.web-eid-safari"' --last 1h + +By default, request and response payloads are redacted in the log output. To reveal them, sign and install `src/mac/web-eid-debug-logging.mobileconfig`: + + security cms -S -N "Apple Development: your@email.com (TEAMID)" \ + -i src/mac/web-eid-debug-logging.mobileconfig \ + -o /tmp/web-eid-debug-logging-signed.mobileconfig + +Open the signed file — macOS will prompt to install it via System Settings → Privacy & Security → Profiles. The profile enables private data system-wide; remove it when done. + ## Internal design The Web eID native application is built with the [Qt](https://www.qt.io/) framework. It consists of diff --git a/src/mac/main.mm b/src/mac/main.mm index 7908efc3..1dc91913 100644 --- a/src/mac/main.mm +++ b/src/mac/main.mm @@ -31,9 +31,12 @@ #import #import +#import #include "shared.hpp" +static os_log_t logger = os_log_create("eu.web-eid.web-eid-safari", "app"); + #include @implementation NSApplication (MacController) @@ -114,7 +117,7 @@ - (void)notificationEvent:(NSNotification*)notification { NSString *nonce = notification.object; NSDictionary *req = takeValue(nonce); - NSLog(@"web-eid-safari: msg from extension nonce (%@) request: %@", nonce, req); + os_log(logger, "msg from extension nonce (%{public}@) request: %@", nonce, req); if (req == nil) { return; } @@ -141,7 +144,7 @@ Controller controller({ } } - NSLog(@"web-eid-safari: msg to extension nonce (%@) request: %@", nonce, resp); + os_log(logger, "msg to extension nonce (%{public}@) response: %@", nonce, resp); setValue(nonce, resp); [NSDistributedNotificationCenter.defaultCenter postNotificationName:WebEidExtension object:nonce userInfo:nil deliverImmediately:YES]; QCoreApplication::quit(); @@ -174,6 +177,9 @@ int main(int argc, char* argv[]) SafariApplication app(argc, argv, QStringLiteral("web-eid-safari")); auto appPtr = &app; + // Sender is not authenticated; DNCF is system-wide. This is intentional: the notification + // is a wake-up signal only — payload is read from the App Group NSUserDefaults, which is + // gated by team-ID entitlement and inaccessible to other processes. [NSDistributedNotificationCenter.defaultCenter addObserver:NSApp selector:@selector(notificationEvent:) name:WebEidApp object:nil]; id starting = [getUserDefaults() objectForKey:WebEidStarting]; diff --git a/src/mac/safari-extension.mm b/src/mac/safari-extension.mm index 01a5c054..70843bf8 100644 --- a/src/mac/safari-extension.mm +++ b/src/mac/safari-extension.mm @@ -25,6 +25,9 @@ #include "shared.hpp" #import +#import + +static os_log_t logger = os_log_create("eu.web-eid.web-eid-safari.web-eid-safari-extension", "extension"); @interface SafariWebExtensionHandler : NSObject { NSMutableDictionary *contexts; @@ -35,8 +38,11 @@ @implementation SafariWebExtensionHandler - (id)init { if (self = [super init]) { - NSLog(@"web-eid-safari-extension: starting"); + os_log(logger, "starting"); contexts = [[NSMutableDictionary alloc] init]; + // Sender is not authenticated; DNCF is system-wide. This is intentional: the notification + // is a wake-up signal only — payload is read from the App Group NSUserDefaults, which is + // gated by team-ID entitlement and inaccessible to other processes. [NSDistributedNotificationCenter.defaultCenter addObserver:self selector:@selector(notificationEvent:) name:WebEidExtension object:nil]; } return self; @@ -44,7 +50,7 @@ - (id)init { - (void)dealloc { - NSLog(@"web-eid-safari-extension: stopping"); + os_log(logger, "stopping"); [NSDistributedNotificationCenter.defaultCenter removeObserver:self name:WebEidExtension object:nil]; } @@ -53,14 +59,19 @@ - (void)notificationEvent:(NSNotification*)notification // Received notification from App NSString *nonce = notification.object; NSDictionary *resp = takeValue(nonce); - NSLog(@"web-eid-safari-extension: from app nonce (%@) request: %@", nonce, resp); + os_log(logger, "from app nonce (%{public}@) request: %@", nonce, resp); if (resp == nil) { return; } + // Wait for the app to fully exit before returning the response to the page. + // The app is single-shot and calls QCoreApplication::quit() after posting the response. + // If we return early and the page immediately sends a follow-up request, execNativeApp + // would race with the still-shutting-down instance: launchApplication may no-op against + // it and the WebEidStarting handshake in Loop 1 would then time out. for (int i = 0; i < 20 && [NSRunningApplication runningApplicationsWithBundleIdentifier:WebEidApp].count > 0; ++i) { [NSThread sleepForTimeInterval:0.5]; - NSLog(@"web-eid-safari-extension: web-eid-safari is still running"); + os_log(logger, "web-eid-safari is still running"); } // Forward to background script @@ -75,31 +86,31 @@ - (BOOL)execNativeApp { NSURL *appURL = [NSWorkspace.sharedWorkspace URLForApplicationWithBundleIdentifier:WebEidApp]; if (appURL == nil) { - NSLog(@"web-eid-safari-extension: failed to get app url"); + os_log_error(logger, "failed to get app url"); return NO; } setValue(WebEidStarting, @(true)); if (![NSWorkspace.sharedWorkspace launchApplication:appURL.path]) { - NSLog(@"web-eid-safari-extension: failed to start app"); + os_log_error(logger, "failed to start app"); return NO; } - NSLog(@"web-eid-safari-extension: started app"); + os_log(logger, "started app"); for (int i = 0; i < 20 && [getUserDefaults() boolForKey:WebEidStarting]; ++i) { [NSThread sleepForTimeInterval:0.5]; - NSLog(@"web-eid-safari-extension: waiting to be running %@", [getUserDefaults() objectForKey:WebEidStarting]); + os_log(logger, "waiting to be running %{public}@", [getUserDefaults() objectForKey:WebEidStarting]); } if ([(NSNumber*)takeValue(WebEidStarting) boolValue]) { - NSLog(@"web-eid-safari-extension: timeout to start app"); + os_log_error(logger, "timeout to start app"); return NO; } - NSLog(@"web-eid-safari-extension: app executed"); + os_log(logger, "app executed"); return YES; } - (void)beginRequestWithExtensionContext:(NSExtensionContext*)context { id message = [context.inputItems.firstObject userInfo][SFExtensionMessageKey]; - NSLog(@"web-eid-safari-extension: msg from background.js %@", message); + os_log(logger, "msg from background.js %@", message); if ([@"status" isEqualToString:message[@"command"]]) { NSString *version = [NSString stringWithFormat:@"%@+%@", @@ -119,7 +130,7 @@ - (void)beginRequestWithExtensionContext:(NSExtensionContext*)context return; } - NSLog(@"web-eid-safari-extension: sending message to app %@", message); + os_log(logger, "sending message to app %@", message); // Save context NSString *nonce = [[[NSUUID alloc] init] UUIDString]; diff --git a/src/mac/web-eid-debug-logging.mobileconfig b/src/mac/web-eid-debug-logging.mobileconfig new file mode 100644 index 00000000..e0686467 --- /dev/null +++ b/src/mac/web-eid-debug-logging.mobileconfig @@ -0,0 +1,33 @@ + + + + + PayloadContent + + + PayloadType + com.apple.system.logging + PayloadIdentifier + eu.web-eid.web-eid-safari.logging + PayloadUUID + 4F8A1B2C-3D4E-5F6A-7B8C-9D0E1F2A3B4C + PayloadVersion + 1 + Enable-Private-Data + + + + PayloadDisplayName + web-eid Debug Logging + PayloadDescription + Enables private data in os_log output system-wide. Install for debugging only; remove when done. + PayloadIdentifier + eu.web-eid.debug-logging-profile + PayloadType + Configuration + PayloadUUID + 5A9B2C3D-4E5F-6A7B-8C9D-0E1F2A3B4C5D + PayloadVersion + 1 + +