2026-02-04 00:07:29 -05:00
"use client" ;
import { useState } from "react" ;
import PropTypes from "prop-types" ;
import { Modal , Button , Input } from "@/shared/components" ;
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard" ;
/ * *
* Cursor Auth Modal
* Import token from Cursor IDE ' s local SQLite database
*
* Token Location :
* - Linux : ~ / . c o n f i g / C u r s o r / U s e r / g l o b a l S t o r a g e / s t a t e . v s c d b
2026-02-04 23:02:50 -05:00
* - macOS : /Users/ < user > / L i b r a r y / A p p l i c a t i o n S u p p o r t / C u r s o r / U s e r / g l o b a l S t o r a g e / s t a t e . v s c d b
2026-02-04 00:07:29 -05:00
* - Windows : % APPDATA % \ Cursor \ User \ globalStorage \ state . vscdb
*
* Database Keys :
* - cursorAuth / accessToken : The access token
* - storage . serviceMachineId : Machine ID for checksum
* /
export default function CursorAuthModal ( { isOpen , onSuccess , onClose } ) {
const [ accessToken , setAccessToken ] = useState ( "" ) ;
const [ machineId , setMachineId ] = useState ( "" ) ;
const [ error , setError ] = useState ( null ) ;
const [ importing , setImporting ] = useState ( false ) ;
const { copied , copy } = useCopyToClipboard ( ) ;
const handleImportToken = async ( ) => {
if ( ! accessToken . trim ( ) ) {
setError ( "Please enter an access token" ) ;
return ;
}
if ( ! machineId . trim ( ) ) {
setError ( "Please enter a machine ID" ) ;
return ;
}
setImporting ( true ) ;
setError ( null ) ;
try {
const res = await fetch ( "/api/oauth/cursor/import" , {
method : "POST" ,
headers : { "Content-Type" : "application/json" } ,
body : JSON . stringify ( {
accessToken : accessToken . trim ( ) ,
machineId : machineId . trim ( ) ,
} ) ,
} ) ;
const data = await res . json ( ) ;
if ( ! res . ok ) {
throw new Error ( data . error || "Import failed" ) ;
}
// Success - close modal and trigger refresh
onSuccess ? . ( ) ;
onClose ( ) ;
} catch ( err ) {
setError ( err . message ) ;
} finally {
setImporting ( false ) ;
}
} ;
const linuxCommand = ` sqlite3 ~/.config/Cursor/User/globalStorage/state.vscdb "SELECT key, value FROM itemTable WHERE key IN ('cursorAuth/accessToken', 'storage.serviceMachineId')" ` ;
2026-02-04 23:02:50 -05:00
const macCommand = ` sqlite3 "/Users/ $ USER/Library/Application Support/Cursor/User/globalStorage/state.vscdb" "SELECT key, value FROM itemTable WHERE key IN ('cursorAuth/accessToken', 'storage.serviceMachineId')" ` ;
2026-02-04 00:07:29 -05:00
return (
< Modal isOpen = { isOpen } title = "Connect Cursor IDE" onClose = { onClose } size = "lg" >
< div className = "flex flex-col gap-4" >
{ /* Info Box */ }
< div className = "bg-blue-50 dark:bg-blue-900/20 p-4 rounded-lg border border-blue-200 dark:border-blue-800" >
< div className = "flex gap-2" >
< span className = "material-symbols-outlined text-blue-600 dark:text-blue-400" > info < / s p a n >
< div className = "flex-1 text-sm" >
< p className = "font-medium text-blue-900 dark:text-blue-100 mb-1" >
Prerequisites
< / p >
< p className = "text-blue-800 dark:text-blue-200" >
Make sure you are logged in to Cursor IDE first . Tokens are stored in the local SQLite database .
< / p >
< / d i v >
< / d i v >
< / d i v >
{ /* Instructions */ }
< div className = "space-y-3" >
< p className = "text-sm font-medium" > How to get your tokens : < / p >
< div className = "bg-sidebar/50 p-3 rounded-lg space-y-2" >
2026-02-04 23:02:50 -05:00
< p className = "text-xs text-text-muted" > Linux : < / p >
2026-02-04 00:07:29 -05:00
< div className = "flex items-start gap-2" >
< code className = "text-xs bg-sidebar px-2 py-1 rounded flex-1 overflow-x-auto whitespace-pre" >
{ linuxCommand }
< / c o d e >
< button
onClick = { ( ) => copy ( linuxCommand , "linux-cmd" ) }
className = "p-1 hover:bg-sidebar rounded text-text-muted hover:text-primary flex-shrink-0"
title = "Copy command"
>
< span className = "material-symbols-outlined text-sm" >
{ copied === "linux-cmd" ? "check" : "content_copy" }
< / s p a n >
< / b u t t o n >
< / d i v >
< / d i v >
2026-02-04 23:02:50 -05:00
< div className = "bg-sidebar/50 p-3 rounded-lg space-y-2" >
< p className = "text-xs text-text-muted" > macOS : < / p >
< div className = "flex items-start gap-2" >
< code className = "text-xs bg-sidebar px-2 py-1 rounded flex-1 overflow-x-auto whitespace-pre" >
{ macCommand }
< / c o d e >
< button
onClick = { ( ) => copy ( macCommand , "mac-cmd" ) }
className = "p-1 hover:bg-sidebar rounded text-text-muted hover:text-primary flex-shrink-0"
title = "Copy command"
>
< span className = "material-symbols-outlined text-sm" >
{ copied === "mac-cmd" ? "check" : "content_copy" }
< / s p a n >
< / b u t t o n >
< / d i v >
< / d i v >
2026-02-04 00:07:29 -05:00
< div className = "text-xs text-text-muted" >
< p className = "mb-1" > Database locations : < / p >
< ul className = "list-disc list-inside space-y-0.5" >
< li > Linux : < code className = "bg-sidebar px-1 rounded" > ~ / . c o n f i g / C u r s o r / U s e r / g l o b a l S t o r a g e / s t a t e . v s c d b < / c o d e > < / l i >
2026-02-04 23:02:50 -05:00
< li > macOS : < code className = "bg-sidebar px-1 rounded" > /Users/ & lt ; user & gt ; / L i b r a r y / A p p l i c a t i o n S u p p o r t / C u r s o r / U s e r / g l o b a l S t o r a g e / s t a t e . v s c d b < / c o d e > < / l i >
2026-02-04 00:07:29 -05:00
< li > Windows : < code className = "bg-sidebar px-1 rounded" > % APPDATA % \ Cursor \ User \ globalStorage \ state . vscdb < / c o d e > < / l i >
< / u l >
< / d i v >
< / d i v >
{ /* Access Token Input */ }
< div >
< label className = "block text-sm font-medium mb-2" >
Access Token < span className = "text-red-500" > * < / s p a n >
< / l a b e l >
< textarea
value = { accessToken }
onChange = { ( e ) => setAccessToken ( e . target . value ) }
placeholder = "Paste your access token here..."
rows = { 3 }
className = "w-full px-3 py-2 text-sm font-mono border border-border rounded-lg bg-background focus:outline-none focus:border-primary resize-none"
/ >
< p className = "text-xs text-text-muted mt-1" >
From key : < code className = "bg-sidebar px-1 rounded" > cursorAuth / accessToken < / c o d e >
< / p >
< / d i v >
{ /* Machine ID Input */ }
< div >
< label className = "block text-sm font-medium mb-2" >
Machine ID < span className = "text-red-500" > * < / s p a n >
< / l a b e l >
< Input
value = { machineId }
onChange = { ( e ) => setMachineId ( e . target . value ) }
placeholder = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
className = "font-mono text-sm"
/ >
< p className = "text-xs text-text-muted mt-1" >
From key : < code className = "bg-sidebar px-1 rounded" > storage . serviceMachineId < / c o d e >
< / p >
< / d i v >
{ /* Error Display */ }
{ error && (
< div className = "bg-red-50 dark:bg-red-900/20 p-3 rounded-lg border border-red-200 dark:border-red-800" >
< p className = "text-sm text-red-600 dark:text-red-400" > { error } < / p >
< / d i v >
) }
{ /* Action Buttons */ }
< div className = "flex gap-2" >
< Button
onClick = { handleImportToken }
fullWidth
disabled = { importing || ! accessToken . trim ( ) || ! machineId . trim ( ) }
>
{ importing ? "Importing..." : "Import Token" }
< / B u t t o n >
< Button onClick = { onClose } variant = "ghost" fullWidth >
Cancel
< / B u t t o n >
< / d i v >
< / d i v >
< / M o d a l >
) ;
}
CursorAuthModal . propTypes = {
isOpen : PropTypes . bool . isRequired ,
onSuccess : PropTypes . func ,
onClose : PropTypes . func . isRequired ,
} ;