Skip to content
Open
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
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<USER>/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
Expand Down
10 changes: 8 additions & 2 deletions src/mac/main.mm
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@

#import <Cocoa/Cocoa.h>
#import <SafariServices/SafariServices.h>
#import <os/log.h>

#include "shared.hpp"

static os_log_t logger = os_log_create("eu.web-eid.web-eid-safari", "app");

#include <iostream>

@implementation NSApplication (MacController)
Expand Down Expand Up @@ -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;
}
Expand All @@ -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();
Expand Down Expand Up @@ -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];
Expand Down
35 changes: 23 additions & 12 deletions src/mac/safari-extension.mm
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
#include "shared.hpp"

#import <AppKit/AppKit.h>
#import <os/log.h>

static os_log_t logger = os_log_create("eu.web-eid.web-eid-safari.web-eid-safari-extension", "extension");

@interface SafariWebExtensionHandler : NSObject <NSExtensionRequestHandling> {
NSMutableDictionary<NSString *, NSExtensionContext *> *contexts;
Expand All @@ -35,16 +38,19 @@ @implementation SafariWebExtensionHandler

- (id)init {
if (self = [super init]) {
NSLog(@"web-eid-safari-extension: starting");
os_log(logger, "starting");
contexts = [[NSMutableDictionary<NSString *, NSExtensionContext *> 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;
}

- (void)dealloc
{
NSLog(@"web-eid-safari-extension: stopping");
os_log(logger, "stopping");
[NSDistributedNotificationCenter.defaultCenter removeObserver:self name:WebEidExtension object:nil];
}

Expand All @@ -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
Expand All @@ -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:@"%@+%@",
Expand All @@ -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];
Expand Down
33 changes: 33 additions & 0 deletions src/mac/web-eid-debug-logging.mobileconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadType</key>
<string>com.apple.system.logging</string>
<key>PayloadIdentifier</key>
<string>eu.web-eid.web-eid-safari.logging</string>
<key>PayloadUUID</key>
<string>4F8A1B2C-3D4E-5F6A-7B8C-9D0E1F2A3B4C</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>Enable-Private-Data</key>
<true/>
</dict>
</array>
<key>PayloadDisplayName</key>
<string>web-eid Debug Logging</string>
<key>PayloadDescription</key>
<string>Enables private data in os_log output system-wide. Install for debugging only; remove when done.</string>
<key>PayloadIdentifier</key>
<string>eu.web-eid.debug-logging-profile</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadUUID</key>
<string>5A9B2C3D-4E5F-6A7B-8C9D-0E1F2A3B4C5D</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</plist>
Loading