Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 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
14 changes: 14 additions & 0 deletions src/composables/auth/useCurrentUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,19 @@ export const useCurrentUser = () => {
return null
})

<<<<<<< HEAD
const onUserResolved = (callback: (user: AuthUserInfo) => void) =>
whenever(resolvedUserInfo, callback, { immediate: true })
=======
const onUserResolved = (callback: (user: AuthUserInfo) => void) => {
if (resolvedUserInfo.value) {
callback(resolvedUserInfo.value)
}

const stop = whenever(resolvedUserInfo, callback)
return () => stop()
}
>>>>>>> 775c856bf (port user ID expose hook from 6786d8e to cloud)
Comment on lines +40 to +49
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do note use this. take HEAD


const userDisplayName = computed(() => {
if (isApiKeyLogin.value) {
Expand Down Expand Up @@ -132,7 +143,10 @@ export const useCurrentUser = () => {
resolvedUserInfo,
handleSignOut,
handleSignIn,
<<<<<<< HEAD
handleDeleteAccount,
=======
>>>>>>> 775c856bf (port user ID expose hook from 6786d8e to cloud)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take deletion

onUserResolved
}
}
8 changes: 8 additions & 0 deletions src/composables/auth/useFirebaseAuthActions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { FirebaseError } from 'firebase/app'
import { AuthErrorCodes } from 'firebase/auth'
import { ref } from 'vue'
import { useRouter } from 'vue-router'

import { useErrorHandling } from '@/composables/useErrorHandling'
import type { ErrorRecoveryStrategy } from '@/composables/useErrorHandling'
Expand All @@ -18,6 +19,7 @@ import { usdToMicros } from '@/utils/formatUtil'
export const useFirebaseAuthActions = () => {
const authStore = useFirebaseAuthStore()
const toastStore = useToastStore()
const router = useRouter()
const { wrapWithErrorHandlingAsync, toastErrorHandler } = useErrorHandling()

const accessError = ref(false)
Expand Down Expand Up @@ -54,6 +56,12 @@ export const useFirebaseAuthActions = () => {
detail: t('auth.signOut.successDetail'),
life: 5000
})

// Redirect to login page if we're on cloud domain
const hostname = window.location.hostname
if (hostname.includes('cloud.comfy.org')) {
Copy link
Contributor Author

@arjansingh arjansingh Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

short term: if(isCloud)...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

longer term: /logout route that calls firebase and then redirects the users.

await router.push({ name: 'cloud-login' })
}
}, reportError)

const sendPasswordReset = wrapWithErrorHandlingAsync(
Expand Down
44 changes: 41 additions & 3 deletions src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import {
createWebHistory
} from 'vue-router'

import { useDialogService } from '@/services/dialogService'
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
import { useUserStore } from '@/stores/userStore'
import { isElectron } from '@/utils/envUtil'
import LayoutDefault from '@/views/layouts/LayoutDefault.vue'

import { useUserStore } from './stores/userStore'
import { isElectron } from './utils/envUtil'

const isFileProtocol = window.location.protocol === 'file:'
const basePath = isElectron() ? '/' : window.location.pathname

Expand Down Expand Up @@ -56,4 +57,41 @@ const router = createRouter({
}
})

// Global authentication guard
router.beforeEach(async (_to, _from, next) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

short term: isCloud
long term: extension for router hooks

const authStore = useFirebaseAuthStore()

// Wait for Firebase auth to initialize
if (!authStore.isInitialized) {
await new Promise<void>((resolve) => {
const unwatch = authStore.$subscribe((_, state) => {
if (state.isInitialized) {
unwatch()
resolve()
}
})
})
}

// Check if user is authenticated (Firebase or API key)
const authHeader = await authStore.getAuthHeader()

if (!authHeader) {
Copy link
Contributor Author

@arjansingh arjansingh Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (authHeader) { 
  next()
  return
}

// User is not authenticated, show sign-in dialog
const dialogService = useDialogService()
const loginSuccess = await dialogService.showSignInDialog()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

double check to see if this is part of the app already.


if (loginSuccess) {
// After successful login, proceed to the intended route
next()
} else {
// User cancelled login, stay on current page or redirect to home
next(false)
}
} else {
// User is authenticated, proceed
next()
}
})

export default router
85 changes: 77 additions & 8 deletions src/scripts/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ import type {
} from '@/schemas/apiSchema'
import type { ComfyNodeDef } from '@/schemas/nodeDefSchema'
import type { NodeExecutionId } from '@/types/nodeIdentification'
<<<<<<< HEAD
=======
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
import { WorkflowTemplates } from '@/types/workflowTemplateTypes'
>>>>>>> 23e881e22 (Prevent access without login.)

interface QueuePromptRequestBody {
client_id: string
Expand Down Expand Up @@ -317,7 +322,27 @@ export class ComfyApi extends EventTarget {
return this.api_base + route
}

fetchApi(route: string, options?: RequestInit) {
/**
* Waits for Firebase auth to be initialized before proceeding
*/
async #waitForAuthInitialization(): Promise<void> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private typescript

const authStore = useFirebaseAuthStore()

if (authStore.isInitialized) {
return
}

return new Promise<void>((resolve) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

await

const unwatch = authStore.$subscribe((_, state) => {
if (state.isInitialized) {
unwatch()
resolve()
}
})
})
}

async fetchApi(route: string, options?: RequestInit) {
if (!options) {
options = {}
}
Expand All @@ -328,6 +353,30 @@ export class ComfyApi extends EventTarget {
options.cache = 'no-cache'
}

// Wait for Firebase auth to be initialized before making any API request
await this.#waitForAuthInitialization()

// Add Firebase JWT token if user is logged in
try {
const authHeader = await useFirebaseAuthStore().getAuthHeader()
if (authHeader) {
if (Array.isArray(options.headers)) {
for (const [key, value] of Object.entries(authHeader)) {
options.headers.push([key, value])
}
} else if (options.headers instanceof Headers) {
for (const [key, value] of Object.entries(authHeader)) {
options.headers.set(key, value)
}
} else {
Object.assign(options.headers, authHeader)
}
}
} catch (error) {
// Silently ignore auth errors to avoid breaking API calls
console.warn('Failed to get auth header:', error)
}

if (Array.isArray(options.headers)) {
options.headers.push(['Comfy-User', this.user])
} else if (options.headers instanceof Headers) {
Expand Down Expand Up @@ -402,19 +451,39 @@ export class ComfyApi extends EventTarget {
* Creates and connects a WebSocket for realtime updates
* @param {boolean} isReconnect If the socket is connection is a reconnect attempt
*/
#createSocket(isReconnect?: boolean) {
async #createSocket(isReconnect?: boolean) {
if (this.socket) {
return
}

let opened = false
let existingSession = window.name

// Get auth token if available
let authToken: string | undefined
try {
authToken = await useFirebaseAuthStore().getIdToken()
} catch (error) {
// Continue without auth token if there's an error
console.warn('Could not get auth token for WebSocket connection:', error)
}

// Build WebSocket URL with query parameters
let wsUrl = `ws${window.location.protocol === 'https:' ? 's' : ''}://${this.api_host}${this.api_base}/ws`
const params = new URLSearchParams()

if (existingSession) {
existingSession = '?clientId=' + existingSession
params.set('clientId', existingSession)
}
this.socket = new WebSocket(
`ws${window.location.protocol === 'https:' ? 's' : ''}://${this.api_host}${this.api_base}/ws${existingSession}`
)
if (authToken) {
params.set('token', authToken)
}

if (params.toString()) {
wsUrl += '?' + params.toString()
}

this.socket = new WebSocket(wsUrl)
this.socket.binaryType = 'arraybuffer'

this.socket.addEventListener('open', () => {
Expand All @@ -441,9 +510,9 @@ export class ComfyApi extends EventTarget {
})

this.socket.addEventListener('close', () => {
setTimeout(() => {
setTimeout(async () => {
this.socket = null
this.#createSocket(true)
await this.#createSocket(true)
}, 300)
if (opened) {
this.dispatchCustomEvent('status', null)
Expand Down
5 changes: 5 additions & 0 deletions src/types/comfy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,14 @@ export interface ComfyExtension {
): Promise<void> | void

/**
<<<<<<< HEAD
* Fired whenever authentication resolves, providing the anonymized user id..
* Extensions can register at any time and will receive the latest value immediately.
* This is an experimental API and may be changed or removed in the future.
=======
* Fired whenever authentication resolves, providing the user id.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

take new

* Extensions can register at any time and will receive the latest value immediately.
>>>>>>> 775c856bf (port user ID expose hook from 6786d8e to cloud)
*/
onAuthUserResolved?(user: AuthUserInfo, app: ComfyApp): Promise<void> | void

Expand Down