Skip to content

1Code is Blocking Other Electron Apps' OAuth/PKCE Login Flows #158

@garikaijenje

Description

@garikaijenje

Problem

1Code is currently preventing other Electron applications from handling their own OAuth/PKCE authentication flows. This happens even when 1Code is not even running.

What's Happening

  • When users click OAuth redirect links for OTHER Electron apps in their browser, the browser shows "Open Electron" and 1Code opens instead of the intended app
  • OAuth authentication fails with "Invalid or expired auth code" errors because the auth code was meant for a different app
  • This blocks other developers' Electron apps from working correctly
  • The issue persists even after closing 1Code or removing node_modules

Why This Is Happening

1Code is registering custom protocols (twentyfirst-agents://) at the macOS system level in two problematic ways:

  1. electron-builder Protocol Registration: The protocols section in package.json creates permanent OS-level associations in macOS LaunchServices that persist even after 1Code is closed or uninstalled.

  2. Non-Unique userData Path: The app.requestSingleInstanceLock() uses a generic userData path that creates lock files interfering with other Electron applications.

Required Fixes

The following code changes must be implemented to stop 1Code from blocking other apps:

1. Remove OS-Level Protocol Registration

Action Required: Delete the protocols section from package.json to stop 1Code from registering at the OS level:

  "build": {
    "appId": "dev.21st.agents",
    "productName": "1Code",
    "npmRebuild": true,
-    "protocols": [
-      {
-        "name": "1Code",
-        "schemes": [
-          "twentyfirst-agents"
-        ]
-      }
-    ],
    "directories": {
      "buildResources": "build",
      "output": "release"
    }
  }

2. Stop Registering Protocols in Development

Action Required: Modify src/main/index.ts to skip protocol registration during development:

  function registerProtocol(): boolean {
+   // Skip protocol registration in development to prevent blocking other apps
+   if (IS_DEV) {
+     console.log("[Protocol] Skipping registration in dev mode to avoid blocking other apps")
+     return false
+   }
+
    let success = false
    
    if (process.defaultApp) {
      // Dev mode: need to pass execPath and script path
      if (process.argv.length >= 2) {
        success = app.setAsDefaultProtocolClient(PROTOCOL, process.execPath, [
          process.argv[1]!,
        ])
      }
    } else {
      // Production mode
      success = app.setAsDefaultProtocolClient(PROTOCOL)
    }
    
    return success
  }

3. Clean Up Protocol Registration on Quit

Action Required: Add cleanup code in src/main/index.ts to unregister the protocol handler when 1Code closes:

/**
 * Unregister the protocol handler when app quits
 * This prevents blocking other Electron apps
 */
function unregisterProtocol(): void {
  if (IS_DEV) {
    return // Skip in dev mode since we didn't register
  }

  try {
    app.removeAsDefaultProtocolClient(PROTOCOL)
    console.log("[Protocol] Unregistered protocol handler")
  } catch (error) {
    console.warn("[Protocol] Failed to unregister:", error)
  }
}

// Register the cleanup handler
app.on("will-quit", () => {
  unregisterProtocol()
})

4. Fix userData Path to Be App-Specific

Action Required: Change the userData path in src/main/index.ts to be unique to 1Code:

  if (IS_DEV) {
    const { join } = require("path")
-   const devUserData = join(app.getPath("userData"), "..", "Agents Dev")
+   // Use appId-specific path to avoid conflicts with other Electron apps
+   const devUserData = join(app.getPath("userData"), "..", "21st-dev-agents-dev")
    app.setPath("userData", devUserData)
    console.log("[Dev] Using separate userData path:", devUserData)
  }

Cleanup Steps for Users Affected by This Bug

If 1Code has already blocked other Electron apps, users need to manually clean up the OS-level registrations. Here's how:

Step 1: Backup Your LaunchServices Preferences

cp ~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist \
   ~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist.backup

Step 2: Remove 1Code's Protocol Entries from LaunchServices

# Convert plist to XML for editing
plutil -convert xml1 ~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist \
       -o /tmp/launchservices_edit.plist

# Remove the protocol entries using Python
python3 << 'EOF'
import plistlib

# Read the plist
with open('/tmp/launchservices_edit.plist', 'rb') as f:
    plist = plistlib.load(f)

# Remove 1Code's protocol entries
if 'LSHandlers' in plist:
    original_count = len(plist['LSHandlers'])
    plist['LSHandlers'] = [
        handler for handler in plist['LSHandlers']
        if handler.get('LSHandlerURLScheme') not in ['twentyfirst-agents', 'twentyfirst-agents-dev']
    ]
    removed_count = original_count - len(plist['LSHandlers'])
    print(f"Removed {removed_count} entries")
    
    # Write back
    with open('/tmp/launchservices_cleaned.plist', 'wb') as f:
        plistlib.dump(plist, f)
    print("Cleaned plist saved")
else:
    print("No LSHandlers found")
EOF

# Convert back to binary and replace original
plutil -convert binary1 /tmp/launchservices_cleaned.plist \
       -o ~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist

Step 3: Clean Up 1Code's Caches and Data

# Remove 1Code's caches
rm -rf ~/Library/Caches/dev.21st.agents
rm -rf ~/Library/Caches/dev.21st.agents.ShipIt

# Remove 1Code's userData directory
rm -rf ~/Library/Application\ Support/21st-dev-agents-dev

Step 4: Unregister 1Code's Development Electron.app

# Unregister 1Code's Electron.app from node_modules
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \
  -u "/path/to/1code/node_modules/electron/dist/Electron.app"

Step 5: Restart Dock

killall Dock

Step 6: Verify 1Code's Protocols Are Removed

# Check if 1Code's protocols are still registered
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \
  -dump 2>&1 | grep -i "twentyfirst-agents"

# Should return no results or minimal cached entries

Troubleshooting

Other Apps Are Still Being Blocked

If 1Code is still blocking other Electron apps after the cleanup:

  1. Restart your Mac - LaunchServices caches can be stubborn and may require a full system restart to clear completely.

  2. Check for mounted 1Code DMG volumes - If a 1Code installer DMG is mounted, it's still registered:

    # Find mounted 1Code volumes
    /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \
      -dump 2>&1 | grep -B20 "twentyfirst-agents" | grep "path:"
    
    # Unmount any 1Code DMG volumes
    hdiutil detach "/Volumes/1Code*"
  3. Remove installed 1Code from /Applications:

    rm -rf /Applications/1Code.app
  4. Rebuild LaunchServices database (use with caution):

    # This will rebuild the entire LaunchServices database
    /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \
      -kill -r -domain local -domain user

    Note: The -kill option has been removed in recent macOS versions, so this may not work on newer systems.

Verify Other Apps Work Again

  1. Click an OAuth link for another Electron app in your browser
  2. Verify the correct app opens (not 1Code)
  3. Confirm the OAuth flow completes successfully

Best Practices to Prevent This

  1. Never register protocols in development mode - Only register in production builds to avoid blocking other developers' apps
  2. Use app-specific userData paths - Include your app ID to prevent single instance lock conflicts
  3. Always clean up on quit - Unregister protocol handlers when the app closes
  4. Avoid electron-builder protocol registration - Handle protocols at runtime, not build time
  5. Prefer localhost callbacks - For OAuth flows, use http://localhost redirect URIs instead of custom schemes when possible

Related Issues

Environment

  • OS: macOS (issue also affects Windows and Linux)
  • Electron Version: Any version using app.setAsDefaultProtocolClient()
  • Build Tool: electron-builder

Impact

This bug completely breaks OAuth flows for other Electron applications. Developers running multiple Electron apps cannot authenticate because 1Code intercepts their auth callbacks. The OS-level protocol registration persists across app restarts and even after uninstalling 1Code, making it extremely difficult to debug.

Fixing this requires:

  1. Code changes in 1Code (to stop future registrations)
  2. Manual cleanup by affected users (to remove existing registrations)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdesktopMac/Windows/Linux app

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions