const { BrowserWindow } = require('@electron/remote'); const { v4: uuidv4 } = require('uuid'); // Add this for GUID generation class GraphApiClient { constructor() { this.baseUrl = 'https://graph.microsoft.com/v1.0'; this.clientId = "073204aa-c1e0-4e66-a200-e5815a0aa93d"; this.scopes = "OneDrive.ReadWrite offline_access openid profile"; this.redirectUrl = "https://photos.onedrive.com/auth/login"; } async getAccessToken() { return new Promise((resolve, reject) => { const authWindow = new BrowserWindow({ width: 800, height: 600, show: true, webPreferences: { nodeIntegration: false, contextIsolation: true } }); const authUrl = `https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?` + `client_id=${this.clientId}` + `&nonce=uv.${uuidv4()}` + `&response_mode=form_post` + `&scope=${this.scopes}` + `&response_type=code` + `&redirect_uri=${encodeURIComponent(this.redirectUrl)}`; console.log('Loading auth URL:', authUrl); authWindow.loadURL(authUrl); // Handle the navigation events authWindow.webContents.on('will-navigate', (event, url) => { console.log('Navigation detected:', url); handleCallback(url); }); authWindow.webContents.on('will-redirect', (event, url) => { console.log('Redirect detected:', url); handleCallback(url); }); const handleCallback = async (callbackUrl) => { // Check if this is our redirect URI if (callbackUrl.startsWith(this.redirectUrl)) { console.log('Redirect URI matched, getting cookies...'); try { // Get all cookies const cookies = await authWindow.webContents.session.cookies.get({}); console.log('Found cookies:', cookies.length); console.log('Cookies:', cookies); // Find the access token cookie const accessTokenCookie = cookies.find( c => c.name === 'AccessToken-OneDrive.ReadWrite' ); console.log('Access token cookie:', accessTokenCookie); if (!accessTokenCookie) { console.log('Access token not found in cookies'); return; } // Clean and format the access token let accessToken = accessTokenCookie.value; console.log('Access token:', accessToken); // Remove any URL encoding accessToken = decodeURIComponent(accessToken); console.log('Access token decoded:', accessToken); // Remove any extra dots beyond the two expected in a JWT const tokenParts = accessToken.split('.'); if (tokenParts.length > 3) { accessToken = tokenParts.slice(0, 3).join('.'); } console.log('Access token formatted:', accessToken); // Convert cookies to a cookie string const cookieString = cookies .map(cookie => `${cookie.name}=${cookie.value}`) .join('; '); authWindow.close(); resolve({ cookies: cookieString, accessToken: accessToken }); } catch (error) { console.error('Error getting cookies:', error); reject(error); } } }; // Handle window closing authWindow.on('closed', () => { console.log('Auth window closed'); reject(new Error('Authentication window was closed')); }); }); } cleanPath(path) { return path .replace(/^\/+|\/+$/g, '') // Remove leading/trailing slashes .split('/') .map(segment => encodeURIComponent(segment)) .join('/'); } async listFolderContents(folderPath) { try { console.log('graphApiClient listFolderContents:', folderPath); const auth = await this.getAccessToken(); console.log('graphApiClient Auth received'); const cleanPath = this.cleanPath(folderPath); console.log('graphApiClient Clean path:', cleanPath); const url = `${this.baseUrl}/me/drive/root:/${cleanPath}:/children`; console.log('Querying OneDrive items from:', url); const response = await fetch(url, { headers: { 'Authorization': `Bearer ${auth.accessToken}`, 'Accept': 'application/json', 'Cookie': auth.cookies, 'Origin': 'https://onedrive.live.com', 'Referer': 'https://onedrive.live.com/' } }); if (!response.ok) { const errorData = await response.json(); console.error('Graph API error details:', errorData); throw new Error(`Graph API error: ${response.status} ${response.statusText}`); } const data = await response.json(); return data.value; } catch (error) { console.error('Graph API error:', error); throw error; } } } module.exports = new GraphApiClient();