diff --git a/apps/studio/components/interfaces/JwtSecrets/jwt-secret-keys-table/index.tsx b/apps/studio/components/interfaces/JwtSecrets/jwt-secret-keys-table/index.tsx index 026a25cba57..5b0389385c3 100644 --- a/apps/studio/components/interfaces/JwtSecrets/jwt-secret-keys-table/index.tsx +++ b/apps/studio/components/interfaces/JwtSecrets/jwt-secret-keys-table/index.tsx @@ -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(null) - const [shownDialog, setShownDialog] = useState< - 'legacy' | 'create' | 'rotate' | 'key-details' | 'revoke' | 'delete' | null - >(null) - - const resetDialog = () => { - setSelectedKey(null) - setShownDialog(null) - } - - const [newKeyAlgorithm, setNewKeyAlgorithm] = useState('RS256') + const [selectedKey, setSelectedKey] = useState() + const [shownDialog, setShownDialog] = useState() 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() { ) : ( setShownDialog('legacy')} - isLoading={isLoadingMutation} + isLoading={isMigrating} /> )} @@ -455,9 +396,8 @@ export default function JWTSecretKeysTable() { diff --git a/apps/studio/components/interfaces/JwtSecrets/jwt-secret-keys-table/signing-key-row.tsx b/apps/studio/components/interfaces/JwtSecrets/jwt-secret-keys-table/signing-key-row.tsx index 6e6075b0532..4459f71e1f2 100644 --- a/apps/studio/components/interfaces/JwtSecrets/jwt-secret-keys-table/signing-key-row.tsx +++ b/apps/studio/components/interfaces/JwtSecrets/jwt-secret-keys-table/signing-key-row.tsx @@ -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 diff --git a/apps/studio/components/interfaces/JwtSecrets/jwt-settings.tsx b/apps/studio/components/interfaces/JwtSecrets/jwt-settings.tsx index 8ef4fe5bc16..49c06672fc3 100644 --- a/apps/studio/components/interfaces/JwtSecrets/jwt-settings.tsx +++ b/apps/studio/components/interfaces/JwtSecrets/jwt-settings.tsx @@ -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' && ( - - 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{' '} - - {legacyKey.status === 'in_use' ? 'sign and verify' : 'only verify'} - {' '} - JSON Web Tokens by Supabase products.{' '} + +

+ Changing the legacy JWT secret can only be done by rotating to a standby + key and then revoking it. It is used to{' '} + + {legacyKey.status === 'in_use' ? 'sign and verify' : 'only verify'} + {' '} + JSON Web Tokens by Supabase products. +

+ {legacyAPIKeysStatus && legacyAPIKeysStatus.enabled && ( - <> +

This includes the anon and service_role JWT based API keys. {' '} Consider switching to publishable and secret API keys to disable them. - +

)} -
-
+