Files
supabase/apps/studio/components/interfaces/APIKeys/useRevealedSecret.ts
Ali Waseem 920571fcf7 feat(studio): rewrite secret key reveal flow without react-query [FE-3206] (#45792)
## Summary

Rewrites the secret API key reveal flow in `ApiKeyPill` to remove its
dependency on React Query, replacing it with a lightweight custom hook.

## Changes

- **`useRevealedSecret` (new hook)**  
  A simple, reusable hook that:
  - Fetches the unmasked secret key via `getAPIKeysById`
  - Exposes `data`, `isLoading`, `reveal()`, and `clear()`
  - Keeps sensitive data in local component state (no global cache)

- **`ApiKeyPill` (refactored)**  
- Removes all React Query imports (`useQueryClient`, `useAPIKeyIdQuery`,
`apiKeysKeys`)
  - Uses `useRevealedSecret` for reveal / copy operations
  - Preserves existing UX:
    - 10-second auto-hide timer
    - Permission-based gating (`canManageSecretKeys`)
    - Loading states on toggle / copy

- **`api-key-id-query.ts` (cleaned up)**  
  - Removes the now-unused `useAPIKeyIdQuery` hook
  - Retains the `getAPIKeysById` fetcher for direct use

## Motivation

The previous React Query–based flow had to aggressively disable caching
(`staleTime: 0`, `gcTime: 0`) and manually purge queries from the cache
on every interaction, which was cumbersome and leaked implementation
details into the component. A plain fetch + local state is simpler and
safer for transient, sensitive data.

## Testing

- [x] Toggle reveal on a secret API key
- [x] Verify 10-second auto-hide
- [x] Copy a secret key (both revealed and unrevealed states)
- [x] Verify restricted users cannot reveal/copy

---

Resolves [FE-3206](https://linear.app/supabase/issue/FE-3206)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Improved API key reveal/copy flow: uses a dedicated reveal/clear
mechanism, preserves permission checks and 10s auto-hide, and shows
reveal/copy failures via user-facing toasts. Copy now falls back to
masked key when needed and the reveal toggle behavior is more reliable.

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45792)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-11 09:11:45 -06:00

44 lines
1.2 KiB
TypeScript

import { useCallback, useRef, useState } from 'react'
import { getAPIKeysById } from '@/data/api-keys/api-key-id-query'
interface UseRevealedSecretOptions {
projectRef?: string
id?: string
}
export function useRevealedSecret({ projectRef, id }: UseRevealedSecretOptions) {
const [data, setData] = useState<string | undefined | null>()
const [isLoading, setIsLoading] = useState(false)
const requestIdRef = useRef(0)
const reveal = useCallback(async () => {
if (!projectRef || !id) return
const requestId = ++requestIdRef.current
setIsLoading(true)
try {
const result = await getAPIKeysById({ projectRef, id, reveal: true })
if (requestId !== requestIdRef.current) return
setData(result.api_key)
return result.api_key
} catch (error) {
if (requestId !== requestIdRef.current) return
console.error('Failed to reveal secret key:', error)
throw error
} finally {
if (requestId === requestIdRef.current) {
setIsLoading(false)
}
}
}, [projectRef, id])
const clear = useCallback(() => {
requestIdRef.current++
setData(undefined)
}, [])
return { data, isLoading, reveal, clear }
}