Get login working
This commit is contained in:
@ -45,6 +45,7 @@
|
|||||||
"html2canvas-pro": "^1.5.5",
|
"html2canvas-pro": "^1.5.5",
|
||||||
"json-rpc-2.0": "^1.6.0",
|
"json-rpc-2.0": "^1.6.0",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
|
"openid-client": "^5.6.5",
|
||||||
"re-resizable": "^6.9.11",
|
"re-resizable": "^6.9.11",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
@ -149,6 +150,8 @@
|
|||||||
"@types/wait-on": "^5.3.4",
|
"@types/wait-on": "^5.3.4",
|
||||||
"@types/wicg-file-system-access": "^2023.10.5",
|
"@types/wicg-file-system-access": "^2023.10.5",
|
||||||
"@types/ws": "^8.5.10",
|
"@types/ws": "^8.5.10",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||||
|
"@typescript-eslint/parser": "^5.0.0",
|
||||||
"@vitejs/plugin-react": "^4.3.0",
|
"@vitejs/plugin-react": "^4.3.0",
|
||||||
"@vitest/web-worker": "^1.5.0",
|
"@vitest/web-worker": "^1.5.0",
|
||||||
"@wdio/cli": "^8.24.3",
|
"@wdio/cli": "^8.24.3",
|
||||||
@ -177,8 +180,6 @@
|
|||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
"typescript": "^4.5.4",
|
"typescript": "^4.5.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
|
||||||
"@typescript-eslint/parser": "^5.0.0",
|
|
||||||
"vite": "^5.0.12",
|
"vite": "^5.0.12",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-plugin-package-version": "^1.1.0",
|
"vite-plugin-package-version": "^1.1.0",
|
||||||
|
@ -415,10 +415,6 @@ export const setState = async (
|
|||||||
appStateStore = state
|
appStateStore = state
|
||||||
}
|
}
|
||||||
|
|
||||||
export const login = () => {
|
|
||||||
console.log('STUB')
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getUser = async (
|
export const getUser = async (
|
||||||
token: string,
|
token: string,
|
||||||
hostname: string
|
hostname: string
|
||||||
|
@ -5,6 +5,7 @@ import packageJson from '../../package.json'
|
|||||||
|
|
||||||
const open = (args: any) => ipcRenderer.invoke('dialog', args)
|
const open = (args: any) => ipcRenderer.invoke('dialog', args)
|
||||||
const showInFolder = (path: string) => ipcRenderer.invoke('shell.showItemInFolder', path)
|
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 readFile = (path: string) => fs.readFile(path, 'utf-8')
|
||||||
const rename = (prev: string, next: string) => fs.rename(prev, next)
|
const rename = (prev: string, next: string) => fs.rename(prev, next)
|
||||||
@ -31,6 +32,7 @@ const exposeProcessEnv = (varName: string) => {
|
|||||||
|
|
||||||
import('@kittycad/lib').then((kittycad) => {
|
import('@kittycad/lib').then((kittycad) => {
|
||||||
contextBridge.exposeInMainWorld('electron', {
|
contextBridge.exposeInMainWorld('electron', {
|
||||||
|
login,
|
||||||
// Passing fs directly is not recommended since it gives a lot of power
|
// Passing fs directly is not recommended since it gives a lot of power
|
||||||
// to the browser side / potential malicious code. We restrict what is
|
// to the browser side / potential malicious code. We restrict what is
|
||||||
// exported.
|
// exported.
|
||||||
|
@ -146,10 +146,7 @@ async function getUser(context: UserContext) {
|
|||||||
})
|
})
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.catch((err) => console.error('error from Browser getUser', err))
|
.catch((err) => console.error('error from Browser getUser', err))
|
||||||
: getUserDesktop(
|
: getUserDesktop(context.token, VITE_KC_API_BASE_URL )
|
||||||
'f8864550-84a6-4a06-8d3f-68d29bbe5608' /* context.token */,
|
|
||||||
VITE_KC_API_BASE_URL
|
|
||||||
)
|
|
||||||
|
|
||||||
const user = await userPromise
|
const user = await userPromise
|
||||||
|
|
||||||
|
45
src/main.ts
45
src/main.ts
@ -4,6 +4,7 @@
|
|||||||
import { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
import { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
||||||
import { app, BrowserWindow, ipcMain, dialog, shell } from 'electron'
|
import { app, BrowserWindow, ipcMain, dialog, shell } from 'electron'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import { Issuer } from 'openid-client'
|
||||||
|
|
||||||
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
|
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
|
||||||
if (require('electron-squirrel-startup')) {
|
if (require('electron-squirrel-startup')) {
|
||||||
@ -61,3 +62,47 @@ ipcMain.handle('dialog', (event, data) => {
|
|||||||
ipcMain.handle('shell.showItemInFolder', (event, data) => {
|
ipcMain.handle('shell.showItemInFolder', (event, data) => {
|
||||||
return shell.showItemInFolder(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
|
||||||
|
})
|
||||||
|
@ -5,7 +5,6 @@ import { Themes, getSystemTheme } from '../lib/theme'
|
|||||||
import { paths } from 'lib/paths'
|
import { paths } from 'lib/paths'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { APP_NAME } from 'lib/constants'
|
import { APP_NAME } from 'lib/constants'
|
||||||
import { login } from 'lib/desktop'
|
|
||||||
|
|
||||||
const SignIn = () => {
|
const SignIn = () => {
|
||||||
const {
|
const {
|
||||||
@ -25,10 +24,10 @@ const SignIn = () => {
|
|||||||
? '-dark'
|
? '-dark'
|
||||||
: ''
|
: ''
|
||||||
|
|
||||||
const signInTauri = async () => {
|
const signInDesktop = async () => {
|
||||||
// We want to invoke our command to login via device auth.
|
// We want to invoke our command to login via device auth.
|
||||||
try {
|
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 })
|
send({ type: 'Log in', token })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error with login button', error)
|
console.error('Error with login button', error)
|
||||||
@ -64,7 +63,7 @@ const SignIn = () => {
|
|||||||
{isDesktop() ? (
|
{isDesktop() ? (
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
onClick={signInTauri}
|
onClick={signInDesktop}
|
||||||
iconStart={{ icon: 'arrowRight' }}
|
iconStart={{ icon: 'arrowRight' }}
|
||||||
className="w-fit mt-4"
|
className="w-fit mt-4"
|
||||||
data-testid="sign-in-button"
|
data-testid="sign-in-button"
|
||||||
|
Reference in New Issue
Block a user