Files
modeling-app/src/machines/authMachine.ts
2024-02-29 11:57:47 +11:00

163 lines
3.9 KiB
TypeScript

import { createMachine, assign } from 'xstate'
import { Models } from '@kittycad/lib'
import withBaseURL from '../lib/withBaseURL'
import { isTauri } from 'lib/isTauri'
import { invoke } from '@tauri-apps/api'
import { VITE_KC_API_BASE_URL } from 'env'
const SKIP_AUTH =
import.meta.env.VITE_KC_SKIP_AUTH === 'true' && import.meta.env.DEV
const LOCAL_USER: Models['User_type'] = {
id: '8675309',
name: 'Test User',
email: 'kittycad.sidebar.test@example.com',
image: 'https://placekitten.com/200/200',
created_at: 'yesteryear',
updated_at: 'today',
company: 'Test Company',
discord: 'Test User#1234',
github: 'testuser',
phone: '555-555-5555',
first_name: 'Test',
last_name: 'User',
can_train_on_data: false,
is_service_account: false,
}
export interface UserContext {
user?: Models['User_type']
token?: string
}
export type Events =
| {
type: 'Log out'
}
| {
type: 'Log in'
token?: string
}
export const TOKEN_PERSIST_KEY = 'TOKEN_PERSIST_KEY'
const persistedToken =
localStorage?.getItem(TOKEN_PERSIST_KEY) ||
getCookie('__Secure-next-auth.session-token') ||
''
export const authMachine = createMachine<UserContext, Events>(
{
id: 'Auth',
initial: 'checkIfLoggedIn',
states: {
checkIfLoggedIn: {
id: 'check-if-logged-in',
invoke: {
src: 'getUser',
id: 'check-logged-in',
onDone: [
{
target: 'loggedIn',
actions: assign({
user: (context, event) => event.data,
}),
},
],
onError: [
{
target: 'loggedOut',
actions: assign({
user: () => undefined,
}),
},
],
},
},
loggedIn: {
entry: ['goToIndexPage'],
on: {
'Log out': {
target: 'loggedOut',
},
},
},
loggedOut: {
entry: ['goToSignInPage'],
on: {
'Log in': {
target: 'checkIfLoggedIn',
actions: assign({
token: (_, event) => {
const token = event.token || ''
localStorage.setItem(TOKEN_PERSIST_KEY, token)
return token
},
}),
},
},
},
},
schema: { events: {} as { type: 'Log out' } | { type: 'Log in' } },
predictableActionArguments: true,
preserveActionOrder: true,
context: {
token: persistedToken,
},
},
{
actions: {},
services: { getUser },
guards: {},
delays: {},
}
)
async function getUser(context: UserContext) {
const url = withBaseURL('/user')
const headers: { [key: string]: string } = {
'Content-Type': 'application/json',
}
if (!context.token && isTauri()) throw new Error('No token found')
if (context.token) headers['Authorization'] = `Bearer ${context.token}`
if (SKIP_AUTH) return LOCAL_USER
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,
hostname: VITE_KC_API_BASE_URL,
}).catch((err) => console.error('error from Tauri getUser', err))
const user = await userPromise
if ('error_code' in user) throw new Error(user.message)
return user
}
function getCookie(cname: string): string {
if (isTauri()) {
return ''
}
let name = cname + '='
let decodedCookie = decodeURIComponent(document.cookie)
let ca = decodedCookie.split(';')
for (let i = 0; i < ca.length; i++) {
let c = ca[i]
while (c.charAt(0) === ' ') {
c = c.substring(1)
}
if (c.indexOf(name) === 0) {
return c.substring(name.length, c.length)
}
}
return ''
}