Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/composables/auth/useCurrentUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ export const useCurrentUser = () => {
whenever(() => authStore.tokenRefreshTrigger, callback)

const onUserLogout = (callback: () => void) => {
watch(resolvedUserInfo, (user) => {
if (!user) callback()
watch(resolvedUserInfo, (user, prevUser) => {
if (prevUser && !user) callback()
})
}

Expand Down
9 changes: 8 additions & 1 deletion src/composables/useFeatureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export enum ServerFeatureFlag {
ONBOARDING_SURVEY_ENABLED = 'onboarding_survey_enabled',
HUGGINGFACE_MODEL_IMPORT_ENABLED = 'huggingface_model_import_enabled',
LINEAR_TOGGLE_ENABLED = 'linear_toggle_enabled',
ASYNC_MODEL_UPLOAD_ENABLED = 'async_model_upload_enabled'
ASYNC_MODEL_UPLOAD_ENABLED = 'async_model_upload_enabled',
TEAM_WORKSPACES_ENABLED = 'team_workspaces_enabled'
}

/**
Expand Down Expand Up @@ -92,6 +93,12 @@ export function useFeatureFlags() {
false
)
)
},
get teamWorkspacesEnabled() {
return (
remoteConfig.value.team_workspaces_enabled ??
api.getServerFeature(ServerFeatureFlag.TEAM_WORKSPACES_ENABLED, false)
)
}
})

Expand Down
15 changes: 15 additions & 0 deletions src/locales/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -2535,5 +2535,20 @@
"completed": "Completed",
"failed": "Failed"
}
},
"workspace": {
"unsavedChanges": {
"title": "Unsaved Changes",
"message": "You have unsaved changes. Do you want to discard them and switch workspaces?"
}
},
"workspaceAuth": {
"errors": {
"notAuthenticated": "You must be logged in to access workspaces",
"invalidFirebaseToken": "Authentication failed. Please try logging in again.",
"accessDenied": "You do not have access to this workspace",
"workspaceNotFound": "Workspace not found",
"tokenExchangeFailed": "Failed to authenticate with workspace: {error}"
}
}
}
88 changes: 61 additions & 27 deletions src/platform/auth/session/useSessionCookie.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { api } from '@/scripts/api'
import { isCloud } from '@/platform/distribution/types'
import { remoteConfig } from '@/platform/remoteConfig/remoteConfig'
import { api } from '@/scripts/api'
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'

/**
Expand All @@ -10,31 +11,59 @@ export const useSessionCookie = () => {
/**
* Creates or refreshes the session cookie.
* Called after login and on token refresh.
*
* When team_workspaces_enabled is true, uses Firebase token directly
* (since getAuthHeader() returns workspace token which shouldn't be used for session creation).
* When disabled, uses getAuthHeader() for backward compatibility.
*/
const createSession = async (): Promise<void> => {
if (!isCloud) return

const authStore = useFirebaseAuthStore()
const authHeader = await authStore.getAuthHeader()
try {
const authStore = useFirebaseAuthStore()

if (!authHeader) {
throw new Error('No auth header available for session creation')
}
let authHeader: Record<string, string>

const response = await fetch(api.apiURL('/auth/session'), {
method: 'POST',
credentials: 'include',
headers: {
...authHeader,
'Content-Type': 'application/json'
if (remoteConfig.value.team_workspaces_enabled) {
const firebaseToken = await authStore.getIdToken()
if (!firebaseToken) {
console.warn(
'Failed to create session cookie:',
'No Firebase token available for session creation'
)
return
}
authHeader = { Authorization: `Bearer ${firebaseToken}` }
} else {
const header = await authStore.getAuthHeader()
if (!header) {
console.warn(
'Failed to create session cookie:',
'No auth header available for session creation'
)
return
}
authHeader = header
}
})

if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
throw new Error(
`Failed to create session: ${errorData.message || response.statusText}`
)
const response = await fetch(api.apiURL('/auth/session'), {
method: 'POST',
credentials: 'include',
headers: {
...authHeader,
'Content-Type': 'application/json'
}
})

if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
console.warn(
'Failed to create session cookie:',
errorData.message || response.statusText
)
}
} catch (error) {
console.warn('Failed to create session cookie:', error)
}
}

Expand All @@ -45,16 +74,21 @@ export const useSessionCookie = () => {
const deleteSession = async (): Promise<void> => {
if (!isCloud) return

const response = await fetch(api.apiURL('/auth/session'), {
method: 'DELETE',
credentials: 'include'
})
try {
const response = await fetch(api.apiURL('/auth/session'), {
method: 'DELETE',
credentials: 'include'
})

if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
throw new Error(
`Failed to delete session: ${errorData.message || response.statusText}`
)
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
console.warn(
'Failed to delete session cookie:',
errorData.message || response.statusText
)
}
} catch (error) {
console.warn('Failed to delete session cookie:', error)
}
}

Expand Down
Loading
Loading