mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
fix: additional fixes for api keys, jwt signing keys (#37069)
* fix: additional fixes for api keys, jwt signing keys * Smol fix * fix issue with screen * Fix success handler for migrate JWT secret * Tiny * text fix --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
1 parent
1cc7124213
commit
e0a3ab2c74
7 files changed
+133
-141
No files matched your search
@@ -1,16 +1,7 @@
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import {
|
||||
CircleArrowUp,
|
||||
Eye,
|
||||
FileKey,
|
||||
Key,
|
||||
MoreVertical,
|
||||
RotateCw,
|
||||
ShieldOff,
|
||||
Timer,
|
||||
Trash2,
|
||||
} from 'lucide-react'
|
||||
import { AnimatePresence } from 'framer-motion'
|
||||
import { RotateCw, Timer } from 'lucide-react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
@@ -18,11 +9,7 @@ import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
|
||||
import { useLegacyAPIKeysStatusQuery } from 'data/api-keys/legacy-api-keys-status-query'
|
||||
import { useJWTSigningKeyDeleteMutation } from 'data/jwt-signing-keys/jwt-signing-key-delete-mutation'
|
||||
import { useJWTSigningKeyUpdateMutation } from 'data/jwt-signing-keys/jwt-signing-key-update-mutation'
|
||||
import {
|
||||
JWTAlgorithm,
|
||||
JWTSigningKey,
|
||||
useJWTSigningKeysQuery,
|
||||
} from 'data/jwt-signing-keys/jwt-signing-keys-query'
|
||||
import { JWTSigningKey, useJWTSigningKeysQuery } from 'data/jwt-signing-keys/jwt-signing-keys-query'
|
||||
import { useLegacyJWTSigningKeyCreateMutation } from 'data/jwt-signing-keys/legacy-jwt-signing-key-create-mutation'
|
||||
import { useLegacyJWTSigningKeyQuery } from 'data/jwt-signing-keys/legacy-jwt-signing-key-query'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
@@ -34,11 +21,9 @@ import {
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
cn,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
@@ -46,29 +31,22 @@ import {
|
||||
DialogSection,
|
||||
DialogSectionSeparator,
|
||||
DialogTitle,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from 'ui'
|
||||
import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal'
|
||||
import { AlgorithmHoverCard } from '../algorithm-hover-card'
|
||||
import { statusColors, statusLabels } from '../jwt.constants'
|
||||
import { SigningKeysComingSoonBanner } from '../signing-keys-coming-soon'
|
||||
import { StartUsingJwtSigningKeysBanner } from '../start-using-keys-banner'
|
||||
import { ActionPanel } from './action-panel'
|
||||
import { CreateKeyDialog } from './create-key-dialog'
|
||||
import { RotateKeyDialog } from './rotate-key-dialog'
|
||||
import { KeyDetailsDialog } from './key-details-dialog'
|
||||
import { RotateKeyDialog } from './rotate-key-dialog'
|
||||
import { SigningKeyRow } from './signing-key-row'
|
||||
|
||||
const MotionTableRow = motion(TableRow)
|
||||
type DialogType = 'legacy' | 'create' | 'rotate' | 'key-details' | 'revoke' | 'delete'
|
||||
|
||||
export default function JWTSecretKeysTable() {
|
||||
const { ref: projectRef } = useParams()
|
||||
@@ -76,17 +54,8 @@ export default function JWTSecretKeysTable() {
|
||||
|
||||
const newJwtSecrets = useFlag('newJwtSecrets')
|
||||
|
||||
const [selectedKey, setSelectedKey] = useState<JWTSigningKey | null>(null)
|
||||
const [shownDialog, setShownDialog] = useState<
|
||||
'legacy' | 'create' | 'rotate' | 'key-details' | 'revoke' | 'delete' | null
|
||||
>(null)
|
||||
|
||||
const resetDialog = () => {
|
||||
setSelectedKey(null)
|
||||
setShownDialog(null)
|
||||
}
|
||||
|
||||
const [newKeyAlgorithm, setNewKeyAlgorithm] = useState<JWTAlgorithm>('RS256')
|
||||
const [selectedKey, setSelectedKey] = useState<JWTSigningKey>()
|
||||
const [shownDialog, setShownDialog] = useState<DialogType>()
|
||||
|
||||
const { data: signingKeys, isLoading: isLoadingSigningKeys } = useJWTSigningKeysQuery({
|
||||
projectRef,
|
||||
@@ -97,13 +66,21 @@ export default function JWTSecretKeysTable() {
|
||||
const { data: legacyAPIKeysStatus, isLoading: isLoadingLegacyAPIKeysStatus } =
|
||||
useLegacyAPIKeysStatusQuery({ projectRef })
|
||||
|
||||
const legacyMutation = useLegacyJWTSigningKeyCreateMutation()
|
||||
const { mutate: migrateJWTSecret, isLoading: isMigrating } = useLegacyJWTSigningKeyCreateMutation(
|
||||
{
|
||||
onSuccess: () => {
|
||||
setShownDialog(undefined)
|
||||
toast.success('Successfully migrated JWT secret!')
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const updateMutation = useJWTSigningKeyUpdateMutation()
|
||||
const deleteMutation = useJWTSigningKeyDeleteMutation()
|
||||
const { mutate: updateJWTSigningKey, isLoading: isUpdatingJWTSigningKey } =
|
||||
useJWTSigningKeyUpdateMutation({ onSuccess: () => resetDialog() })
|
||||
const { mutate: deleteJWTSigningKey, isLoading: isDeletingJWTSigningKey } =
|
||||
useJWTSigningKeyDeleteMutation({ onSuccess: () => resetDialog(), onError: () => resetDialog() })
|
||||
|
||||
const isLoadingMutation =
|
||||
updateMutation.isLoading || deleteMutation.isLoading || legacyMutation.isLoading
|
||||
const isLoadingMutation = isUpdatingJWTSigningKey || isDeletingJWTSigningKey || isMigrating
|
||||
const isLoading =
|
||||
isProjectLoading || isLoadingSigningKeys || isLoadingLegacyKey || isLoadingLegacyAPIKeysStatus
|
||||
|
||||
@@ -135,61 +112,25 @@ export default function JWTSecretKeysTable() {
|
||||
[sortedKeys]
|
||||
)
|
||||
|
||||
const handleLegacyMigration = async () => {
|
||||
try {
|
||||
await legacyMutation.mutateAsync({
|
||||
projectRef: projectRef!,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Failed to migrate legacy JWT secret to new JWT signing keys', error)
|
||||
}
|
||||
const resetDialog = () => {
|
||||
setSelectedKey(undefined)
|
||||
setShownDialog(undefined)
|
||||
}
|
||||
|
||||
const handlePreviouslyUsedKey = async (keyId: string) => {
|
||||
updateMutation.mutate(
|
||||
{ projectRef, keyId, status: 'previously_used' },
|
||||
{
|
||||
onSuccess: () => {
|
||||
resetDialog()
|
||||
},
|
||||
}
|
||||
)
|
||||
updateJWTSigningKey({ projectRef, keyId, status: 'previously_used' })
|
||||
}
|
||||
|
||||
const handleStandbyKey = (keyId: string) => {
|
||||
updateMutation.mutate(
|
||||
{ projectRef: projectRef!, keyId, status: 'standby' },
|
||||
{
|
||||
onSuccess: () => {
|
||||
resetDialog()
|
||||
},
|
||||
}
|
||||
)
|
||||
updateJWTSigningKey({ projectRef: projectRef!, keyId, status: 'standby' })
|
||||
}
|
||||
|
||||
const handleRevokeKey = (keyId: string) => {
|
||||
updateMutation.mutate(
|
||||
{ projectRef: projectRef!, keyId, status: 'revoked' },
|
||||
{
|
||||
onSuccess: () => {
|
||||
resetDialog()
|
||||
},
|
||||
}
|
||||
)
|
||||
updateJWTSigningKey({ projectRef: projectRef!, keyId, status: 'revoked' })
|
||||
}
|
||||
|
||||
const handleDeleteKey = (keyId: string) => {
|
||||
deleteMutation.mutate(
|
||||
{ projectRef: projectRef!, keyId },
|
||||
{
|
||||
onSuccess: () => {
|
||||
resetDialog()
|
||||
},
|
||||
onError: () => {
|
||||
resetDialog()
|
||||
},
|
||||
}
|
||||
)
|
||||
deleteJWTSigningKey({ projectRef: projectRef!, keyId })
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
@@ -232,7 +173,7 @@ export default function JWTSecretKeysTable() {
|
||||
) : (
|
||||
<StartUsingJwtSigningKeysBanner
|
||||
onClick={() => setShownDialog('legacy')}
|
||||
isLoading={isLoadingMutation}
|
||||
isLoading={isMigrating}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -455,9 +396,8 @@ export default function JWTSecretKeysTable() {
|
||||
</DialogSection>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
onClick={() => handleLegacyMigration()}
|
||||
disabled={isLoadingMutation}
|
||||
loading={isLoadingMutation}
|
||||
loading={isMigrating}
|
||||
onClick={() => migrateJWTSecret({ projectRef: projectRef! })}
|
||||
>
|
||||
Migrate JWT secret
|
||||
</Button>
|
||||
|
||||
+3
-3
@@ -2,12 +2,12 @@ import { motion } from 'framer-motion'
|
||||
import {
|
||||
CircleArrowDown,
|
||||
CircleArrowUp,
|
||||
Trash2,
|
||||
Eye,
|
||||
Key,
|
||||
MoreVertical,
|
||||
ShieldOff,
|
||||
Timer,
|
||||
Trash2,
|
||||
} from 'lucide-react'
|
||||
|
||||
import { components } from 'api-types'
|
||||
@@ -28,8 +28,8 @@ import { statusColors, statusLabels } from '../jwt.constants'
|
||||
|
||||
interface SigningKeyRowProps {
|
||||
signingKey: components['schemas']['SigningKeyResponse']
|
||||
setSelectedKey: (key: JWTSigningKey | null) => void
|
||||
setShownDialog: (dialog: 'key-details' | 'revoke' | 'delete' | null) => void
|
||||
setSelectedKey: (key?: JWTSigningKey) => void
|
||||
setShownDialog: (dialog?: 'key-details' | 'revoke' | 'delete') => void
|
||||
handlePreviouslyUsedKey: (keyId: string) => void
|
||||
handleStandbyKey: (keyId: string) => void
|
||||
legacyKey?: JWTSigningKey | null
|
||||
|
||||
@@ -4,43 +4,40 @@ import {
|
||||
JwtSecretUpdateProgress,
|
||||
JwtSecretUpdateStatus,
|
||||
} from '@supabase/shared-types/out/events'
|
||||
import {
|
||||
AlertCircle,
|
||||
ChevronDown,
|
||||
Eye,
|
||||
EyeOff,
|
||||
Hourglass,
|
||||
Key,
|
||||
CloudOff,
|
||||
Loader2,
|
||||
PenTool,
|
||||
Power,
|
||||
RefreshCw,
|
||||
TriangleAlert,
|
||||
ExternalLink,
|
||||
Lightbulb,
|
||||
} from 'lucide-react'
|
||||
import { Dispatch, SetStateAction, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import { number, object } from 'yup'
|
||||
import Link from 'next/link'
|
||||
import { useParams } from 'common'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { FormActions } from 'components/ui/Forms/FormActions'
|
||||
import Panel from 'components/ui/Panel'
|
||||
import { useLegacyAPIKeysStatusQuery } from 'data/api-keys/legacy-api-keys-status-query'
|
||||
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
|
||||
import { useAuthConfigUpdateMutation } from 'data/auth/auth-config-update-mutation'
|
||||
import { useJwtSecretUpdateMutation } from 'data/config/jwt-secret-update-mutation'
|
||||
import { useJwtSecretUpdatingStatusQuery } from 'data/config/jwt-secret-updating-status-query'
|
||||
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
|
||||
import { useLegacyJWTSigningKeyQuery } from 'data/jwt-signing-keys/legacy-jwt-signing-key-query'
|
||||
import { useLegacyAPIKeysStatusQuery } from 'data/api-keys/legacy-api-keys-status-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
import { uuidv4 } from 'lib/helpers'
|
||||
import {
|
||||
AlertDescription_Shadcn_,
|
||||
AlertTitle_Shadcn_,
|
||||
Alert_Shadcn_,
|
||||
AlertCircle,
|
||||
ChevronDown,
|
||||
CloudOff,
|
||||
ExternalLink,
|
||||
Eye,
|
||||
EyeOff,
|
||||
Hourglass,
|
||||
Key,
|
||||
Lightbulb,
|
||||
Loader2,
|
||||
PenTool,
|
||||
Power,
|
||||
RefreshCw,
|
||||
TriangleAlert,
|
||||
} from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { Dispatch, SetStateAction, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import {
|
||||
Button,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -51,12 +48,10 @@ import {
|
||||
Input,
|
||||
InputNumber,
|
||||
Modal,
|
||||
WarningIcon,
|
||||
} from 'ui'
|
||||
import { Admonition } from 'ui-patterns/admonition'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
import { number, object } from 'yup'
|
||||
import {
|
||||
JWT_SECRET_UPDATE_ERROR_MESSAGES,
|
||||
JWT_SECRET_UPDATE_PROGRESS_MESSAGES,
|
||||
@@ -193,25 +188,29 @@ const JWTSettings = () => {
|
||||
) : (
|
||||
<>
|
||||
{legacyKey && legacyKey.status !== 'revoked' && (
|
||||
<Admonition type="warning">
|
||||
You've successfully migrated your legacy JWT secret to the new JWT Signing
|
||||
Keys feature. Changing the legacy JWT secret now can only be done by
|
||||
rotating to a standby key and finally revoking it. Now used to{' '}
|
||||
<em className="text-brand not-italic">
|
||||
{legacyKey.status === 'in_use' ? 'sign and verify' : 'only verify'}
|
||||
</em>{' '}
|
||||
JSON Web Tokens by Supabase products.{' '}
|
||||
<Admonition
|
||||
type="warning"
|
||||
title="Legacy JWT secret has been migrated to new JWT Signing Keys"
|
||||
>
|
||||
<p className="!leading-normal">
|
||||
Changing the legacy JWT secret can only be done by rotating to a standby
|
||||
key and then revoking it. It is used to{' '}
|
||||
<em className="text-foreground not-italic">
|
||||
{legacyKey.status === 'in_use' ? 'sign and verify' : 'only verify'}
|
||||
</em>{' '}
|
||||
JSON Web Tokens by Supabase products.
|
||||
</p>
|
||||
|
||||
{legacyAPIKeysStatus && legacyAPIKeysStatus.enabled && (
|
||||
<>
|
||||
<p className="!leading-normal">
|
||||
<em className="text-warning not-italic">
|
||||
This includes the <code>anon</code> and <code>service_role</code> JWT
|
||||
based API keys.
|
||||
</em>{' '}
|
||||
Consider switching to publishable and secret API keys to disable them.
|
||||
</>
|
||||
</p>
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<Button type="default" asChild icon={<ExternalLink className="size-4" />}>
|
||||
<Link href={`/project/${projectRef}/settings/api-keys`}>
|
||||
Go to API keys
|
||||
@@ -220,10 +219,11 @@ const JWTSettings = () => {
|
||||
</Admonition>
|
||||
)}
|
||||
{legacyKey && legacyKey.status === 'revoked' && (
|
||||
<Admonition type="note">
|
||||
Your project has revoked the legacy JWT secret. No new JSON Web Tokens are
|
||||
issued nor verified with it by Supabase products.
|
||||
</Admonition>
|
||||
<Admonition
|
||||
type="note"
|
||||
title="Your project has revoked the legacy JWT secret"
|
||||
description="No new JSON Web Tokens are issued nor verified with it by Supabase products."
|
||||
/>
|
||||
)}
|
||||
<Input
|
||||
label={
|
||||
|
||||
@@ -9,12 +9,12 @@ const ApiKeysLayout = ({ children }: PropsWithChildren) => {
|
||||
|
||||
const navigationItems = [
|
||||
{
|
||||
label: 'API Keys',
|
||||
label: 'Legacy API Keys',
|
||||
href: `/project/${projectRef}/settings/api-keys`,
|
||||
id: 'legacy-keys',
|
||||
},
|
||||
{
|
||||
label: 'New API Keys (Coming Soon)',
|
||||
label: 'API Keys',
|
||||
href: `/project/${projectRef}/settings/api-keys/new`,
|
||||
id: 'new-keys',
|
||||
},
|
||||
|
||||
@@ -7,13 +7,28 @@ import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { useToggleLegacyAPIKeysMutation } from 'data/api-keys/legacy-api-key-toggle-mutation'
|
||||
import { useLegacyAPIKeysStatusQuery } from 'data/api-keys/legacy-api-keys-status-query'
|
||||
import { useLegacyJWTSigningKeyQuery } from 'data/jwt-signing-keys/legacy-jwt-signing-key-query'
|
||||
import { useAuthorizedAppsQuery } from 'data/oauth/authorized-apps-query'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from 'ui'
|
||||
import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal'
|
||||
import Panel from '../Panel'
|
||||
|
||||
export const ToggleLegacyApiKeysPanel = () => {
|
||||
const { ref: projectRef } = useParams()
|
||||
const org = useSelectedOrganization()
|
||||
|
||||
const [isConfirmOpen, setIsConfirmOpen] = useState(false)
|
||||
const [isAppsWarningOpen, setIsAppsWarningOpen] = useState(false)
|
||||
|
||||
const { data: legacyAPIKeysStatusData, isSuccess: isLegacyAPIKeysStatusSuccess } =
|
||||
useLegacyAPIKeysStatusQuery({ projectRef })
|
||||
@@ -23,9 +38,13 @@ export const ToggleLegacyApiKeysPanel = () => {
|
||||
const { can: canUpdateAPIKeys, isSuccess: isPermissionsSuccess } =
|
||||
useAsyncCheckProjectPermissions(PermissionAction.SECRETS_WRITE, '*')
|
||||
|
||||
const { data: authorizedApps = [], isSuccess: isAuthorizedAppsSuccess } = useAuthorizedAppsQuery({
|
||||
slug: org?.slug,
|
||||
})
|
||||
|
||||
const { enabled: isLegacyKeysEnabled } = legacyAPIKeysStatusData || {}
|
||||
|
||||
if (!(isLegacyAPIKeysStatusSuccess && isPermissionsSuccess)) {
|
||||
if (!(isLegacyAPIKeysStatusSuccess && isPermissionsSuccess && isAuthorizedAppsSuccess)) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -47,7 +66,11 @@ export const ToggleLegacyApiKeysPanel = () => {
|
||||
<div className="flex items-center">
|
||||
<ButtonTooltip
|
||||
type="default"
|
||||
onClick={() => setIsConfirmOpen(true)}
|
||||
onClick={
|
||||
authorizedApps?.length
|
||||
? () => setIsAppsWarningOpen(true)
|
||||
: () => setIsConfirmOpen(true)
|
||||
}
|
||||
disabled={
|
||||
!canUpdateAPIKeys ||
|
||||
(!isLegacyKeysEnabled && legacyJWTSecret?.status === 'revoked')
|
||||
@@ -77,6 +100,25 @@ export const ToggleLegacyApiKeysPanel = () => {
|
||||
onClose={() => setIsConfirmOpen(false)}
|
||||
legacyAPIKeysStatusData={legacyAPIKeysStatusData}
|
||||
/>
|
||||
|
||||
<AlertDialog open={isAppsWarningOpen} onOpenChange={(value) => setIsAppsWarningOpen(value)}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Apps using Supabase may break</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Your project uses apps that integrate with Supabase. Disabling the legacy API keys is
|
||||
a brand new feature and the apps you're using may not have added support for this yet.
|
||||
It can cause them to stop functioning. Check before continuing.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={() => setIsConfirmOpen(true)}>
|
||||
Proceed to disable API keys
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -136,6 +178,12 @@ const ToggleApiKeysModal = ({
|
||||
use will cause downtime for your application. Ensure they are no longer in use
|
||||
before proceeding. If you have not created a publishable and at least one secret
|
||||
API key, some dashboard functionality may become unavailable.
|
||||
<br />
|
||||
<br />
|
||||
<span className="text-danger">
|
||||
This disables API keys when used in the <code>apikey</code> header. They remain
|
||||
valid as a JWT.
|
||||
</span>
|
||||
</span>
|
||||
),
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ export const useLegacyJWTSigningKeyCreateMutation = ({
|
||||
const { projectRef } = variables
|
||||
|
||||
await queryClient.invalidateQueries(jwtSigningKeysKeys.legacy(projectRef))
|
||||
// invalidate the keys as well since migration creates 2 new keys and the UI needs to be refreshed
|
||||
await queryClient.invalidateQueries(jwtSigningKeysKeys.list(projectRef))
|
||||
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
|
||||
@@ -37,6 +37,8 @@ export const useLegacyJWTSigningKeyQuery = <TData = LegacyJWTSigningKeyData>(
|
||||
({ signal }) => getLegacyJWTSigningKey({ projectRef }, signal),
|
||||
{
|
||||
enabled: enabled && !!projectRef,
|
||||
retry: false,
|
||||
refetchOnWindowFocus: false,
|
||||
...options,
|
||||
}
|
||||
)
|
||||
Reference in new issue
Block a user