Skip to content

Commit 5f94ac0

Browse files
fix(docker): copy profile to temp dir for cross-platform cookie bridge (stickerdaniel#150)
When native auth fails and a portable cookie file exists, copy the browser profile to a temp directory before restarting with imported cookies. This prevents Docker's Linux Chromium from corrupting the macOS profile by writing Linux-encrypted data back to the mounted dir. Also removes the encrypted Cookies DB from the copy since it can't be decrypted cross-platform, and bumps linkedin-scraper-patchright to 3.1.4 which fixes cookie domain normalization and auth-only import.
2 parents 863af99 + 728fda6 commit 5f94ac0

3 files changed

Lines changed: 45 additions & 13 deletions

File tree

linkedin_mcp_server/drivers/browser.py

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
"""
88

99
import logging
10+
import shutil
11+
import tempfile
1012
from pathlib import Path
1113

1214
from linkedin_scraper import (
@@ -99,15 +101,45 @@ async def get_or_create_browser(
99101
_browser = browser # Assign only after auth succeeds
100102
return _browser
101103

102-
# Auth failed — try importing portable cookies (cross-platform support)
103-
logger.info("Native auth failed, attempting portable cookie import...")
104-
if await browser.import_cookies():
104+
# Native auth failed — try the cross-platform cookie bridge.
105+
# On macOS→Linux, Chromium can't decrypt macOS-encrypted cookies in the
106+
# persistent profile. We copy the profile to a temp dir (so the original
107+
# isn't corrupted by Linux Chromium writing back), remove the undecryptable
108+
# Cookies DB, and inject auth cookies from the portable JSON file.
109+
cookie_path = user_data_dir.parent / "cookies.json"
110+
if cookie_path.exists():
111+
logger.info("Native auth failed, attempting cross-platform cookie bridge...")
112+
await browser.close()
113+
114+
# Copy profile to temp dir — protects the macOS original
115+
temp_dir = Path(tempfile.mkdtemp(prefix="linkedin-mcp-"))
116+
temp_profile = temp_dir / "profile"
117+
shutil.copytree(user_data_dir, temp_profile)
118+
119+
# Remove encrypted Cookies DB (can't be decrypted cross-platform)
120+
(temp_profile / "Default" / "Cookies").unlink(missing_ok=True)
121+
(temp_profile / "Default" / "Cookies-journal").unlink(missing_ok=True)
122+
123+
browser = BrowserManager(
124+
user_data_dir=temp_profile,
125+
headless=_headless,
126+
slow_mo=config.browser.slow_mo,
127+
user_agent=config.browser.user_agent,
128+
viewport=viewport,
129+
**launch_options,
130+
)
131+
await browser.start()
132+
133+
# First nav establishes session cookies (bcookie, JSESSIONID, etc.)
105134
await browser.page.goto("https://www.linkedin.com/feed/")
106-
if await is_logged_in(browser.page):
107-
logger.info("Authentication recovered via portable cookies")
108-
_apply_browser_settings(browser)
109-
_browser = browser
110-
return _browser
135+
# Import auth cookies (li_at, li_rm) from the portable file
136+
if await browser.import_cookies(cookie_path):
137+
await browser.page.goto("https://www.linkedin.com/feed/")
138+
if await is_logged_in(browser.page):
139+
logger.info("Authentication recovered via portable cookies")
140+
_apply_browser_settings(browser)
141+
_browser = browser
142+
return _browser
111143

112144
# Auth failed — clean up and fail fast
113145
await browser.close()

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ classifiers = [
3434
dependencies = [
3535
"fastmcp>=2.14.0",
3636
"inquirer>=3.4.0",
37-
"linkedin-scraper-patchright>=3.1.3",
37+
"linkedin-scraper-patchright>=3.1.4",
3838
"patchright>=1.40.0",
3939
"pyperclip>=1.9.0",
4040
"python-dotenv>=1.1.1",

uv.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)