mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-24 08:19:51 +00:00
[auth] handle auth/requires-recent-login for account deletion and password updates (#6109)
## Summary Implemented error recovery system to handle Firebase `auth/requires-recent-login` errors when deleting accounts or updating passwords. https://github.com/user-attachments/assets/92a79e2a-cff5-4b18-b529-dbaf5f2303f2 ## Changes - **What**: Added [ErrorRecoveryStrategy pattern](https://firebase.google.com/docs/auth/web/manage-users#re-authenticate_a_user) to `useErrorHandling` composable with automatic retry logic for sensitive Firebase operations - **Breaking**: None - recovery strategies are optional, all existing code unchanged ## Technical Details Firebase enforces [reauthentication](https://firebase.google.com/docs/reference/js/auth#autherrorcodes) for security-sensitive operations (account deletion, password changes) after ~5 minutes of inactivity. Previously these operations failed with cryptic error messages. New flow: 1. Operation throws `auth/requires-recent-login` 2. Recovery strategy shows confirmation dialog 3. User logs out and re-authenticates 4. Operation automatically retries ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6109-auth-handle-auth-requires-recent-login-for-account-deletion-and-password-updates-28f6d73d36508119abf4ce30eecea976) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -1,6 +1,54 @@
|
||||
import { t } from '@/i18n'
|
||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||
|
||||
/**
|
||||
* Strategy for recovering from specific error conditions.
|
||||
* Allows operations to be retried after resolving the error condition.
|
||||
*
|
||||
* @template TArgs - The argument types of the operation to be retried
|
||||
* @template TReturn - The return type of the operation
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const networkRecovery: ErrorRecoveryStrategy = {
|
||||
* shouldHandle: (error) => error instanceof NetworkError,
|
||||
* recover: async (error, retry) => {
|
||||
* await waitForNetwork()
|
||||
* await retry()
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export interface ErrorRecoveryStrategy<
|
||||
TArgs extends unknown[] = unknown[],
|
||||
TReturn = unknown
|
||||
> {
|
||||
/**
|
||||
* Determines if this strategy should handle the given error.
|
||||
* @param error - The error to check
|
||||
* @returns true if this strategy can handle the error
|
||||
*/
|
||||
shouldHandle: (error: unknown) => boolean
|
||||
|
||||
/**
|
||||
* Attempts to recover from the error and retry the operation.
|
||||
* This function is responsible for:
|
||||
* 1. Resolving the error condition (e.g., reauthentication, network reconnect)
|
||||
* 2. Calling retry() to re-execute the original operation
|
||||
* 3. Handling the retry result (success or failure)
|
||||
*
|
||||
* @param error - The error that occurred
|
||||
* @param retry - Function to retry the original operation
|
||||
* @param args - Original arguments passed to the operation
|
||||
* @returns Promise that resolves when recovery completes (whether successful or not)
|
||||
*/
|
||||
recover: (
|
||||
error: unknown,
|
||||
retry: (...args: TArgs) => Promise<TReturn> | TReturn,
|
||||
args: TArgs
|
||||
) => Promise<void>
|
||||
}
|
||||
|
||||
export function useErrorHandling() {
|
||||
const toast = useToastStore()
|
||||
const toastErrorHandler = (error: unknown) => {
|
||||
@@ -13,9 +61,9 @@ export function useErrorHandling() {
|
||||
}
|
||||
|
||||
const wrapWithErrorHandling =
|
||||
<TArgs extends any[], TReturn>(
|
||||
<TArgs extends unknown[], TReturn>(
|
||||
action: (...args: TArgs) => TReturn,
|
||||
errorHandler?: (error: any) => void,
|
||||
errorHandler?: (error: unknown) => void,
|
||||
finallyHandler?: () => void
|
||||
) =>
|
||||
(...args: TArgs): TReturn | undefined => {
|
||||
@@ -29,15 +77,27 @@ export function useErrorHandling() {
|
||||
}
|
||||
|
||||
const wrapWithErrorHandlingAsync =
|
||||
<TArgs extends any[], TReturn>(
|
||||
<TArgs extends unknown[], TReturn>(
|
||||
action: (...args: TArgs) => Promise<TReturn> | TReturn,
|
||||
errorHandler?: (error: any) => void,
|
||||
finallyHandler?: () => void
|
||||
errorHandler?: (error: unknown) => void,
|
||||
finallyHandler?: () => void,
|
||||
recoveryStrategies: ErrorRecoveryStrategy<TArgs, TReturn>[] = []
|
||||
) =>
|
||||
async (...args: TArgs): Promise<TReturn | undefined> => {
|
||||
try {
|
||||
return await action(...args)
|
||||
} catch (e) {
|
||||
for (const strategy of recoveryStrategies) {
|
||||
if (strategy.shouldHandle(e)) {
|
||||
try {
|
||||
await strategy.recover(e, action, args)
|
||||
return
|
||||
} catch (recoveryError) {
|
||||
console.error('Recovery strategy failed:', recoveryError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
;(errorHandler ?? toastErrorHandler)(e)
|
||||
} finally {
|
||||
finallyHandler?.()
|
||||
|
||||
Reference in New Issue
Block a user