Franknoirot/fix prod tauri auth (#531)

* Invoke tauri-based logout if in tauri

* Add kittycad Rust library

* Add logout and get_user Tauri commands

* Invoke get_user instead of fetching in Tauri

* @jessfraz review

Signed-off-by: Frank Noirot <frank@kittycad.io>

* Remove unnecessary logout command

* Fix rushed last commit

---------

Signed-off-by: Frank Noirot <frank@kittycad.io>
This commit is contained in:
Frank Noirot
2023-09-14 19:31:16 -04:00
committed by GitHub
parent 6e3c642d22
commit d7ad7c749e
5 changed files with 692 additions and 89 deletions

712
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -16,13 +16,14 @@ tauri-build = { version = "1.4.0", features = [] }
[dependencies] [dependencies]
anyhow = "1" anyhow = "1"
kittycad = "0.2.25"
oauth2 = "4.4.2" oauth2 = "4.4.2"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
tauri = { version = "1.4.1", features = ["dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "updater", "devtools"] } tauri = { version = "1.4.1", features = ["dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "updater", "devtools"] }
tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
tokio = { version = "1.32.0", features = ["time"] } tokio = { version = "1.32.0", features = ["time"] }
toml = "0.8.0" toml = "0.8.0"
tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
[features] [features]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.

View File

@ -85,6 +85,24 @@ async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError>
Ok(token) Ok(token)
} }
///This command returns the KittyCAD user info given a token.
/// The string returned from this method is the user info as a json string.
#[tauri::command]
async fn get_user(token: Option<String>) -> Result<kittycad::types::User, InvokeError> {
println!("Getting user info...");
// use kittycad library to fetch the user info from /user/me
let client = kittycad::Client::new(token.unwrap());
let user_info: kittycad::types::User = client
.users()
.get_self()
.await
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
Ok(user_info)
}
fn main() { fn main() {
tauri::Builder::default() tauri::Builder::default()
.setup(|app| { .setup(|app| {
@ -97,7 +115,12 @@ fn main() {
} }
Ok(()) Ok(())
}) })
.invoke_handler(tauri::generate_handler![login, read_toml, read_txt_file]) .invoke_handler(tauri::generate_handler![
get_user,
login,
read_toml,
read_txt_file
])
.plugin(tauri_plugin_fs_extra::init()) .plugin(tauri_plugin_fs_extra::init())
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");

View File

@ -24,6 +24,9 @@ import {
StateFrom, StateFrom,
} from 'xstate' } from 'xstate'
import { useCommandsContext } from 'hooks/useCommandsContext' import { useCommandsContext } from 'hooks/useCommandsContext'
import { invoke } from '@tauri-apps/api'
import { isTauri } from 'lib/isTauri'
import { VITE_KC_API_BASE_URL } from 'env'
type MachineContext<T extends AnyStateMachine> = { type MachineContext<T extends AnyStateMachine> = {
state: StateFrom<T> state: StateFrom<T>
@ -108,6 +111,7 @@ export const GlobalStateProvider = ({
actions: { actions: {
goToSignInPage: () => { goToSignInPage: () => {
navigate(paths.SIGN_IN) navigate(paths.SIGN_IN)
logout() logout()
}, },
goToIndexPage: () => { goToIndexPage: () => {
@ -149,10 +153,12 @@ export const GlobalStateProvider = ({
export default GlobalStateProvider export default GlobalStateProvider
export function logout() { export function logout() {
const url = withBaseUrl('/logout')
localStorage.removeItem(TOKEN_PERSIST_KEY) localStorage.removeItem(TOKEN_PERSIST_KEY)
return fetch(url, { return (
method: 'POST', !isTauri() &&
credentials: 'include', fetch(withBaseUrl('/logout'), {
}) method: 'POST',
credentials: 'include',
})
)
} }

View File

@ -2,6 +2,8 @@ import { createMachine, assign } from 'xstate'
import { Models } from '@kittycad/lib' import { Models } from '@kittycad/lib'
import withBaseURL from '../lib/withBaseURL' import withBaseURL from '../lib/withBaseURL'
import { CommandBarMeta } from '../lib/commands' import { CommandBarMeta } from '../lib/commands'
import { isTauri } from 'lib/isTauri'
import { invoke } from '@tauri-apps/api'
const SKIP_AUTH = const SKIP_AUTH =
import.meta.env.VITE_KC_SKIP_AUTH === 'true' && import.meta.env.DEV import.meta.env.VITE_KC_SKIP_AUTH === 'true' && import.meta.env.DEV
@ -115,16 +117,25 @@ async function getUser(context: UserContext) {
const headers: { [key: string]: string } = { const headers: { [key: string]: string } = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
} }
if (!context.token && '__TAURI__' in window) throw 'not log in'
if (!context.token && isTauri()) throw new Error('No token found')
if (context.token) headers['Authorization'] = `Bearer ${context.token}` if (context.token) headers['Authorization'] = `Bearer ${context.token}`
if (SKIP_AUTH) return LOCAL_USER if (SKIP_AUTH) return LOCAL_USER
const response = await fetch(url, {
method: 'GET',
credentials: 'include',
headers,
})
const user = await response.json() const userPromise = !isTauri()
? fetch(url, {
method: 'GET',
credentials: 'include',
headers,
})
.then((res) => res.json())
.catch((err) => console.error('error from Browser getUser', err))
: invoke<Models['User_type'] | Record<'error_code', unknown>>('get_user', {
token: context.token,
}).catch((err) => console.error('error from Tauri getUser', err))
const user = await userPromise
if ('error_code' in user) throw new Error(user.message) if ('error_code' in user) throw new Error(user.message)
return user return user