OAuthSecrets.tsx 3.3 KB

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