Add Chrome extension cookie bridge for session import
Users logged into Alta in Chrome can now send their session cookie to the running Electron app via a local HTTP server on port 18247, eliminating the need for re-authentication. - main.js: HTTP cookie server with CORS, token, domain validation - preload.js: onExtensionCookie push-pattern IPC bridge - renderer.js: handleExtensionCookie sets session, fetches devices - chrome-extension/: Manifest V3 extension with popup UI - CLAUDE.md: updated architecture docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 579 B |
Binary file not shown.
|
After Width: | Height: | Size: 117 B |
Binary file not shown.
|
After Width: | Height: | Size: 225 B |
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Alta Proxy Tool Bridge",
|
||||
"version": "1.0.0",
|
||||
"description": "Send Alta session cookies to the Alta Proxy Tool desktop app.",
|
||||
"permissions": ["cookies", "activeTab"],
|
||||
"host_permissions": [
|
||||
"https://*.avasecurity.com/*",
|
||||
"https://*.avigilon.com/*",
|
||||
"http://127.0.0.1:18247/*"
|
||||
],
|
||||
"action": {
|
||||
"default_popup": "popup.html",
|
||||
"default_icon": {
|
||||
"16": "icon16.png",
|
||||
"48": "icon48.png",
|
||||
"128": "icon128.png"
|
||||
}
|
||||
},
|
||||
"icons": {
|
||||
"16": "icon16.png",
|
||||
"48": "icon48.png",
|
||||
"128": "icon128.png"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/* Dark theme matching the Electron app */
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, sans-serif;
|
||||
background: #1E1E1E;
|
||||
color: #E0E0E0;
|
||||
font-size: 14px;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.popup-container {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #0E7AFE;
|
||||
margin: 0 0 12px 0;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.tab-info {
|
||||
background: #2D2D30;
|
||||
border: 1px solid #3C3C3C;
|
||||
border-radius: 4px;
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 12px;
|
||||
font-size: 13px;
|
||||
color: #999999;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.tab-info.detected {
|
||||
color: #4CAF50;
|
||||
border-color: #4CAF50;
|
||||
}
|
||||
|
||||
.tab-info.not-detected {
|
||||
color: #F44336;
|
||||
border-color: #F44336;
|
||||
}
|
||||
|
||||
.send-btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 10px 16px;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
background: #0E7AFE;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.send-btn:hover:not(:disabled) {
|
||||
background: #0A5FD9;
|
||||
}
|
||||
|
||||
.send-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.status-msg {
|
||||
margin-top: 10px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
display: none;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.status-msg.success {
|
||||
display: block;
|
||||
background: rgba(76, 175, 80, 0.1);
|
||||
color: #4CAF50;
|
||||
border-color: #4CAF50;
|
||||
}
|
||||
|
||||
.status-msg.error {
|
||||
display: block;
|
||||
background: rgba(244, 67, 54, 0.1);
|
||||
color: #F44336;
|
||||
border-color: #F44336;
|
||||
}
|
||||
|
||||
.status-msg.info {
|
||||
display: block;
|
||||
background: rgba(14, 122, 254, 0.1);
|
||||
color: #0E7AFE;
|
||||
border-color: #0E7AFE;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; connect-src http://127.0.0.1:18247;">
|
||||
<title>Alta Proxy Tool Bridge</title>
|
||||
<link rel="stylesheet" href="popup.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="popup-container">
|
||||
<h1>Alta Proxy Tool</h1>
|
||||
<div id="tabInfo" class="tab-info">Checking tab...</div>
|
||||
<button id="sendBtn" class="send-btn" disabled>Send Cookie to APT</button>
|
||||
<div id="statusMsg" class="status-msg"></div>
|
||||
</div>
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,100 @@
|
||||
const APT_URL = 'http://127.0.0.1:18247/cookie';
|
||||
const APT_TOKEN = 'apt-local-bridge-token';
|
||||
|
||||
const tabInfo = document.getElementById('tabInfo');
|
||||
const sendBtn = document.getElementById('sendBtn');
|
||||
const statusMsg = document.getElementById('statusMsg');
|
||||
|
||||
let detectedOrigin = null;
|
||||
|
||||
function showStatus(message, type) {
|
||||
statusMsg.textContent = message;
|
||||
statusMsg.className = 'status-msg ' + type;
|
||||
}
|
||||
|
||||
// Check the active tab on popup open
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||||
if (!tabs || tabs.length === 0) {
|
||||
tabInfo.textContent = 'No active tab found.';
|
||||
tabInfo.className = 'tab-info not-detected';
|
||||
return;
|
||||
}
|
||||
|
||||
const tab = tabs[0];
|
||||
let url;
|
||||
try {
|
||||
url = new URL(tab.url);
|
||||
} catch {
|
||||
tabInfo.textContent = 'Cannot read this tab URL.';
|
||||
tabInfo.className = 'tab-info not-detected';
|
||||
return;
|
||||
}
|
||||
|
||||
const hostname = url.hostname;
|
||||
const isAlta = hostname.endsWith('.avasecurity.com') || hostname.endsWith('.avigilon.com');
|
||||
|
||||
if (!isAlta) {
|
||||
tabInfo.textContent = 'This tab is not an Alta deployment.';
|
||||
tabInfo.className = 'tab-info not-detected';
|
||||
return;
|
||||
}
|
||||
|
||||
detectedOrigin = url.origin;
|
||||
tabInfo.textContent = 'Detected: ' + hostname;
|
||||
tabInfo.className = 'tab-info detected';
|
||||
sendBtn.disabled = false;
|
||||
});
|
||||
|
||||
// Send cookie on button click
|
||||
sendBtn.addEventListener('click', async () => {
|
||||
if (!detectedOrigin) return;
|
||||
|
||||
sendBtn.disabled = true;
|
||||
showStatus('Retrieving cookie...', 'info');
|
||||
|
||||
try {
|
||||
const cookie = await chrome.cookies.get({ url: detectedOrigin, name: 'va' });
|
||||
|
||||
if (!cookie || !cookie.value) {
|
||||
showStatus('No "va" session cookie found. Are you logged in?', 'error');
|
||||
sendBtn.disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cookie.expirationDate && cookie.expirationDate < Date.now() / 1000) {
|
||||
showStatus('Session cookie has expired. Please log in again.', 'error');
|
||||
sendBtn.disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
showStatus('Sending to Alta Proxy Tool...', 'info');
|
||||
|
||||
const response = await fetch(APT_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-APT-Token': APT_TOKEN
|
||||
},
|
||||
body: JSON.stringify({
|
||||
deploymentUrl: detectedOrigin,
|
||||
cookieValue: cookie.value
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
showStatus('Cookie sent successfully!', 'success');
|
||||
} else {
|
||||
showStatus('Error: ' + (data.message || 'Unknown error'), 'error');
|
||||
sendBtn.disabled = false;
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.message && err.message.includes('Failed to fetch')) {
|
||||
showStatus('Alta Proxy Tool is not running.', 'error');
|
||||
} else {
|
||||
showStatus('Error: ' + err.message, 'error');
|
||||
}
|
||||
sendBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user