mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-26 17:54:14 +00:00
fix: use getAuthHeader for API key auth in subscription/billing (#9142)
## Summary Fix "User not authenticated" errors when API key users (desktop/portable) trigger subscription status checks or billing operations. ## Changes - **What**: Replace `getFirebaseAuthHeader()` with `getAuthHeader()` in subscription and billing call sites (`fetchSubscriptionStatus`, `initiateSubscriptionCheckout`, `fetchBalance`, `addCredits`, `accessBillingPortal`, `performSubscriptionCheckout`). `getAuthHeader()` supports the full auth fallback chain (workspace token → Firebase token → API key), whereas `getFirebaseAuthHeader()` returns null for API key users since they bypass Firebase entirely. Also add an `isCloud` guard to the subscription status watcher so non-cloud environments skip subscription checks. ## Review Focus - The `isCloud` guard on the watcher ensures local/desktop users never hit the subscription endpoint. This was the originally intended design per code owner confirmation. - `getAuthHeader()` already exists in `firebaseAuthStore` with proper fallback logic — no new auth code was added. Fixes https://www.notion.so/comfy-org/Bug-Subscription-status-check-occurring-in-non-cloud-environments-causing-authentication-errors-3116d73d365081738b21db157e88a9ed ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9142-fix-use-getAuthHeader-for-API-key-auth-in-subscription-billing-3116d73d3650817fa345deaddc8c3fcd) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -16,7 +16,7 @@ const mockAccessBillingPortal = vi.fn()
|
||||
const mockReportError = vi.fn()
|
||||
const mockTrackBeginCheckout = vi.fn()
|
||||
const mockUserId = ref<string | undefined>('user-123')
|
||||
const mockGetFirebaseAuthHeader = vi.fn(() =>
|
||||
const mockGetAuthHeader = vi.fn(() =>
|
||||
Promise.resolve({ Authorization: 'Bearer test-token' })
|
||||
)
|
||||
const mockGetCheckoutAttribution = vi.hoisted(() => vi.fn(() => ({})))
|
||||
@@ -58,7 +58,7 @@ vi.mock('@/composables/useErrorHandling', () => ({
|
||||
vi.mock('@/stores/firebaseAuthStore', () => ({
|
||||
useFirebaseAuthStore: () =>
|
||||
reactive({
|
||||
getFirebaseAuthHeader: mockGetFirebaseAuthHeader,
|
||||
getAuthHeader: mockGetAuthHeader,
|
||||
userId: computed(() => mockUserId.value)
|
||||
}),
|
||||
FirebaseAuthStoreError: class extends Error {}
|
||||
|
||||
@@ -108,7 +108,7 @@ vi.mock('@/services/dialogService', () => ({
|
||||
|
||||
vi.mock('@/stores/firebaseAuthStore', () => ({
|
||||
useFirebaseAuthStore: vi.fn(() => ({
|
||||
getFirebaseAuthHeader: mockGetAuthHeader,
|
||||
getAuthHeader: mockGetAuthHeader,
|
||||
get userId() {
|
||||
return mockUserId.value
|
||||
}
|
||||
@@ -363,6 +363,27 @@ describe('useSubscription', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('non-cloud environments', () => {
|
||||
it('should not fetch subscription status when not on cloud', async () => {
|
||||
mockIsCloud.value = false
|
||||
mockIsLoggedIn.value = true
|
||||
|
||||
useSubscriptionWithScope()
|
||||
|
||||
await vi.dynamicImportSettled()
|
||||
|
||||
expect(global.fetch).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should report isActiveSubscription as true when not on cloud', () => {
|
||||
mockIsCloud.value = false
|
||||
|
||||
const { isActiveSubscription } = useSubscriptionWithScope()
|
||||
|
||||
expect(isActiveSubscription.value).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('action handlers', () => {
|
||||
it('should open usage history URL', () => {
|
||||
const windowOpenSpy = vi
|
||||
|
||||
@@ -40,7 +40,7 @@ function useSubscriptionInternal() {
|
||||
const { showSubscriptionRequiredDialog } = useDialogService()
|
||||
|
||||
const firebaseAuthStore = useFirebaseAuthStore()
|
||||
const { getFirebaseAuthHeader } = firebaseAuthStore
|
||||
const { getAuthHeader } = firebaseAuthStore
|
||||
const { wrapWithErrorHandlingAsync } = useErrorHandling()
|
||||
|
||||
const { isLoggedIn } = useCurrentUser()
|
||||
@@ -184,7 +184,7 @@ function useSubscriptionInternal() {
|
||||
* @returns Subscription status or null if no subscription exists
|
||||
*/
|
||||
async function fetchSubscriptionStatus(): Promise<CloudSubscriptionStatusResponse | null> {
|
||||
const authHeader = await getFirebaseAuthHeader()
|
||||
const authHeader = await getAuthHeader()
|
||||
if (!authHeader) {
|
||||
throw new FirebaseAuthStoreError(t('toastMessages.userNotAuthenticated'))
|
||||
}
|
||||
@@ -217,7 +217,7 @@ function useSubscriptionInternal() {
|
||||
watch(
|
||||
() => isLoggedIn.value,
|
||||
async (loggedIn) => {
|
||||
if (loggedIn) {
|
||||
if (loggedIn && isCloud) {
|
||||
try {
|
||||
await fetchSubscriptionStatus()
|
||||
} catch (error) {
|
||||
@@ -238,7 +238,7 @@ function useSubscriptionInternal() {
|
||||
|
||||
const initiateSubscriptionCheckout =
|
||||
async (): Promise<CloudSubscriptionCheckoutResponse> => {
|
||||
const authHeader = await getFirebaseAuthHeader()
|
||||
const authHeader = await getAuthHeader()
|
||||
if (!authHeader) {
|
||||
throw new FirebaseAuthStoreError(
|
||||
t('toastMessages.userNotAuthenticated')
|
||||
|
||||
@@ -39,7 +39,7 @@ vi.mock('@/platform/telemetry', () => ({
|
||||
vi.mock('@/stores/firebaseAuthStore', () => ({
|
||||
useFirebaseAuthStore: vi.fn(() =>
|
||||
reactive({
|
||||
getFirebaseAuthHeader: mockGetAuthHeader,
|
||||
getAuthHeader: mockGetAuthHeader,
|
||||
userId: computed(() => mockUserId.value)
|
||||
})
|
||||
),
|
||||
|
||||
@@ -54,7 +54,7 @@ export async function performSubscriptionCheckout(
|
||||
const firebaseAuthStore = useFirebaseAuthStore()
|
||||
const { userId } = storeToRefs(firebaseAuthStore)
|
||||
const telemetry = useTelemetry()
|
||||
const authHeader = await firebaseAuthStore.getFirebaseAuthHeader()
|
||||
const authHeader = await firebaseAuthStore.getAuthHeader()
|
||||
|
||||
if (!authHeader) {
|
||||
throw new FirebaseAuthStoreError(t('toastMessages.userNotAuthenticated'))
|
||||
|
||||
Reference in New Issue
Block a user