OAuthSecrets.tsx95 lines · main
| 1 | import { PermissionAction } from '@supabase/shared-types/out/constants' |
| 2 | import { useParams } from 'common' |
| 3 | import { useState } from 'react' |
| 4 | import { Alert, AlertTitle, InfoIcon } from 'ui' |
| 5 | |
| 6 | import { SecretRow } from './SecretRow' |
| 7 | import { ButtonTooltip } from '@/components/ui/ButtonTooltip' |
| 8 | import { InlineLink } from '@/components/ui/InlineLink' |
| 9 | import { useClientSecretCreateMutation } from '@/data/oauth-secrets/client-secret-create-mutation' |
| 10 | import { CreatedSecret, useClientSecretsQuery } from '@/data/oauth-secrets/client-secrets-query' |
| 11 | import { OAuthApp } from '@/data/oauth/oauth-apps-query' |
| 12 | import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' |
| 13 | import { DOCS_URL } from '@/lib/constants' |
| 14 | |
| 15 | interface Props { |
| 16 | selectedApp?: OAuthApp |
| 17 | } |
| 18 | |
| 19 | export const OAuthSecrets = ({ selectedApp }: Props) => { |
| 20 | const { slug } = useParams() |
| 21 | const [createdSecret, setCreatedSecret] = useState<CreatedSecret>() |
| 22 | const { can: canManageSecrets } = useAsyncCheckPermissions(PermissionAction.UPDATE, 'oauth_apps') |
| 23 | |
| 24 | const { id: appId } = selectedApp ?? {} |
| 25 | |
| 26 | const { data } = useClientSecretsQuery({ slug, appId }) |
| 27 | const secrets = data?.client_secrets ?? [] |
| 28 | |
| 29 | const { mutate: createSecret, isPending: isCreatingSecret } = useClientSecretCreateMutation({ |
| 30 | onSuccess: (data) => setCreatedSecret(data), |
| 31 | }) |
| 32 | |
| 33 | const handleCreateSecret = () => { |
| 34 | if (!slug) return console.error('Slug is required') |
| 35 | if (!appId) return console.error('App ID is required') |
| 36 | createSecret({ slug, appId }) |
| 37 | } |
| 38 | |
| 39 | return ( |
| 40 | <div className="flex flex-col gap-4"> |
| 41 | <div className="flex items-center justify-between"> |
| 42 | <div className="flex flex-col"> |
| 43 | <span className="text-sm text-foreground">Client secrets</span> |
| 44 | <span className="text-sm text-foreground-light"> |
| 45 | For handling callbacks in the OAuth 2.0 flow. Learn more{' '} |
| 46 | <InlineLink |
| 47 | href={`${DOCS_URL}/guides/integrations/build-a-briven-integration#handling-the-callback`} |
| 48 | > |
| 49 | here |
| 50 | </InlineLink> |
| 51 | . |
| 52 | </span> |
| 53 | </div> |
| 54 | |
| 55 | {canManageSecrets && ( |
| 56 | <ButtonTooltip |
| 57 | type="default" |
| 58 | disabled={!appId || secrets.length >= 5} |
| 59 | onClick={handleCreateSecret} |
| 60 | loading={isCreatingSecret} |
| 61 | tooltip={{ |
| 62 | content: { |
| 63 | side: 'bottom', |
| 64 | text: secrets.length >= 5 ? 'You can only have up to 5 client secrets' : undefined, |
| 65 | }, |
| 66 | }} |
| 67 | > |
| 68 | Generate new secret |
| 69 | </ButtonTooltip> |
| 70 | )} |
| 71 | </div> |
| 72 | |
| 73 | {createdSecret && ( |
| 74 | <Alert variant="default"> |
| 75 | <InfoIcon /> |
| 76 | <AlertTitle> |
| 77 | Make sure to copy your new client secret now. You won't be able to see it again. |
| 78 | </AlertTitle> |
| 79 | </Alert> |
| 80 | )} |
| 81 | |
| 82 | <div className="-space-y-px"> |
| 83 | {secrets |
| 84 | .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()) |
| 85 | .map((secret) => { |
| 86 | const isNew = createdSecret?.id === secret.id |
| 87 | const _secret = isNew |
| 88 | ? { ...secret, client_secret: createdSecret.client_secret } |
| 89 | : secret |
| 90 | return <SecretRow key={secret.id} secret={_secret} appId={appId} /> |
| 91 | })} |
| 92 | </div> |
| 93 | </div> |
| 94 | ) |
| 95 | } |