mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
Improve loading and success handlers for JWT keys (#37109)
This commit is contained in:
1 parent
57edade445
commit
f93058ca73
2 files changed
+66
-22
No files matched your search
@@ -55,6 +55,7 @@ export default function JWTSecretKeysTable() {
|
||||
const newJwtSecrets = useFlag('newJwtSecrets')
|
||||
|
||||
const [selectedKey, setSelectedKey] = useState<JWTSigningKey>()
|
||||
const [selectedKeyToUpdate, setSelectedKeyToUpdate] = useState<string>()
|
||||
const [shownDialog, setShownDialog] = useState<DialogType>()
|
||||
|
||||
const { data: signingKeys, isLoading: isLoadingSigningKeys } = useJWTSigningKeysQuery({
|
||||
@@ -76,7 +77,12 @@ export default function JWTSecretKeysTable() {
|
||||
)
|
||||
|
||||
const { mutate: updateJWTSigningKey, isLoading: isUpdatingJWTSigningKey } =
|
||||
useJWTSigningKeyUpdateMutation({ onSuccess: () => resetDialog() })
|
||||
useJWTSigningKeyUpdateMutation({
|
||||
onSuccess: () => {
|
||||
resetDialog()
|
||||
setSelectedKeyToUpdate(undefined)
|
||||
},
|
||||
})
|
||||
const { mutate: deleteJWTSigningKey, isLoading: isDeletingJWTSigningKey } =
|
||||
useJWTSigningKeyDeleteMutation({ onSuccess: () => resetDialog(), onError: () => resetDialog() })
|
||||
|
||||
@@ -118,19 +124,33 @@ export default function JWTSecretKeysTable() {
|
||||
}
|
||||
|
||||
const handlePreviouslyUsedKey = async (keyId: string) => {
|
||||
updateJWTSigningKey({ projectRef, keyId, status: 'previously_used' })
|
||||
setSelectedKeyToUpdate(keyId)
|
||||
updateJWTSigningKey(
|
||||
{ projectRef, keyId, status: 'previously_used' },
|
||||
{ onSuccess: () => toast.success('Successfully moved key to previously used') }
|
||||
)
|
||||
}
|
||||
|
||||
const handleStandbyKey = (keyId: string) => {
|
||||
updateJWTSigningKey({ projectRef: projectRef!, keyId, status: 'standby' })
|
||||
setSelectedKeyToUpdate(keyId)
|
||||
updateJWTSigningKey(
|
||||
{ projectRef: projectRef!, keyId, status: 'standby' },
|
||||
{ onSuccess: () => toast.success('Successfully moved key to standby') }
|
||||
)
|
||||
}
|
||||
|
||||
const handleRevokeKey = (keyId: string) => {
|
||||
updateJWTSigningKey({ projectRef: projectRef!, keyId, status: 'revoked' })
|
||||
updateJWTSigningKey(
|
||||
{ projectRef: projectRef!, keyId, status: 'revoked' },
|
||||
{ onSuccess: () => toast.success('Successfully revoked key') }
|
||||
)
|
||||
}
|
||||
|
||||
const handleDeleteKey = (keyId: string) => {
|
||||
deleteJWTSigningKey({ projectRef: projectRef!, keyId })
|
||||
deleteJWTSigningKey(
|
||||
{ projectRef: projectRef!, keyId },
|
||||
{ onSuccess: () => toast.success('Successfully deleted key') }
|
||||
)
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
@@ -152,7 +172,7 @@ export default function JWTSecretKeysTable() {
|
||||
description="Switch the standby key to in use. All new JSON Web Tokens issued by Supabase Auth will be signed with this key."
|
||||
buttonLabel="Rotate keys"
|
||||
onClick={() => setShownDialog('rotate')}
|
||||
loading={isLoadingMutation}
|
||||
loading={isUpdatingJWTSigningKey}
|
||||
icon={<RotateCw className="size-4" />}
|
||||
type="primary"
|
||||
/>
|
||||
@@ -206,12 +226,15 @@ export default function JWTSecretKeysTable() {
|
||||
<SigningKeyRow
|
||||
key={standbyKey.id}
|
||||
signingKey={standbyKey}
|
||||
legacyKey={legacyKey}
|
||||
standbyKey={standbyKey}
|
||||
isLoading={
|
||||
selectedKeyToUpdate === standbyKey.id && isUpdatingJWTSigningKey
|
||||
}
|
||||
setSelectedKey={setSelectedKey}
|
||||
setShownDialog={setShownDialog}
|
||||
handleStandbyKey={handleStandbyKey}
|
||||
handlePreviouslyUsedKey={handlePreviouslyUsedKey}
|
||||
legacyKey={legacyKey}
|
||||
standbyKey={standbyKey}
|
||||
/>
|
||||
)}
|
||||
{inUseKey && (
|
||||
@@ -271,12 +294,13 @@ export default function JWTSecretKeysTable() {
|
||||
<SigningKeyRow
|
||||
key={key.id}
|
||||
signingKey={key}
|
||||
legacyKey={legacyKey}
|
||||
standbyKey={standbyKey}
|
||||
isLoading={selectedKeyToUpdate === key.id && isUpdatingJWTSigningKey}
|
||||
setSelectedKey={setSelectedKey}
|
||||
setShownDialog={setShownDialog}
|
||||
handleStandbyKey={handleStandbyKey}
|
||||
handlePreviouslyUsedKey={handlePreviouslyUsedKey}
|
||||
legacyKey={legacyKey}
|
||||
standbyKey={standbyKey}
|
||||
/>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
|
||||
+32
-12
@@ -12,6 +12,7 @@ import {
|
||||
} from 'lucide-react'
|
||||
|
||||
import { components } from 'api-types'
|
||||
import { DropdownMenuItemTooltip } from 'components/ui/DropdownMenuItemTooltip'
|
||||
import { JWTSigningKey } from 'data/jwt-signing-keys/jwt-signing-keys-query'
|
||||
import {
|
||||
Badge,
|
||||
@@ -35,6 +36,7 @@ interface SigningKeyRowProps {
|
||||
handleStandbyKey: (keyId: string) => void
|
||||
legacyKey?: JWTSigningKey | null
|
||||
standbyKey?: JWTSigningKey | null
|
||||
isLoading?: boolean
|
||||
}
|
||||
|
||||
const MotionTableRow = motion(TableRow)
|
||||
@@ -47,6 +49,7 @@ export const SigningKeyRow = ({
|
||||
handleStandbyKey,
|
||||
legacyKey,
|
||||
standbyKey,
|
||||
isLoading = false,
|
||||
}: SigningKeyRowProps) => (
|
||||
<MotionTableRow
|
||||
key={signingKey.id}
|
||||
@@ -112,9 +115,14 @@ export const SigningKeyRow = ({
|
||||
{(signingKey.status !== 'in_use' || signingKey.algorithm !== 'HS256') && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button type="text" className="px-2" icon={<MoreVertical className="size-4" />} />
|
||||
<Button
|
||||
type="text"
|
||||
className="px-1.5"
|
||||
loading={isLoading}
|
||||
icon={<MoreVertical className="size-4" />}
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuContent align="end" className="w-52">
|
||||
{signingKey.algorithm !== 'HS256' && (
|
||||
<DropdownMenuItem
|
||||
onSelect={() => {
|
||||
@@ -139,15 +147,21 @@ export const SigningKeyRow = ({
|
||||
)}
|
||||
{signingKey.status === 'previously_used' && (
|
||||
<>
|
||||
<DropdownMenuItem
|
||||
onSelect={() => {
|
||||
handleStandbyKey(signingKey.id)
|
||||
}}
|
||||
<DropdownMenuItemTooltip
|
||||
disabled={!!standbyKey}
|
||||
onSelect={() => handleStandbyKey(signingKey.id)}
|
||||
tooltip={{
|
||||
content: {
|
||||
side: 'left',
|
||||
text: !!standbyKey
|
||||
? 'You may only have one standby key at a time'
|
||||
: undefined,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CircleArrowUp className="mr-2 size-4" />
|
||||
Move to standby key
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuItemTooltip>
|
||||
<DropdownMenuItem
|
||||
onSelect={() => {
|
||||
setSelectedKey(signingKey)
|
||||
@@ -162,15 +176,21 @@ export const SigningKeyRow = ({
|
||||
)}
|
||||
{signingKey.status === 'revoked' && (
|
||||
<>
|
||||
<DropdownMenuItem
|
||||
onSelect={() => {
|
||||
handleStandbyKey(signingKey.id)
|
||||
}}
|
||||
<DropdownMenuItemTooltip
|
||||
disabled={!!standbyKey}
|
||||
onSelect={() => handleStandbyKey(signingKey.id)}
|
||||
tooltip={{
|
||||
content: {
|
||||
side: 'left',
|
||||
text: !!standbyKey
|
||||
? 'You may only have one standby key at a time'
|
||||
: undefined,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CircleArrowUp className="mr-2 size-4" />
|
||||
Move to standby key
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuItemTooltip>
|
||||
<DropdownMenuItem
|
||||
onSelect={() => {
|
||||
setSelectedKey(signingKey)
|
||||
|
||||
Reference in new issue
Block a user