diff --git a/apps/studio/components/interfaces/Organization/OAuthApps/OAuthAppsAuthorizedRow.tsx b/apps/studio/components/interfaces/Organization/OAuthApps/OAuthAppsAuthorizedRow.tsx index 8f828c2e587..9e1e6ae0ab4 100644 --- a/apps/studio/components/interfaces/Organization/OAuthApps/OAuthAppsAuthorizedRow.tsx +++ b/apps/studio/components/interfaces/Organization/OAuthApps/OAuthAppsAuthorizedRow.tsx @@ -16,6 +16,7 @@ import { } from 'ui' import { OAuthAppsMemberGrantsDialogContent } from './OAuthAppsMemberGrantsDialogContent' +import { OAuthAppsRevokeDialogContent } from './OAuthAppsRevokeDialogContent' import type { OAuthApprovalItem, OAuthApprovalTarget } from '@/data/oauth-apps/types' export interface OAuthAppsAuthorizedRowProps { @@ -78,9 +79,23 @@ export const OAuthAppsAuthorizedRow = ({ approval }: OAuthAppsAuthorizedRowProps View grants + + setDialogContent('revoke')} + > + Disconnect + + {dialogContent === 'grants' && } + {dialogContent === 'revoke' && ( + setDialogContent(null)} + /> + )} diff --git a/apps/studio/components/interfaces/Organization/OAuthApps/OAuthAppsRevokeDialogContent.tsx b/apps/studio/components/interfaces/Organization/OAuthApps/OAuthAppsRevokeDialogContent.tsx new file mode 100644 index 00000000000..20a5a03710c --- /dev/null +++ b/apps/studio/components/interfaces/Organization/OAuthApps/OAuthAppsRevokeDialogContent.tsx @@ -0,0 +1,93 @@ +import { useParams } from 'common' +import { toast } from 'sonner' +import { + Button, + DialogClose, + DialogContent, + DialogFooter, + DialogHeader, + DialogSection, + DialogTitle, +} from 'ui' + +import { useOAuthAppRevokeMutation } from '@/data/oauth-apps/oauth-apps-revoke-mutation' +import type { OAuthApprovalItem } from '@/data/oauth-apps/types' + +export interface OAuthAppsRevokeDialogProps { + approval?: OAuthApprovalItem + onClose: () => void +} + +export const OAuthAppsRevokeDialogContent = ({ approval, onClose }: OAuthAppsRevokeDialogProps) => { + const { slug } = useParams() + + const { mutate: revokeApp, isPending } = useOAuthAppRevokeMutation({ + onSuccess: () => { + toast.success(`Revoked access for ${approval?.app.name}`) + onClose() + }, + }) + + if (!approval) return null + const hasOrgGrant = Boolean(approval.org_grant) + + return ( + + + Revoke access for {approval.app.name} + + + +

+ This revokes the app at{' '} + organization level and affects{' '} + all users. +

+ +
+

Caveats:

+
    + {hasOrgGrant && ( +
  • + – + The organization-wide grant will be revoked. +
  • + )} +
  • + – + Every member loses access on the apps next request. +
  • +
  • + – + Members will need to authorize again to reconnect. +
  • +
  • + – + + This is not a per-user + revocation. + +
  • +
+
+
+ + + + + + + +
+ ) +}