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

> Learn how OpenWorks deep-link bridge manages OAuth and sign-in flows for Electron apps. Discover its custom event system for seamless native to web UI data transfer.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: deep-dive
- Published: 2026-08-16

---

**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`](https://github.com/different-ai/openwork/blob/main/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`](https://github.com/different-ai/openwork/blob/main/apps/app/src/app/lib/desktop.ts) (lines 21-33), enabling URLs like `openwork://auth?code=abc123` for OAuth callbacks.

## The Bridge Implementation: [`deep-link-bridge.ts`](https://github.com/different-ai/openwork/blob/main/deep-link-bridge.ts)

The core bridge logic lives in **[`apps/app/src/app/lib/deep-link-bridge.ts`](https://github.com/different-ai/openwork/blob/main/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

```typescript
// 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[] };

```

### `pushPendingDeepLinks()` – Store and Broadcast URLs

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

```typescript
// 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;
}

```

### `drainPendingDeepLinks()` – Retrieve and Clear

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

```typescript
// 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`](https://github.com/different-ai/openwork/blob/main/startup-deep-links.ts)

The **[`apps/app/src/react-app/shell/startup-deep-links.ts`](https://github.com/different-ai/openwork/blob/main/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.

```typescript
// 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

## Desktop Integration: `subscribeDesktopDeepLinks()`

The `subscribeDesktopDeepLinks()` function in **[`apps/app/src/app/lib/desktop.ts`](https://github.com/different-ai/openwork/blob/main/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

## Consuming Deep Links in Application Code

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

```typescript
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`](https://github.com/different-ai/openwork/blob/main/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`](https://github.com/different-ai/openwork/blob/main/startup-deep-links.ts) initializes the system, adapting to desktop or browser environments
- **`subscribeDesktopDeepLinks()`** in [`desktop.ts`](https://github.com/different-ai/openwork/blob/main/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

### How does the OpenWork deep-link bridge differ from standard browser deep links?

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.

### What happens if a deep link arrives before the web UI is ready?

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.

### Can the deep-link bridge be used outside of OAuth flows?

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.

### Is the deep-link bridge available in the web-only version of OpenWork?

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.