How OpenWork's Deep-Link Bridge Handles OAuth and Sign-In Flows

OpenWork's deep-link bridge is a custom event-based system that transfers OAuth callbacks and authentication URLs from the native Electron shell to the web UI using openwork:deep-link-native events and a window.__OPENWORK__ storage mechanism.

The OpenWork deep-link bridge solves a critical cross-platform challenge: how to capture custom protocol URLs (like openwork://) on desktop and forward them to the browser-based application code. This article explores the architecture, source code implementation, and practical usage of this bridge for OAuth and sign-in workflows.

Architecture Overview

The bridge operates across three layers:

  1. Native Electron layer – captures OS-level deep links and emits openwork:deep-link-native events
  2. Bridge layer (deep-link-bridge.ts) – normalizes URLs, stores them on window.__OPENWORK__, and dispatches openwork:deep-link events to the web UI
  3. Application layer – consumes deep links via subscribeDesktopDeepLinks() and drainPendingDeepLinks() to complete authentication flows

The custom protocol scheme openwork is defined in readDesktopDistributionInfo() within apps/app/src/app/lib/desktop.ts (lines 21-33), enabling URLs like openwork://auth?code=abc123 for OAuth callbacks.

The core bridge logic lives in apps/app/src/app/lib/deep-link-bridge.ts. It defines two custom event types and provides functions to push and drain pending URLs.

Event Constants and Types

// apps/app/src/app/lib/deep-link-bridge.ts

export const deepLinkBridgeEvent = "openwork:deep-link";
export const nativeDeepLinkEvent = "openwork:deep-link-native";

export type DeepLinkBridgeDetail = { urls: string[] };

When the native side receives a deep link, this function normalizes the URLs, appends them to window.__OPENWORK__.deepLinks, and fires a custom event:

// apps/app/src/app/lib/deep-link-bridge.ts

export function pushPendingDeepLinks(
  target: Window,
  urls: readonly string[]
): string[] {
  const normalized = urls.flatMap(u => u.trim() ? [u.trim()] : []);
  if (!normalized.length) return [];

  target.__OPENWORK__ ??= {};
  const pending = target.__OPENWORK__.deepLinks ?? [];
  target.__OPENWORK__.deepLinks = [...pending, ...normalized];

  target.dispatchEvent(
    new CustomEvent<DeepLinkBridgeDetail>(deepLinkBridgeEvent, {
      detail: { urls: normalized },
    })
  );

  return normalized;
}

Components call this to fetch any URLs that arrived before their event listener was registered:

// apps/app/src/app/lib/deep-link-bridge.ts

export function drainPendingDeepLinks(target: Window): string[] {
  const pending = target.__OPENWORK__?.deepLinks ?? [];
  if (target.__OPENWORK__) {
    target.__OPENWORK__.deepLinks = [];
  }
  return [...pending];
}

Starting the Bridge: startup-deep-links.ts

The apps/app/src/react-app/shell/startup-deep-links.ts module initializes the bridge once per application lifecycle. It handles two environments: standard web browsers and the Electron desktop runtime.

// apps/app/src/react-app/shell/startup-deep-links.ts

import { pushPendingDeepLinks } from "../../app/lib/deep-link-bridge";
import { subscribeDesktopDeepLinks } from "../../app/lib/desktop";
import { isDesktopRuntime } from "../../app/utils";

let started = false;

export function startDeepLinkBridge(): void {
  if (typeof window === "undefined" || started) return;
  started = true;

  // Standard web: treat current URL as a deep link
  if (!isDesktopRuntime()) {
    pushPendingDeepLinks(window, [window.location.href]);
    return;
  }

  // Desktop: subscribe to native deep-link events
  void (async () => {
    try {
      await subscribeDesktopDeepLinks((urls) => {
        pushPendingDeepLinks(window, urls);
      });
    } catch {
      // Silent failure on startup prevents blocking app launch
    }
  })();
}

Key behaviors:

  • Non-desktop environments immediately push the current window.location.href as a deep link
  • Desktop environments register an async listener via subscribeDesktopDeepLinks() that forwards native events to the bridge

The subscribeDesktopDeepLinks() function in apps/app/src/app/lib/desktop.ts (lines 497-514) bridges the Electron preload script's event system to the web application's event bus. It listens for openwork:deep-link-native events dispatched by the native layer when the OS handles a custom protocol URL.

This function enables the following flow:

  1. User completes OAuth flow in external browser
  2. Redirect to openwork://auth?code=... triggers OS protocol handler
  3. Electron main process captures URL and forwards to preload script
  4. Preload emits openwork:deep-link-native with the URL array
  5. subscribeDesktopDeepLinks() callback invokes pushPendingDeepLinks()
  6. Web UI receives openwork:deep-link event and processes the authentication code

React components or authentication services consume the bridge through event listeners and the drain function:

import { useEffect } from 'react';
import {
  deepLinkBridgeEvent,
  DeepLinkBridgeDetail,
  drainPendingDeepLinks,
} from './app/lib/deep-link-bridge';

function useOAuthDeepLink() {
  useEffect(() => {
    const handler = (e: CustomEvent<DeepLinkBridgeDetail>) => {
      e.detail.urls.forEach(url => {
        // Match OAuth callback: openwork://auth?code=<token>
        const match = url.match(/^openwork:\/\/auth\?code=(.+)$/);
        if (match) {
          const authCode = decodeURIComponent(match[1]);
          completeOAuthSignIn(authCode); // Your auth logic
        }
      });
    };

    // Subscribe to future deep links
    window.addEventListener(
      deepLinkBridgeEvent,
      handler as EventListener
    );

    // Process any links that arrived before mount
    drainPendingDeepLinks(window).forEach(url => {
      handler(new CustomEvent(deepLinkBridgeEvent, {
        detail: { urls: [url] }
      }));
    });

    return () => {
      window.removeEventListener(
        deepLinkBridgeEvent,
        handler as EventListener
      );
    };
  }, []);
}

OAuth and Sign-In Flow Integration

The deep-link bridge enables several authentication patterns:

Flow URL Pattern Handler Action
OAuth authorization code openwork://auth?code=...&state=... Exchange code for tokens, validate state
Magic link sign-in openwork://login?token=... Validate token, establish session
Email verification openwork://verify?email=...&code=... Confirm email address
Password reset openwork://reset?token=... Redirect to reset form with token

All flows share the same underlying mechanism: native capture → bridge normalization → web consumption.

Summary

  • Deep-link bridge (deep-link-bridge.ts) provides pushPendingDeepLinks() and drainPendingDeepLinks() functions with window.__OPENWORK__ storage
  • Two event types coordinate across layers: openwork:deep-link-native (Electron → web) and openwork:deep-link (bridge → application)
  • startDeepLinkBridge() in startup-deep-links.ts initializes the system, adapting to desktop or browser environments
  • subscribeDesktopDeepLinks() in desktop.ts connects the native Electron event system to the bridge
  • The openwork protocol scheme enables custom URL handling for OAuth callbacks and authentication flows

Frequently Asked Questions

Standard web deep links use standard https:// URLs that the browser handles directly. The OpenWork deep-link bridge specifically handles custom protocol URLs (openwork://) that require native OS integration through Electron. The bridge translates these native events into browser-compatible CustomEvent objects that the React application can consume.

The bridge stores all incoming URLs in window.__OPENWORK__.deepLinks. When your component mounts, calling drainPendingDeepLinks(window) retrieves and clears this array. This pattern ensures no authentication callbacks are lost during application startup or navigation delays.

Yes. Any feature that benefits from custom protocol URLs can use the bridge—email verification, password reset, referral codes, or content sharing. The DeepLinkBridgeDetail type accepts any URL array, and application code determines how to parse and route each URL pattern.

The bridge functions in both environments, but behavior differs. In non-desktop builds, startDeepLinkBridge() immediately pushes window.location.href as a deep link. This allows shared routing logic between desktop and web without conditional branching throughout the codebase.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →