From 170786bcb7fff966c209b90eebf4dcc88cd7b998 Mon Sep 17 00:00:00 2001 From: Theodosis Katsoulis Date: Thu, 5 Nov 2026 00:35:40 +0200 Subject: [PATCH 1/5] Point the Safari app at Drive sign-in and the published bundle id. The Mac app presents the existing Google URL and hands the callback back to the extension. Signing and the Drive client secret stay with the maintainers. --- Authenticator.xcodeproj/project.pbxproj | 42 ++++--- Shared (App)/ViewController.swift | 3 +- .../SafariWebExtensionHandler.swift | 105 +++++++++++++++--- authenticator | 2 +- macOS (App)/AppDelegate.swift | 9 +- macOS (App)/Authenticator.entitlements | 4 + macOS (App)/DriveAuth.swift | 78 +++++++++++++ macOS (Extension)/Authenticator.entitlements | 4 + readme.md | 36 +++++- scripts/build-safari.sh | 16 +++ 10 files changed, 259 insertions(+), 40 deletions(-) create mode 100644 macOS (App)/DriveAuth.swift create mode 100755 scripts/build-safari.sh diff --git a/Authenticator.xcodeproj/project.pbxproj b/Authenticator.xcodeproj/project.pbxproj index 32e88d8..512d190 100644 --- a/Authenticator.xcodeproj/project.pbxproj +++ b/Authenticator.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 6FAFAD3E277FCD590041F854 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FAFAD3D277FCD590041F854 /* AppDelegate.swift */; }; + 6FAFAD91277FCD590041F854 /* DriveAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FAFAD90277FCD590041F854 /* DriveAuth.swift */; }; 6FAFAD41277FCD590041F854 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6FAFAD3F277FCD590041F854 /* Main.storyboard */; }; 6FAFAD53277FCD590041F854 /* Authenticator Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 6FAFAD52277FCD590041F854 /* Authenticator Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 6FAFAD5A277FCD590041F854 /* Main.html in Resources */ = {isa = PBXBuildFile; fileRef = 6FAFAD1C277FCD550041F854 /* Main.html */; }; @@ -62,21 +63,22 @@ 6FAFAD24277FCD590041F854 /* SafariWebExtensionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariWebExtensionHandler.swift; sourceTree = ""; }; 6FAFAD3B277FCD590041F854 /* Authenticator.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Authenticator.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6FAFAD3D277FCD590041F854 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 6FAFAD90277FCD590041F854 /* DriveAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DriveAuth.swift; sourceTree = ""; }; 6FAFAD40277FCD590041F854 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 6FAFAD42277FCD590041F854 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 6FAFAD43277FCD590041F854 /* Authenticator.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Authenticator.entitlements; sourceTree = ""; }; 6FAFAD52277FCD590041F854 /* Authenticator Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Authenticator Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 6FAFAD57277FCD590041F854 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 6FAFAD58277FCD590041F854 /* Authenticator.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Authenticator.entitlements; sourceTree = ""; }; - 6FAFAD78277FCD5B0041F854 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; name = LICENSE; path = ./Authenticator/test/chrome/LICENSE; sourceTree = ""; }; - 6FAFAD79277FCD5B0041F854 /* css */ = {isa = PBXFileReference; lastKnownFileType = folder; name = css; path = ./Authenticator/test/chrome/css; sourceTree = ""; }; - 6FAFAD7A277FCD5B0041F854 /* dist */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dist; path = ./Authenticator/test/chrome/dist; sourceTree = ""; }; - 6FAFAD7B277FCD5B0041F854 /* images */ = {isa = PBXFileReference; lastKnownFileType = folder; name = images; path = ./Authenticator/test/chrome/images; sourceTree = ""; }; - 6FAFAD7C277FCD5B0041F854 /* manifest.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = manifest.json; path = ./Authenticator/test/chrome/manifest.json; sourceTree = ""; }; - 6FAFAD7D277FCD5B0041F854 /* view */ = {isa = PBXFileReference; lastKnownFileType = folder; name = view; path = ./Authenticator/test/chrome/view; sourceTree = ""; }; - 6FAFAD7E277FCD5B0041F854 /* schema.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = schema.json; path = ./Authenticator/test/chrome/schema.json; sourceTree = ""; }; - 6FAFAD7F277FCD5B0041F854 /* _locales */ = {isa = PBXFileReference; lastKnownFileType = folder; name = _locales; path = ./Authenticator/test/chrome/_locales; sourceTree = ""; }; - 6FAFAD80277FCD5B0041F854 /* manifest-pwa.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "manifest-pwa.json"; path = "./Authenticator/test/chrome/manifest-pwa.json"; sourceTree = ""; }; + 6FAFAD78277FCD5B0041F854 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; name = LICENSE; path = ../authenticator/safari/LICENSE; sourceTree = ""; }; + 6FAFAD79277FCD5B0041F854 /* css */ = {isa = PBXFileReference; lastKnownFileType = folder; name = css; path = ../authenticator/safari/css; sourceTree = ""; }; + 6FAFAD7A277FCD5B0041F854 /* dist */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dist; path = ../authenticator/safari/dist; sourceTree = ""; }; + 6FAFAD7B277FCD5B0041F854 /* images */ = {isa = PBXFileReference; lastKnownFileType = folder; name = images; path = ../authenticator/safari/images; sourceTree = ""; }; + 6FAFAD7C277FCD5B0041F854 /* manifest.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = manifest.json; path = ../authenticator/safari/manifest.json; sourceTree = ""; }; + 6FAFAD7D277FCD5B0041F854 /* view */ = {isa = PBXFileReference; lastKnownFileType = folder; name = view; path = ../authenticator/safari/view; sourceTree = ""; }; + 6FAFAD7E277FCD5B0041F854 /* schema.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = schema.json; path = ../authenticator/safari/schema.json; sourceTree = ""; }; + 6FAFAD7F277FCD5B0041F854 /* _locales */ = {isa = PBXFileReference; lastKnownFileType = folder; name = _locales; path = ../authenticator/safari/_locales; sourceTree = ""; }; + 6FAFAD80277FCD5B0041F854 /* manifest-pwa.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "manifest-pwa.json"; path = "../authenticator/safari/manifest-pwa.json"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -151,6 +153,7 @@ isa = PBXGroup; children = ( 6FAFAD3D277FCD590041F854 /* AppDelegate.swift */, + 6FAFAD90277FCD590041F854 /* DriveAuth.swift */, 6FAFAD3F277FCD590041F854 /* Main.storyboard */, 6FAFAD42277FCD590041F854 /* Info.plist */, 6FAFAD43277FCD590041F854 /* Authenticator.entitlements */, @@ -299,6 +302,7 @@ files = ( 6FAFAD62277FCD590041F854 /* ViewController.swift in Sources */, 6FAFAD3E277FCD590041F854 /* AppDelegate.swift in Sources */, + 6FAFAD91277FCD590041F854 /* DriveAuth.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -467,13 +471,13 @@ "@executable_path/../Frameworks", "@executable_path/../../../../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 14.4; MARKETING_VERSION = 1.0; OTHER_LDFLAGS = ( "-framework", SafariServices, ); - PRODUCT_BUNDLE_IDENTIFIER = com.yourCompany.Authenticator.Extension; + PRODUCT_BUNDLE_IDENTIFIER = com.rebornix.Authen.Extension; PRODUCT_NAME = "Authenticator Extension"; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -498,13 +502,13 @@ "@executable_path/../Frameworks", "@executable_path/../../../../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 14.4; MARKETING_VERSION = 1.0; OTHER_LDFLAGS = ( "-framework", SafariServices, ); - PRODUCT_BUNDLE_IDENTIFIER = com.yourCompany.Authenticator.Extension; + PRODUCT_BUNDLE_IDENTIFIER = com.rebornix.Authen.Extension; PRODUCT_NAME = "Authenticator Extension"; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -532,15 +536,17 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 14.4; MARKETING_VERSION = 1.0; OTHER_LDFLAGS = ( + "-framework", + AuthenticationServices, "-framework", SafariServices, "-framework", WebKit, ); - PRODUCT_BUNDLE_IDENTIFIER = com.yourCompany.Authenticator; + PRODUCT_BUNDLE_IDENTIFIER = com.rebornix.Authen; PRODUCT_NAME = Authenticator; SDKROOT = macosx; SWIFT_EMIT_LOC_STRINGS = YES; @@ -567,15 +573,17 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 14.4; MARKETING_VERSION = 1.0; OTHER_LDFLAGS = ( + "-framework", + AuthenticationServices, "-framework", SafariServices, "-framework", WebKit, ); - PRODUCT_BUNDLE_IDENTIFIER = com.yourCompany.Authenticator; + PRODUCT_BUNDLE_IDENTIFIER = com.rebornix.Authen; PRODUCT_NAME = Authenticator; SDKROOT = macosx; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/Shared (App)/ViewController.swift b/Shared (App)/ViewController.swift index e7afde4..2e44f4f 100644 --- a/Shared (App)/ViewController.swift +++ b/Shared (App)/ViewController.swift @@ -16,7 +16,8 @@ import SafariServices typealias PlatformViewController = NSViewController #endif -let extensionBundleIdentifier = "com.yourCompany.Authenticator.Extension" +let extensionBundleIdentifier = + (Bundle.main.bundleIdentifier ?? "com.rebornix.Authen") + ".Extension" class ViewController: PlatformViewController, WKNavigationDelegate, WKScriptMessageHandler { diff --git a/Shared (Extension)/SafariWebExtensionHandler.swift b/Shared (Extension)/SafariWebExtensionHandler.swift index 52f10b9..3cdd757 100644 --- a/Shared (Extension)/SafariWebExtensionHandler.swift +++ b/Shared (Extension)/SafariWebExtensionHandler.swift @@ -1,26 +1,103 @@ -// -// SafariWebExtensionHandler.swift -// Shared (Extension) -// -// Created by penlv on 12/31/21. -// - -import SafariServices +import AppKit import os.log +import SafariServices let SFExtensionMessageKey = "message" -class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling { +private let appGroupId = "group.com.rebornix.Authen" +private let driveAuthNotification = Notification.Name("com.rebornix.Authen.driveAuth") +class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling { func beginRequest(with context: NSExtensionContext) { - let item = context.inputItems[0] as! NSExtensionItem - let message = item.userInfo?[SFExtensionMessageKey] - os_log(.default, "Received message from browser.runtime.sendNativeMessage: %@", message as! CVarArg) + DispatchQueue.main.async { + self.handle(context) + } + } - let response = NSExtensionItem() - response.userInfo = [ SFExtensionMessageKey: [ "Response to": message ] ] + private func handle(_ context: NSExtensionContext) { + let item = context.inputItems.first as? NSExtensionItem + let message = item?.userInfo?[SFExtensionMessageKey] as? [String: Any] + os_log(.default, "Received native message") + + guard + message?["action"] as? String == "drive-auth", + let urlString = message?["url"] as? String, + let url = URL(string: urlString), + url.scheme == "https", + url.host == "accounts.google.com" + else { + complete(context, ["ok": false, "error": "unsupported"]) + return + } + + guard let defaults = UserDefaults(suiteName: appGroupId) else { + complete(context, ["ok": false, "error": "failed"]) + return + } + defaults.removeObject(forKey: "driveAuthCallback") + defaults.removeObject(forKey: "driveAuthError") + defaults.set(urlString, forKey: "driveAuthURL") + defaults.set("pending", forKey: "driveAuthState") + defaults.synchronize() + + openContainerApp() + DistributedNotificationCenter.default().post(name: driveAuthNotification, object: nil) + poll(context: context, defaults: defaults, deadline: Date().addingTimeInterval(180)) + } + + private func poll( + context: NSExtensionContext, + defaults: UserDefaults, + deadline: Date + ) { + let state = defaults.string(forKey: "driveAuthState") + if state == "done", let callback = defaults.string(forKey: "driveAuthCallback") { + clear(defaults) + complete(context, ["ok": true, "url": callback]) + return + } + if state == "error" { + let error = defaults.string(forKey: "driveAuthError") ?? "failed" + clear(defaults) + complete(context, ["ok": false, "error": error]) + return + } + if Date() > deadline { + clear(defaults) + complete(context, ["ok": false, "error": "timeout"]) + return + } + DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { + self.poll(context: context, defaults: defaults, deadline: deadline) + } + } + + private func clear(_ defaults: UserDefaults) { + defaults.removeObject(forKey: "driveAuthCallback") + defaults.removeObject(forKey: "driveAuthError") + defaults.removeObject(forKey: "driveAuthURL") + defaults.set("idle", forKey: "driveAuthState") + defaults.synchronize() + } + + private func complete(_ context: NSExtensionContext, _ body: [String: Any]) { + let response = NSExtensionItem() + response.userInfo = [SFExtensionMessageKey: body] context.completeRequest(returningItems: [response], completionHandler: nil) } + private func openContainerApp() { + let appURL = Bundle.main.bundleURL + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + let config = NSWorkspace.OpenConfiguration() + config.activates = true + NSWorkspace.shared.openApplication(at: appURL, configuration: config) { _, error in + if error != nil { + os_log(.error, "Could not open Authen") + } + } + } } diff --git a/authenticator b/authenticator index 72430cf..81f3897 160000 --- a/authenticator +++ b/authenticator @@ -1 +1 @@ -Subproject commit 72430cf1e27906312908d5a14aabad552e5a711c +Subproject commit 81f3897a6cbeccad8d8fb1b7bb9bf182d35d83cb diff --git a/macOS (App)/AppDelegate.swift b/macOS (App)/AppDelegate.swift index 600550f..fc221d4 100644 --- a/macOS (App)/AppDelegate.swift +++ b/macOS (App)/AppDelegate.swift @@ -11,7 +11,14 @@ import Cocoa class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ notification: Notification) { - // Override point for customization after application launch. + DriveAuth.shared.startObserving() + DispatchQueue.main.async { + DriveAuth.shared.startIfPending() + } + } + + func applicationDidBecomeActive(_ notification: Notification) { + DriveAuth.shared.startIfPending() } func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { diff --git a/macOS (App)/Authenticator.entitlements b/macOS (App)/Authenticator.entitlements index 625af03..3ff45ac 100644 --- a/macOS (App)/Authenticator.entitlements +++ b/macOS (App)/Authenticator.entitlements @@ -4,6 +4,10 @@ com.apple.security.app-sandbox + com.apple.security.application-groups + + group.com.rebornix.Authen + com.apple.security.files.user-selected.read-only com.apple.security.network.client diff --git a/macOS (App)/DriveAuth.swift b/macOS (App)/DriveAuth.swift new file mode 100644 index 0000000..234683b --- /dev/null +++ b/macOS (App)/DriveAuth.swift @@ -0,0 +1,78 @@ +import AuthenticationServices +import Cocoa + +private let appGroupId = "group.com.rebornix.Authen" +private let driveAuthNotification = Notification.Name("com.rebornix.Authen.driveAuth") +private let driveRedirect = URL(string: "https://authenticator.cc/oauth")! + +final class DriveAuth: NSObject, ASWebAuthenticationPresentationContextProviding { + static let shared = DriveAuth() + + private var session: ASWebAuthenticationSession? + + func startObserving() { + DistributedNotificationCenter.default().addObserver( + forName: driveAuthNotification, + object: nil, + queue: .main + ) { [weak self] _ in + self?.startIfPending() + } + } + + func startIfPending() { + guard session == nil else { + return + } + guard let defaults = UserDefaults(suiteName: appGroupId) else { + return + } + guard defaults.string(forKey: "driveAuthState") == "pending", + let urlString = defaults.string(forKey: "driveAuthURL"), + let url = URL(string: urlString), + url.scheme == "https", + url.host == "accounts.google.com" + else { + return + } + + let session = ASWebAuthenticationSession( + url: url, + callback: .https(host: driveRedirect.host ?? "authenticator.cc", path: driveRedirect.path), + completionHandler: { [weak self] callbackURL, error in + self?.finish(callbackURL: callbackURL, error: error) + } + ) + session.presentationContextProvider = self + session.prefersEphemeralWebBrowserSession = false + self.session = session + if !session.start() { + finish(callbackURL: nil, error: nil) + } + } + + func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { + return NSApplication.shared.keyWindow + ?? NSApplication.shared.windows.first + ?? ASPresentationAnchor() + } + + private func finish(callbackURL: URL?, error: Error?) { + let defaults = UserDefaults(suiteName: appGroupId) + if let callbackURL = callbackURL, + callbackURL.scheme == "https", + callbackURL.host == driveRedirect.host, + callbackURL.path == driveRedirect.path + { + defaults?.set(callbackURL.absoluteString, forKey: "driveAuthCallback") + defaults?.set("done", forKey: "driveAuthState") + } else { + let cancelled = (error as NSError?)?.code == ASWebAuthenticationSessionError.canceledLogin.rawValue + defaults?.set(cancelled ? "cancelled" : "failed", forKey: "driveAuthError") + defaults?.set("error", forKey: "driveAuthState") + } + defaults?.removeObject(forKey: "driveAuthURL") + defaults?.synchronize() + session = nil + } +} diff --git a/macOS (Extension)/Authenticator.entitlements b/macOS (Extension)/Authenticator.entitlements index f2ef3ae..005ef8c 100644 --- a/macOS (Extension)/Authenticator.entitlements +++ b/macOS (Extension)/Authenticator.entitlements @@ -4,6 +4,10 @@ com.apple.security.app-sandbox + com.apple.security.application-groups + + group.com.rebornix.Authen + com.apple.security.files.user-selected.read-only diff --git a/readme.md b/readme.md index ddb3df7..3b5ca66 100644 --- a/readme.md +++ b/readme.md @@ -1,14 +1,38 @@ # Authen -Safari extension distro for [Authenticator](https://github.com/Authenticator-Extension/Authenticator) +Safari extension distro for [Authenticator](https://github.com/Authenticator-Extension/Authenticator) Download on the App Store ![2560x1600bb](https://user-images.githubusercontent.com/876920/176361939-a359dd46-254a-4784-86bf-19f9c999f5aa.png) -## How to contribute +## Build -* Clone the project -* `git submodule update --remote` to update the sub module for [Authenticator](https://github.com/Authenticator-Extension/Authenticator) -* `npm ci && npm run chrome` -* Launch the project and run \ No newline at end of file +The extension lives in the `authenticator` submodule. + +``` +git submodule update --init +./scripts/build-safari.sh +``` + +Open `Authenticator.xcodeproj` and run the macOS app. The script installs the submodule dependencies with Puppeteer's Chromium download skipped, and it sets `NODE_OPTIONS=--openssl-legacy-provider` so webpack 4 runs on current Node. Xcode packages `authenticator/safari`. + +The project uses the published bundle ids `com.rebornix.Authen` and `com.rebornix.Authen.Extension`. `DEVELOPMENT_TEAM` is empty. Signing and the App Store upload stay with the maintainers. The deployment target is macOS 14.4, which is what `ASWebAuthenticationSession` needs for an https callback. + +Google Drive sign-in uses the client id already in the Chrome manifest. The client secret in `authenticator/src/models/credentials.ts` stays empty in git. The release script fills it before a production build. The redirect is `https://authenticator.cc/oauth`. + +## Drive backups + +A backup is a dated JSON file in a Drive folder named Authenticator Backups. The scope is `drive.file`, so the app only sees files it created. There is no download or merge. + +With a passphrase, the file is encrypted. That encryption is only as strong as the passphrase. With encryption turned off, or with no passphrase, the file is the raw secrets. The Drive page already warns about that. Dropbox and OneDrive stay in the code and stay hidden on Safari. + +## Dependencies + +`crypto-js` is 4.2.0. That clears the production PBKDF2 advisory `npm audit` reported for 4.0.0. + +These stay unfixed because the fix is a new major version, and this tree stays on Vue 2 and webpack 4: + +- `uuid` 3.x. The patched release is 11. +- Vue 2. The ReDoS advisory is fixed in Vue 3, and Vue 2 is end of life. +- High and critical findings in the dev tree (webpack 4, eslint 6, and their dependencies). `npm audit fix --force` would leave Vue 2 and webpack 4. diff --git a/scripts/build-safari.sh b/scripts/build-safari.sh new file mode 100755 index 0000000..b627220 --- /dev/null +++ b/scripts/build-safari.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT/authenticator" + +case ":${NODE_OPTIONS:-}:" in + *--openssl-legacy-provider*) ;; + *) export NODE_OPTIONS="${NODE_OPTIONS:-} --openssl-legacy-provider" ;; +esac + +if [[ ! -d node_modules ]]; then + PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true npm ci +fi + +npm run safari From cb8ad8fb80de9f6ae7e92fc4a8d13e39361641a6 Mon Sep 17 00:00:00 2001 From: Theodosis Katsoulis Date: Thu, 5 Nov 2026 00:41:39 +0200 Subject: [PATCH 2/5] Point the submodule at the commit that leaves Drive credentials empty. --- authenticator | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authenticator b/authenticator index 81f3897..6e9eb37 160000 --- a/authenticator +++ b/authenticator @@ -1 +1 @@ -Subproject commit 81f3897a6cbeccad8d8fb1b7bb9bf182d35d83cb +Subproject commit 6e9eb37da2dd857b7855a61b043dea391da514d8 From c1dcce613d5a4ae6804ff2d1d236fde878f11c64 Mon Sep 17 00:00:00 2001 From: Theodosis Katsoulis Date: Thu, 5 Nov 2026 00:43:12 +0200 Subject: [PATCH 3/5] Keep the Xcode bundle ids at com.yourCompany.Authenticator. The Drive app group uses that same id. The published App Store id stays unset. --- Authenticator.xcodeproj/project.pbxproj | 8 ++++---- Shared (App)/ViewController.swift | 3 +-- Shared (Extension)/SafariWebExtensionHandler.swift | 4 ++-- authenticator | 2 +- macOS (App)/Authenticator.entitlements | 2 +- macOS (App)/DriveAuth.swift | 4 ++-- macOS (Extension)/Authenticator.entitlements | 2 +- readme.md | 2 +- 8 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Authenticator.xcodeproj/project.pbxproj b/Authenticator.xcodeproj/project.pbxproj index 512d190..8d6a602 100644 --- a/Authenticator.xcodeproj/project.pbxproj +++ b/Authenticator.xcodeproj/project.pbxproj @@ -477,7 +477,7 @@ "-framework", SafariServices, ); - PRODUCT_BUNDLE_IDENTIFIER = com.rebornix.Authen.Extension; + PRODUCT_BUNDLE_IDENTIFIER = com.yourCompany.Authenticator.Extension; PRODUCT_NAME = "Authenticator Extension"; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -508,7 +508,7 @@ "-framework", SafariServices, ); - PRODUCT_BUNDLE_IDENTIFIER = com.rebornix.Authen.Extension; + PRODUCT_BUNDLE_IDENTIFIER = com.yourCompany.Authenticator.Extension; PRODUCT_NAME = "Authenticator Extension"; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -546,7 +546,7 @@ "-framework", WebKit, ); - PRODUCT_BUNDLE_IDENTIFIER = com.rebornix.Authen; + PRODUCT_BUNDLE_IDENTIFIER = com.yourCompany.Authenticator; PRODUCT_NAME = Authenticator; SDKROOT = macosx; SWIFT_EMIT_LOC_STRINGS = YES; @@ -583,7 +583,7 @@ "-framework", WebKit, ); - PRODUCT_BUNDLE_IDENTIFIER = com.rebornix.Authen; + PRODUCT_BUNDLE_IDENTIFIER = com.yourCompany.Authenticator; PRODUCT_NAME = Authenticator; SDKROOT = macosx; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/Shared (App)/ViewController.swift b/Shared (App)/ViewController.swift index 2e44f4f..e7afde4 100644 --- a/Shared (App)/ViewController.swift +++ b/Shared (App)/ViewController.swift @@ -16,8 +16,7 @@ import SafariServices typealias PlatformViewController = NSViewController #endif -let extensionBundleIdentifier = - (Bundle.main.bundleIdentifier ?? "com.rebornix.Authen") + ".Extension" +let extensionBundleIdentifier = "com.yourCompany.Authenticator.Extension" class ViewController: PlatformViewController, WKNavigationDelegate, WKScriptMessageHandler { diff --git a/Shared (Extension)/SafariWebExtensionHandler.swift b/Shared (Extension)/SafariWebExtensionHandler.swift index 3cdd757..25c0af7 100644 --- a/Shared (Extension)/SafariWebExtensionHandler.swift +++ b/Shared (Extension)/SafariWebExtensionHandler.swift @@ -4,8 +4,8 @@ import SafariServices let SFExtensionMessageKey = "message" -private let appGroupId = "group.com.rebornix.Authen" -private let driveAuthNotification = Notification.Name("com.rebornix.Authen.driveAuth") +private let appGroupId = "group.com.yourCompany.Authenticator" +private let driveAuthNotification = Notification.Name("com.yourCompany.Authenticator.driveAuth") class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling { func beginRequest(with context: NSExtensionContext) { diff --git a/authenticator b/authenticator index 6e9eb37..2217816 160000 --- a/authenticator +++ b/authenticator @@ -1 +1 @@ -Subproject commit 6e9eb37da2dd857b7855a61b043dea391da514d8 +Subproject commit 221781693cc87a34ddc63cd41b7fb8d67b4dcd6f diff --git a/macOS (App)/Authenticator.entitlements b/macOS (App)/Authenticator.entitlements index 3ff45ac..0313a8a 100644 --- a/macOS (App)/Authenticator.entitlements +++ b/macOS (App)/Authenticator.entitlements @@ -6,7 +6,7 @@ com.apple.security.application-groups - group.com.rebornix.Authen + group.com.yourCompany.Authenticator com.apple.security.files.user-selected.read-only diff --git a/macOS (App)/DriveAuth.swift b/macOS (App)/DriveAuth.swift index 234683b..2c02324 100644 --- a/macOS (App)/DriveAuth.swift +++ b/macOS (App)/DriveAuth.swift @@ -1,8 +1,8 @@ import AuthenticationServices import Cocoa -private let appGroupId = "group.com.rebornix.Authen" -private let driveAuthNotification = Notification.Name("com.rebornix.Authen.driveAuth") +private let appGroupId = "group.com.yourCompany.Authenticator" +private let driveAuthNotification = Notification.Name("com.yourCompany.Authenticator.driveAuth") private let driveRedirect = URL(string: "https://authenticator.cc/oauth")! final class DriveAuth: NSObject, ASWebAuthenticationPresentationContextProviding { diff --git a/macOS (Extension)/Authenticator.entitlements b/macOS (Extension)/Authenticator.entitlements index 005ef8c..ad87ae0 100644 --- a/macOS (Extension)/Authenticator.entitlements +++ b/macOS (Extension)/Authenticator.entitlements @@ -6,7 +6,7 @@ com.apple.security.application-groups - group.com.rebornix.Authen + group.com.yourCompany.Authenticator com.apple.security.files.user-selected.read-only diff --git a/readme.md b/readme.md index 3b5ca66..c57dd05 100644 --- a/readme.md +++ b/readme.md @@ -17,7 +17,7 @@ git submodule update --init Open `Authenticator.xcodeproj` and run the macOS app. The script installs the submodule dependencies with Puppeteer's Chromium download skipped, and it sets `NODE_OPTIONS=--openssl-legacy-provider` so webpack 4 runs on current Node. Xcode packages `authenticator/safari`. -The project uses the published bundle ids `com.rebornix.Authen` and `com.rebornix.Authen.Extension`. `DEVELOPMENT_TEAM` is empty. Signing and the App Store upload stay with the maintainers. The deployment target is macOS 14.4, which is what `ASWebAuthenticationSession` needs for an https callback. +The bundle ids stay `com.yourCompany.Authenticator` and `com.yourCompany.Authenticator.Extension`. `DEVELOPMENT_TEAM` is empty. Signing and the App Store upload stay with the maintainers. The deployment target is macOS 14.4, which is what `ASWebAuthenticationSession` needs for an https callback. Google Drive sign-in uses the client id already in the Chrome manifest. The client secret in `authenticator/src/models/credentials.ts` stays empty in git. The release script fills it before a production build. The redirect is `https://authenticator.cc/oauth`. From bc2483344ec5421f2336e8a71c1b5b3f27898ae1 Mon Sep 17 00:00:00 2001 From: Theodosis Katsoulis Date: Thu, 5 Nov 2026 00:43:52 +0200 Subject: [PATCH 4/5] Leave the app and extension entitlements unchanged. The application group was not in the project, so Drive sign-in does not add one. --- macOS (App)/Authenticator.entitlements | 4 ---- macOS (Extension)/Authenticator.entitlements | 4 ---- 2 files changed, 8 deletions(-) diff --git a/macOS (App)/Authenticator.entitlements b/macOS (App)/Authenticator.entitlements index 0313a8a..625af03 100644 --- a/macOS (App)/Authenticator.entitlements +++ b/macOS (App)/Authenticator.entitlements @@ -4,10 +4,6 @@ com.apple.security.app-sandbox - com.apple.security.application-groups - - group.com.yourCompany.Authenticator - com.apple.security.files.user-selected.read-only com.apple.security.network.client diff --git a/macOS (Extension)/Authenticator.entitlements b/macOS (Extension)/Authenticator.entitlements index ad87ae0..f2ef3ae 100644 --- a/macOS (Extension)/Authenticator.entitlements +++ b/macOS (Extension)/Authenticator.entitlements @@ -4,10 +4,6 @@ com.apple.security.app-sandbox - com.apple.security.application-groups - - group.com.yourCompany.Authenticator - com.apple.security.files.user-selected.read-only From f0c61f57332e2e760c2b7c23ac49227172b60f61 Mon Sep 17 00:00:00 2001 From: Theodosis Katsoulis Date: Thu, 5 Nov 2026 00:50:46 +0200 Subject: [PATCH 5/5] Point the submodule at the Safari Drive commit rebased onto current dev. --- authenticator | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authenticator b/authenticator index 2217816..dd6a4c9 160000 --- a/authenticator +++ b/authenticator @@ -1 +1 @@ -Subproject commit 221781693cc87a34ddc63cd41b7fb8d67b4dcd6f +Subproject commit dd6a4c9e696b9b3a4988e44d94e6a416034d75a1