mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-08-21 06:23:59 +00:00
Co-authored-by: Prasanna <106952318+Prasanna721@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: ved015 <vedant.04.mahajan@gmail.com> Co-authored-by: ved015 <ved015@users.noreply.github.com> Co-authored-by: Ishaan Gupta <ishaankone@gmail.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com>
121 lines
3.7 KiB
TypeScript
121 lines
3.7 KiB
TypeScript
// One-time helper to capture a Tier D refresh token — run: bun e2e/capture-oauth-token.ts
|
|
|
|
import { createHash, randomBytes } from "node:crypto"
|
|
import { chmod, mkdir, writeFile } from "node:fs/promises"
|
|
import { createServer } from "node:http"
|
|
import { exec } from "node:child_process"
|
|
import { dirname } from "node:path"
|
|
import { fileURLToPath } from "node:url"
|
|
|
|
const API_URL = process.env.SUPERMEMORY_API_URL ?? "https://api.supermemory.ai"
|
|
const MCP_RESOURCE =
|
|
process.env.SUPERMEMORY_MCP_RESOURCE ?? "https://mcp.supermemory.ai/mcp"
|
|
const CREDENTIAL_FILE =
|
|
process.env.SUPERMEMORY_MCP_CREDENTIAL_FILE ??
|
|
fileURLToPath(new URL("../../../.context/mcp-oauth.env", import.meta.url))
|
|
const PORT = Number(process.env.SUPERMEMORY_MCP_CALLBACK_PORT ?? "8765")
|
|
const REDIRECT_URI = `http://localhost:${PORT}/callback`
|
|
|
|
const b64url = (b: Buffer) =>
|
|
b
|
|
.toString("base64")
|
|
.replace(/\+/g, "-")
|
|
.replace(/\//g, "_")
|
|
.replace(/=+$/, "")
|
|
|
|
async function main() {
|
|
const meta = (await (
|
|
await fetch(`${API_URL}/.well-known/oauth-authorization-server`)
|
|
).json()) as {
|
|
authorization_endpoint: string
|
|
token_endpoint: string
|
|
registration_endpoint: string
|
|
}
|
|
|
|
const reg = (await (
|
|
await fetch(meta.registration_endpoint, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
client_name: "sm-mcp-e2e-capture",
|
|
redirect_uris: [REDIRECT_URI],
|
|
grant_types: ["authorization_code", "refresh_token"],
|
|
response_types: ["code"],
|
|
token_endpoint_auth_method: "none",
|
|
}),
|
|
})
|
|
).json()) as { client_id: string }
|
|
|
|
const verifier = b64url(randomBytes(32))
|
|
const challenge = b64url(createHash("sha256").update(verifier).digest())
|
|
const state = b64url(randomBytes(16))
|
|
|
|
const authUrl = new URL(meta.authorization_endpoint)
|
|
authUrl.search = new URLSearchParams({
|
|
response_type: "code",
|
|
client_id: reg.client_id,
|
|
redirect_uri: REDIRECT_URI,
|
|
code_challenge: challenge,
|
|
code_challenge_method: "S256",
|
|
scope: "openid profile email offline_access",
|
|
resource: MCP_RESOURCE,
|
|
state,
|
|
}).toString()
|
|
|
|
const code: string = await new Promise((resolve, reject) => {
|
|
const server = createServer((req, res) => {
|
|
const u = new URL(req.url ?? "", `http://localhost:${PORT}`)
|
|
if (u.pathname !== "/callback") return res.end()
|
|
if (u.searchParams.get("state") !== state) {
|
|
res.end("state mismatch")
|
|
return reject(new Error("state mismatch"))
|
|
}
|
|
const c = u.searchParams.get("code")
|
|
res.end("Done — you can close this tab.")
|
|
server.close()
|
|
c ? resolve(c) : reject(new Error("no code in callback"))
|
|
}).listen(PORT, () => {
|
|
console.log(`\nOpening browser to log in:\n${authUrl}\n`)
|
|
exec(`open "${authUrl}" || xdg-open "${authUrl}"`)
|
|
})
|
|
})
|
|
|
|
const tokenRes = (await (
|
|
await fetch(meta.token_endpoint, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
body: new URLSearchParams({
|
|
grant_type: "authorization_code",
|
|
code,
|
|
client_id: reg.client_id,
|
|
code_verifier: verifier,
|
|
redirect_uri: REDIRECT_URI,
|
|
resource: MCP_RESOURCE,
|
|
}),
|
|
})
|
|
).json()) as { refresh_token?: string; error?: string }
|
|
|
|
if (!tokenRes.refresh_token) {
|
|
console.error("No refresh_token returned:", tokenRes)
|
|
process.exit(1)
|
|
}
|
|
|
|
await mkdir(dirname(CREDENTIAL_FILE), { recursive: true })
|
|
await writeFile(
|
|
CREDENTIAL_FILE,
|
|
[
|
|
`SUPERMEMORY_MCP_CLIENT_ID=${JSON.stringify(reg.client_id)}`,
|
|
`SUPERMEMORY_MCP_REFRESH_TOKEN=${JSON.stringify(tokenRes.refresh_token)}`,
|
|
"",
|
|
].join("\n"),
|
|
{ mode: 0o600 },
|
|
)
|
|
await chmod(CREDENTIAL_FILE, 0o600)
|
|
|
|
console.log(`\nOAuth test credentials saved to ${CREDENTIAL_FILE}`)
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error(e)
|
|
process.exit(1)
|
|
})
|