-
Notifications
You must be signed in to change notification settings - Fork 151
updated auth #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
updated auth #42
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ const fs = require('node:fs'); | |
| const path = require('node:path'); | ||
| const os = require('node:os'); | ||
| const { execFile } = require('node:child_process'); | ||
| const { randomBytes } = require('node:crypto'); | ||
|
|
||
| const authSuccessHtml = require('../templates/auth-success.html'); | ||
| const authErrorHtml = require('../templates/auth-error.html'); | ||
|
|
@@ -11,9 +12,9 @@ const SETTINGS_DIR = path.join(os.homedir(), '.supermemory-claude'); | |
| const CREDENTIALS_FILE = path.join(SETTINGS_DIR, 'credentials.json'); | ||
|
|
||
| const AUTH_BASE_URL = | ||
| process.env.SUPERMEMORY_AUTH_URL || 'https://app.supermemory.ai/auth/connect'; | ||
| const AUTH_PORT = 19876; | ||
| const AUTH_TIMEOUT = 25000; | ||
| process.env.SUPERMEMORY_AUTH_URL || | ||
| 'https://console.supermemory.ai/auth/agent-connect'; | ||
| const AUTH_TIMEOUT = Number(process.env.SUPERMEMORY_AUTH_TIMEOUT) || 60000; | ||
|
|
||
| function ensureDir() { | ||
| if (!fs.existsSync(SETTINGS_DIR)) { | ||
|
|
@@ -64,11 +65,19 @@ function openBrowser(url) { | |
| function startAuthFlow() { | ||
| return new Promise((resolve, reject) => { | ||
| let resolved = false; | ||
| const stateToken = randomBytes(16).toString('hex'); | ||
|
|
||
| const server = http.createServer((req, res) => { | ||
| const url = new URL(req.url, `http://localhost:${AUTH_PORT}`); | ||
| const url = new URL(req.url, 'http://localhost'); | ||
|
|
||
| if (url.pathname === '/callback') { | ||
| const callbackState = url.searchParams.get('state'); | ||
| if (callbackState !== stateToken) { | ||
| res.writeHead(403, { 'Content-Type': 'text/html' }); | ||
| res.end(authErrorHtml); | ||
| return; | ||
| } | ||
|
|
||
| const apiKey = | ||
| url.searchParams.get('apikey') || url.searchParams.get('api_key'); | ||
|
|
||
|
|
@@ -77,6 +86,7 @@ function startAuthFlow() { | |
| res.writeHead(200, { 'Content-Type': 'text/html' }); | ||
| res.end(authSuccessHtml); | ||
| resolved = true; | ||
| clearTimeout(timer); | ||
| server.close(); | ||
| resolve(apiKey); | ||
| } else { | ||
|
|
@@ -89,19 +99,31 @@ function startAuthFlow() { | |
| } | ||
| }); | ||
|
|
||
| server.listen(AUTH_PORT, '127.0.0.1', () => { | ||
| const callbackUrl = `http://localhost:${AUTH_PORT}/callback`; | ||
| const authUrl = `${AUTH_BASE_URL}?callback=${encodeURIComponent(callbackUrl)}&client=claude_code`; | ||
| // Listen on an ephemeral port; embed state token in callback URL so the | ||
| // console redirects it back and the CSRF check passes. | ||
| server.listen(0, '127.0.0.1', () => { | ||
| const { port } = server.address(); | ||
| const callbackUrl = `http://localhost:${port}/callback?state=${stateToken}`; | ||
| const params = new URLSearchParams({ | ||
| callback: callbackUrl, | ||
| client: 'claude-code', | ||
| hostname: os.hostname(), | ||
| os: `${process.platform}-${os.arch()}`, | ||
| cwd: process.cwd(), | ||
| cli_version: '1.0.0', | ||
| }); | ||
| const authUrl = `${AUTH_BASE_URL}?${params.toString()}`; | ||
| openBrowser(authUrl); | ||
|
Comment on lines
+104
to
116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Medium -- the manual-visit fallback in When The fallback at If the browser did not open, visit: ${AUTH_BASE_URL}needs to be updated to include the full |
||
| }); | ||
|
|
||
| server.on('error', (err) => { | ||
| if (!resolved) { | ||
| clearTimeout(timer); | ||
| reject(new Error(`Failed to start auth server: ${err.message}`)); | ||
| } | ||
| }); | ||
|
|
||
| setTimeout(() => { | ||
| const timer = setTimeout(() => { | ||
| if (!resolved) { | ||
| server.close(); | ||
| reject(new Error('AUTH_TIMEOUT')); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Medium --
cwdandhostnameleak local machine metadata in the browser URL.hostname,cwd, andosare sent as query parameters on a GET request opened in the user's browser. This means they can end up in browser history, proxy logs, server access logs, and referrer headers.cwdis especially sensitive -- it often contains internal repo names, customer names, or usernames (e.g./home/jdoe/acme-corp/secret-project).Consider whether these params are strictly necessary. If they are needed server-side, sending them in a POST from the local server (rather than embedding in the browser URL) would avoid the exposure.