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
1 change: 1 addition & 0 deletions .github/workflows/cmake-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,4 @@ jobs:
path: |
build/src/app/*.pkg
build/src/app/*.dmg
build/src/mac/*.pkg
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
5 changes: 4 additions & 1 deletion src/app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ elseif(APPLE)
set(WEBEID_PATH /Applications/Utilities/web-eid.app/Contents/MacOS/web-eid)
set_source_files_properties(${CMAKE_SOURCE_DIR}/install/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
set_source_files_properties(InfoPlist.strings PROPERTIES MACOSX_PACKAGE_LOCATION Resources/en.lproj)
set_target_properties(web-eid PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/MacOSXBundleInfo.plist.in)
set_target_properties(web-eid PROPERTIES
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/MacOSXBundleInfo.plist.in
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "eu.web-eid.web-eid"
)
configure_file(${CMAKE_SOURCE_DIR}/install/macos-postinstall.in root_firefox_scripts/postinstall @ONLY)
install(TARGETS web-eid DESTINATION /Applications/Utilities COMPONENT main)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/eu.webeid.json COMPONENT chrome
Expand Down
1 change: 0 additions & 1 deletion src/controller/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ add_library(controller STATIC
if(APPLE)
target_sources(controller PRIVATE application.mm)
set_source_files_properties(application.mm PROPERTIES COMPILE_FLAGS "-fobjc-arc")
target_link_libraries(controller "-framework Cocoa" "-framework AppKit" "-fobjc-arc")
endif()

set_property(SOURCE application.cpp APPEND PROPERTY COMPILE_DEFINITIONS PROJECT_VERSION="${SEMVER_VERSION_STRING}")
Expand Down
1 change: 0 additions & 1 deletion src/controller/application.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include <QUrl>

#import <AppKit/AppKit.h>
#import <Cocoa/Cocoa.h>

void Application::showAbout()
{
Expand Down
26 changes: 13 additions & 13 deletions src/mac/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
# Helper
add_library(SafariServices INTERFACE shared.hpp)
target_compile_options(SafariServices INTERFACE -fobjc-arc)
target_link_options(SafariServices INTERFACE -fobjc-arc -fobjc-link-runtime)
target_link_libraries(SafariServices INTERFACE "-framework SafariServices")
target_link_libraries(SafariServices INTERFACE $<LINK_LIBRARY:FRAMEWORK,SafariServices.framework>)

# Extension JS
find_program(NPM_EXECUTABLE NAMES npm REQUIRED)
Expand All @@ -18,6 +17,7 @@ add_custom_command(OUTPUT ${EXTENSION}
COMMENT "Generating extension js"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/js
)
set_source_files_properties(${EXTENSION} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)

# Extension
add_executable(web-eid-safari-extension MACOSX_BUNDLE safari-extension.mm web-eid-safari-extension.entitlements ${EXTENSION})
Expand All @@ -29,19 +29,15 @@ set_target_properties(web-eid-safari-extension PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_LIST_DIR}/web-eid-safari-extension.entitlements"
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "eu.web-eid.web-eid-safari.web-eid-safari-extension"
)
target_link_libraries(web-eid-safari-extension SafariServices "-framework AppKit")
target_link_libraries(web-eid-safari-extension SafariServices $<LINK_LIBRARY:FRAMEWORK,AppKit>)
target_link_options(web-eid-safari-extension PRIVATE -e _NSExtensionMain -fapplication-extension)
add_custom_command(TARGET web-eid-safari-extension POST_BUILD
COMMAND mkdir -p $<TARGET_BUNDLE_CONTENT_DIR:web-eid-safari-extension>/Resources
COMMAND cp ${EXTENSION} $<TARGET_BUNDLE_CONTENT_DIR:web-eid-safari-extension>/Resources
)

# Application
add_executable(web-eid-safari MACOSX_BUNDLE
${CMAKE_SOURCE_DIR}/install/appicon.icns
InfoPlist.strings
main.mm
../app/getCommandHandler.cpp
../app/getcommandhandler.cpp
web-eid-safari.entitlements
)
set_source_files_properties(${CMAKE_SOURCE_DIR}/install/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
Expand All @@ -51,12 +47,16 @@ set_target_properties(web-eid-safari PROPERTIES
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info-app.plist.cmake
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_LIST_DIR}/web-eid-safari.entitlements"
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "eu.web-eid.web-eid-safari"
XCODE_EMBED_APP_EXTENSIONS web-eid-safari-extension
)
target_link_libraries(web-eid-safari SafariServices controller ui pcsc "-framework Cocoa")
add_custom_command(TARGET web-eid-safari POST_BUILD
COMMAND mkdir -p $<TARGET_BUNDLE_CONTENT_DIR:web-eid-safari>/PlugIns
COMMAND cp -a $<TARGET_BUNDLE_DIR:web-eid-safari-extension> $<TARGET_BUNDLE_CONTENT_DIR:web-eid-safari>/PlugIns
)
target_link_libraries(web-eid-safari SafariServices controller ui pcsc)
add_dependencies(web-eid-safari web-eid-safari-extension)
if(NOT XCODE)
add_custom_command(TARGET web-eid-safari POST_BUILD
COMMAND mkdir -p $<TARGET_BUNDLE_CONTENT_DIR:web-eid-safari>/PlugIns
COMMAND cp -a $<TARGET_BUNDLE_DIR:web-eid-safari-extension> $<TARGET_BUNDLE_CONTENT_DIR:web-eid-safari>/PlugIns
)
endif()

add_custom_target(macdeployqt-safari DEPENDS web-eid-safari
COMMAND Qt6::macdeployqt $<TARGET_BUNDLE_DIR:web-eid-safari> -appstore-compliant
Expand Down
12 changes: 9 additions & 3 deletions src/mac/main.mm
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@
#include <QJsonObject>
#include <QTimer>

#import <Cocoa/Cocoa.h>
#import <AppKit/AppKit.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 @@ -95,7 +98,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 @@ -122,7 +125,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 @@ -157,6 +160,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 @@ -6,6 +6,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 @@ -16,16 +19,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 @@ -34,14 +40,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 @@ -56,31 +67,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 @@ -100,7 +111,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