Skip to content

Commit 611c8ed

Browse files
authored
Feature/embed auth (#276)
* Add embedded mx token flow * Implement embedded auth flow * Use embed login flow in embedded mode
1 parent ac475d7 commit 611c8ed

File tree

9 files changed

+77
-12
lines changed

9 files changed

+77
-12
lines changed

extension/src/content/RPCs/fetchData.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ function getCookie(name: string): string | null {
1515
return cookieValue
1616
}
1717

18+
export function getMXToken(): string | null {
19+
// Get the CSRF token from the cookie
20+
return getCookie('mx_jwt')
21+
}
22+
1823
export async function fetchData<T>(
1924
url: string,
2025
method: HttpMethod,

extension/src/content/RPCs/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { captureVisibleTab } from "./rpcCalls"
66
import { copyToClipboard } from "./copyToClipboard"
77
import { getElementScreenCapture } from "./elementScreenCapture"
88
import ripple from "./ripple"
9-
import { fetchData } from "./fetchData"
9+
import { fetchData, getMXToken } from "./fetchData"
1010
import { initWindowListener, RPCPayload } from './initListeners'
1111
import { addNativeElements, attachEventsListener, attachMutationListener, detachMutationListener, initMutationObserver } from "./mutationObserver"
1212
import { respondToOtherTab, forwardToTab, getPendingMessage } from "./crossInstanceComms"
@@ -43,6 +43,7 @@ export const rpc = {
4343
stopRecording,
4444
attachEventsListener,
4545
addNativeElements,
46+
getMXToken
4647
}
4748

4849
type RPC = typeof rpc

web/src/app/api/auth.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,14 @@ export default {
7777
}
7878
return {}
7979
}
80+
},
81+
82+
async embedAuth(token: string) {
83+
try {
84+
const response = await axios.post(`${API_BASE_URL}/embed`, { token });
85+
return response.data;
86+
} catch (error) {
87+
console.error('Error during embed auth:', error);
88+
}
8089
}
81-
}
90+
}

web/src/app/rpc.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export const fetchData = (
106106
log_rpc: true,
107107
})
108108
export const queryURL = () => sendMessage('queryURL', [])
109+
export const getMXToken = () => sendMessage('getMXToken', [])
109110
export const getMetabaseState = (path: Parameters<typeof get>[1]) =>
110111
sendMessage('getMetabaseState', [path], { log_rpc: false })
111112
export const dispatchMetabaseAction = (type: string, payload?: any) =>

web/src/components/common/App.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ const AppLoggedIn = forwardRef((_props, ref) => {
149149
const analystMode = useSelector((state: RootState) => state.settings.analystMode)
150150
const drMode = useSelector((state: RootState) => state.settings.drMode)
151151
const currentAgent: AgentType = analystMode ? AGENTS.EXPLORER : drMode ? AGENTS.SIMPLE : AGENTS.CLASSIC
152+
const isEmbedded = getParsedIframeInfo().isEmbedded as unknown === 'true'
152153

153154
const agentIconMap: Record<AgentType, any> = {
154155
[AGENTS.EXPLORER]: BiSolidMapAlt,
@@ -190,7 +191,7 @@ const AppLoggedIn = forwardRef((_props, ref) => {
190191
// Update thread id on start
191192
useEffect(() => {
192193
dispatch(updateThreadID())
193-
}, [])
194+
}, [])
194195

195196
useEffect(() => {
196197
getBillingInfo().then(billingInfo => {
@@ -214,7 +215,7 @@ const AppLoggedIn = forwardRef((_props, ref) => {
214215
}, [])
215216
useEffect(() => {
216217
const attemptRefresh = () => {
217-
if (configs.IS_DEV) {
218+
if (configs.IS_DEV || isEmbedded) {
218219
return
219220
}
220221
authModule.refresh().then(({ expired, changed, session_jwt, profile_id, email }) => {
@@ -246,7 +247,6 @@ const AppLoggedIn = forwardRef((_props, ref) => {
246247
// const isDevToolsOpen = useSelector((state: RootState) => state.settings.isDevToolsOpen)
247248
const platformShortcut = getPlatformShortcut()
248249
const width = getParsedIframeInfo().width
249-
const isEmbedded = getParsedIframeInfo().isEmbedded as unknown === 'true'
250250

251251
const clearMessages = () => {
252252
if (taskInProgress) {

web/src/components/common/Auth.tsx

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { useState, useRef, useEffect } from 'react';
22
import { Button, Input, Box, VStack, Image, CloseButton, HStack, Text, Progress } from '@chakra-ui/react';
33
import { Select, CreatableSelect } from "chakra-react-select";
44
import { login } from '../../state/auth/reducer'
5-
import { dispatch } from '../../state/dispatch'
5+
import { dispatch, logoutState, resetState } from '../../state/dispatch'
66
import {auth, auth as authModule} from '../../app/api'
77
import { useSelector } from 'react-redux';
88
import logo from '../../assets/img/logo.svg'
@@ -14,10 +14,11 @@ import { TelemetryToggle } from '../devtools/Settings';
1414
import { getParsedIframeInfo } from '../../helpers/origin';
1515
import { toast } from '../../app/toast';
1616
import { get } from 'lodash';
17-
import { setMinusxMode } from '../../app/rpc';
17+
import { getMXToken, setMinusxMode } from '../../app/rpc';
1818
import { RootState } from '../../state/store';
1919
import { getApp } from '../../helpers/app';
2020
import { Markdown } from './Markdown';
21+
import { setCurrentEmail } from '../../state/settings/reducer';
2122

2223

2324
const useAppStore = getApp().useStore()
@@ -177,6 +178,35 @@ const Auth = () => {
177178
const [discoverySource, setDiscoverySource] = useState("");
178179
const isOTPMode = authJWT ? true : false
179180
const helperMessage = useAppStore((state) => state.helperMessage)?.split('---')[1] || "Welcome to MinusX! You can ask us anything related to your data, and our agents will take care of the rest!"
181+
const isEmbedded = getParsedIframeInfo().isEmbedded as unknown === 'true'
182+
const currentEmail = useSelector((state: RootState) => state.settings.currentEmail)
183+
184+
useEffect(() => {
185+
const checkToken = async () => {
186+
const mx_token = await getMXToken()
187+
188+
if (mx_token) {
189+
try {
190+
const embedAuthResult = await authModule.embedAuth(mx_token)
191+
const { session_jwt, profile_id, email } = embedAuthResult
192+
if (email != currentEmail) {
193+
resetState()
194+
}
195+
dispatch(login({
196+
session_jwt,
197+
profile_id,
198+
email,
199+
}))
200+
dispatch(setCurrentEmail(email))
201+
} catch (error) {
202+
console.error('Failed to authenticate embed token:', error)
203+
}
204+
}
205+
}
206+
if (isEmbedded) {
207+
checkToken()
208+
}
209+
}, [])
180210

181211

182212
useEffect(() => {
@@ -217,6 +247,7 @@ const Auth = () => {
217247
profile_id,
218248
email,
219249
}))
250+
dispatch(setCurrentEmail(email))
220251
if (is_new_user) {
221252
captureEvent(GLOBAL_EVENTS.user_signup, { email, profile_id, discoverySource })
222253
} else {
@@ -330,12 +361,13 @@ const Auth = () => {
330361
borderWidth={1.5} borderLeftColor={"minusxBW.500"}>
331362
<Image src={logo} alt="MinusX" maxWidth='150px'/>
332363
<VStack spacing={4} mt={5} position={"relative"}>
364+
{isEmbedded && <Text>Logging you in...</Text>}
333365
<Input
334366
type="email"
335367
placeholder="Enter work email ID"
336368
aria-label="Enter work email ID"
337369
value={email}
338-
disabled={isOTPMode ? true : false}
370+
disabled={isEmbedded ? true : isOTPMode ? true : false}
339371
onChange={(e) => setEmail(e.target.value)}
340372
// just trigger the handleSignin function when enter is pressed in this input as well
341373
onKeyUp={(e) => {

web/src/components/devtools/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export const DevToolsBox: React.FC = () => {
6969
// Check existing dev/production logic
7070
const isAllowedByEnv = configs.IS_DEV || Monitor.tags?.includes('production')
7171

72-
if (isEmbedded) {
72+
if (isEmbedded && !configs.IS_DEV) {
7373
if (Monitor.title === 'History' || Monitor.title === 'Memory') {
7474
return true
7575
}

web/src/state/settings/reducer.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ interface Settings {
133133
metadataHashes: Record<string, number>
134134
metadataProcessingCache: Record<number, MetadataProcessingCacheEntry>
135135
manuallyLimitContext: boolean
136+
currentEmail?: string
136137
}
137138

138139
const initialState: Settings = {
@@ -224,6 +225,9 @@ export const settingsSlice = createSlice({
224225
setAppRecording: (state, action: PayloadAction<boolean>) => {
225226
state.isRecording = action.payload
226227
},
228+
setCurrentEmail: (state, action: PayloadAction<string | undefined>) => {
229+
state.currentEmail = action.payload
230+
},
227231
setAiRules: (state, action: PayloadAction<string>) => {
228232
state.aiRules = action.payload
229233
},
@@ -412,7 +416,7 @@ export const { updateIsLocal, updateUploadLogs,
412416
setIframeInfo, setConfirmChanges, setDemoMode, setAppRecording, setAiRules,
413417
applyTableDiff, setSelectedModels, setDRMode, setAnalystMode, setSelectedCatalog, saveCatalog, deleteCatalog, setMemberships,
414418
setGroupsEnabled, resetDefaultTablesDB, setModelsMode, setViewAllCatalogs, setEnableHighlightHelpers, setUseMemory, addMemory, setCustomCSS, setEnableStyleCustomization, setEnableUserDebugTools, setEnableReviews, setMetadataHash, setMetadataProcessingCache, clearMetadataProcessingCache,
415-
updateManualContextSelection
419+
updateManualContextSelection, setCurrentEmail
416420
} = settingsSlice.actions
417421

418422
export default settingsSlice.reducer

web/src/state/store.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import cache from './cache/reducer'
1616
import notifications from './notifications/reducer'
1717
import { userStateApi } from '../app/api/userStateApi'
1818
import { get } from 'lodash'
19+
import { getParsedIframeInfo } from '../helpers/origin'
1920

2021
const combinedReducer = combineReducers({
2122
chat,
@@ -502,13 +503,25 @@ const migrations = {
502503
newState.settings.enableReviews = true
503504
return newState
504505
},
506+
50: (state: RootState) => {
507+
let newState = {...state}
508+
// Refresh metadataProcessingCache
509+
newState.settings.currentEmail = newState.auth?.email
510+
return newState
511+
},
512+
}
513+
514+
const BLACKLIST = ['billing', 'cache', userStateApi.reducerPath]
515+
const isEmbedded = getParsedIframeInfo().isEmbedded as unknown === 'true'
516+
if (isEmbedded) {
517+
BLACKLIST.push('auth')
505518
}
506519

507520
const persistConfig = {
508521
key: 'root',
509-
version: 49,
522+
version: 50,
510523
storage,
511-
blacklist: ['billing', 'cache', userStateApi.reducerPath],
524+
blacklist: BLACKLIST,
512525
// @ts-ignore
513526
migrate: createMigrate(migrations, { debug: true }),
514527
};

0 commit comments

Comments
 (0)