From 4c2b8078903f33bebeec0980721c2c48a097a6b4 Mon Sep 17 00:00:00 2001 From: 49lf Date: Wed, 31 Jul 2024 14:22:32 -0400 Subject: [PATCH] Get login working --- package.json | 5 +++-- src/lib/desktop.ts | 4 ---- src/lib/electron.ts | 2 ++ src/machines/authMachine.ts | 5 +---- src/main.ts | 45 +++++++++++++++++++++++++++++++++++++ src/routes/SignIn.tsx | 7 +++--- 6 files changed, 54 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index ded59f11e..fb5b8ad43 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "html2canvas-pro": "^1.5.5", "json-rpc-2.0": "^1.6.0", "jszip": "^3.10.1", + "openid-client": "^5.6.5", "re-resizable": "^6.9.11", "react": "^18.3.1", "react-dom": "^18.2.0", @@ -149,6 +150,8 @@ "@types/wait-on": "^5.3.4", "@types/wicg-file-system-access": "^2023.10.5", "@types/ws": "^8.5.10", + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0", "@vitejs/plugin-react": "^4.3.0", "@vitest/web-worker": "^1.5.0", "@wdio/cli": "^8.24.3", @@ -177,8 +180,6 @@ "tailwindcss": "^3.4.1", "ts-node": "^10.0.0", "typescript": "^4.5.4", - "@typescript-eslint/eslint-plugin": "^5.0.0", - "@typescript-eslint/parser": "^5.0.0", "vite": "^5.0.12", "vite-plugin-eslint": "^1.8.1", "vite-plugin-package-version": "^1.1.0", diff --git a/src/lib/desktop.ts b/src/lib/desktop.ts index 05d1606cc..ad561b6ee 100644 --- a/src/lib/desktop.ts +++ b/src/lib/desktop.ts @@ -415,10 +415,6 @@ export const setState = async ( appStateStore = state } -export const login = () => { - console.log('STUB') -} - export const getUser = async ( token: string, hostname: string diff --git a/src/lib/electron.ts b/src/lib/electron.ts index 3ddd1032d..1ac567d7e 100644 --- a/src/lib/electron.ts +++ b/src/lib/electron.ts @@ -5,6 +5,7 @@ import packageJson from '../../package.json' const open = (args: any) => ipcRenderer.invoke('dialog', args) const showInFolder = (path: string) => ipcRenderer.invoke('shell.showItemInFolder', path) +const login = (host: string) => ipcRenderer.invoke('login', host) const readFile = (path: string) => fs.readFile(path, 'utf-8') const rename = (prev: string, next: string) => fs.rename(prev, next) @@ -31,6 +32,7 @@ const exposeProcessEnv = (varName: string) => { import('@kittycad/lib').then((kittycad) => { contextBridge.exposeInMainWorld('electron', { + login, // Passing fs directly is not recommended since it gives a lot of power // to the browser side / potential malicious code. We restrict what is // exported. diff --git a/src/machines/authMachine.ts b/src/machines/authMachine.ts index 11cfa83de..27cd0326c 100644 --- a/src/machines/authMachine.ts +++ b/src/machines/authMachine.ts @@ -146,10 +146,7 @@ async function getUser(context: UserContext) { }) .then((res) => res.json()) .catch((err) => console.error('error from Browser getUser', err)) - : getUserDesktop( - 'f8864550-84a6-4a06-8d3f-68d29bbe5608' /* context.token */, - VITE_KC_API_BASE_URL - ) + : getUserDesktop(context.token, VITE_KC_API_BASE_URL ) const user = await userPromise diff --git a/src/main.ts b/src/main.ts index ac3d7a841..537673a0d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,6 +4,7 @@ import { Configuration } from 'wasm-lib/kcl/bindings/Configuration' import { app, BrowserWindow, ipcMain, dialog, shell } from 'electron' import path from 'path' +import { Issuer } from 'openid-client' // Handle creating/removing shortcuts on Windows when installing/uninstalling. if (require('electron-squirrel-startup')) { @@ -61,3 +62,47 @@ ipcMain.handle('dialog', (event, data) => { ipcMain.handle('shell.showItemInFolder', (event, data) => { return shell.showItemInFolder(data) }) + +ipcMain.handle('login', async (event, host) => { + console.log('Logging in...') + // Do an OAuth 2.0 Device Authorization Grant dance to get a token. + const issuer = new Issuer({ + device_authorization_endpoint: `${host}/oauth2/device/auth`, + token_endpoint: `${host}/oauth2/device/token`, + }) + const client = new issuer.Client({ + // We can hardcode the client ID. + // This value is safe to be embedded in version control. + // This is the client ID of the KittyCAD app. + client_id: "2af127fb-e14e-400a-9c57-a9ed08d1a5b7", + token_endpoint_auth_method: 'none', + }) + + const handle = await client.deviceAuthorization() + + // Open the system browser with the auth_uri. + // We do this in the browser and not a separate window because we want 1password and + // other crap to work well. + // TODO: find a better way to share this value with tauri e2e tests + // Here we're using an env var to enable the /tmp file (windows not supported for now) + // and bypass the shell::open call as it fails on GitHub Actions. + const e2e_tauri_enabled = process.env.E2E_TAURI_ENABLED + if (e2e_tauri_enabled) { + console.warn(`E2E_TAURI_ENABLED is set, won't open ${handle.verification_uri_complete} externally`) + let temp = '/tmp' + // Overwrite with Windows variable + if (process.env.TEMP) { + temp = process.env.TEMP + } + let path = path.join(temp, "kittycad_user_code") + console.log(`Writing to ${path}`) + await fs.writeFile(path, handle.user_code) + } else { + shell.openExternal(handle.verification_uri_complete) + } + + // Wait for the user to login. + const tokenSet = await handle.poll() + + return tokenSet +}) diff --git a/src/routes/SignIn.tsx b/src/routes/SignIn.tsx index 236a80d6e..2651670d4 100644 --- a/src/routes/SignIn.tsx +++ b/src/routes/SignIn.tsx @@ -5,7 +5,6 @@ import { Themes, getSystemTheme } from '../lib/theme' import { paths } from 'lib/paths' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { APP_NAME } from 'lib/constants' -import { login } from 'lib/desktop' const SignIn = () => { const { @@ -25,10 +24,10 @@ const SignIn = () => { ? '-dark' : '' - const signInTauri = async () => { + const signInDesktop = async () => { // We want to invoke our command to login via device auth. try { - const token: string = await login(VITE_KC_API_BASE_URL) + const token: string = await window.electron.login(VITE_KC_API_BASE_URL) send({ type: 'Log in', token }) } catch (error) { console.error('Error with login button', error) @@ -64,7 +63,7 @@ const SignIn = () => { {isDesktop() ? (