[PR #28201] feat: Add option to delete or keep API keys when uninstalling plugin #31983

Closed
opened 2026-02-21 20:50:31 -05:00 by yindo · 0 comments
Owner

Original Pull Request: https://github.com/langgenius/dify/pull/28201

State: closed
Merged: Yes


Description

Summary

This PR automatically deletes plugin credentials when uninstalling a plugin, preventing orphaned API keys from persisting in the database.

Updated Implementation (Nov 29, 2025): Following review feedback from @Yeuoly, this PR has been completely refactored to use automatic forced deletion instead of user selection.

"IMO, whether to delete credentials should not be an option to users, I prefer deleting them forcefully." - @Yeuoly


Problem (Issue #27531)

When users deleted a plugin, associated API keys remained in the database:

  • Credentials silently retained after plugin deletion
  • No cleanup of unused credentials
  • API keys automatically restored on plugin reinstall
  • Users confused about credential persistence

User complaint:

"I deleted the OpenAI plugin, and after reinstalling it, the original API key was automatically restored. I want to clear it."


Solution

Simple automatic deletion:

  • When plugin is uninstalled, all associated credentials are automatically deleted
  • No user interaction required
  • Clean database state after uninstall
  • Plugin reinstall requires fresh API key configuration

Changes

Backend (api/services/plugin/plugin_service.py)

Modified the uninstall() method to:

  1. Get plugin information before uninstalling
  2. Query all associated ProviderCredential entries (matching plugin_id/% pattern)
  3. Delete all found credentials
  4. Proceed with plugin uninstall

@staticmethod
def uninstall(tenant_id: str, plugin_installation_id: str) -> bool:
# Get plugin info before uninstalling
plugins = manager.list_plugins(tenant_id)
plugin = next((p for p in plugins if p.installation_id == plugin_installation_id), None)

if plugin:
    # Delete all credentials matching plugin_id/%
    credentials = db.session.scalars(
        select(ProviderCredential).where(
            ProviderCredential.tenant_id == tenant_id,
            ProviderCredential.provider_name.like(f"{plugin_id}/%"),
        )
    ).all()
    
    for cred in credentials:
        db.session.delete(cred)
    db.session.commit()

return manager.uninstall(tenant_id, plugin_installation_id)**Key Features:**
  • Uses lazy % logging format (Python best practice)
  • Graceful error handling - continues uninstall even if credential deletion fails
  • Logs credential deletion operations for debugging

Code Comparison

Previous approach: +435 −23 lines (user selection UI, multiple components)
Current approach: +57 −0 lines (automatic deletion, backend only)

Simplification:

  • 87% less code
  • No frontend changes needed
  • Simpler to maintain
  • Follows reviewer recommendation

Testing

Manual Testing:

  1. Install plugin with API key configuration
  2. Uninstall plugin → credentials automatically deleted
  3. Reinstall plugin → requires new API key (old one gone)
  4. Database queries confirm no orphaned credentials

Tested with Docker deployment:

  • All services running correctly
  • Plugin uninstall flow working as expected
  • No database orphaned records

Database Impact

Affected Tables:

  • provider_credential - Provider-level credentials deleted via pattern match plugin_id/%

Query Pattern:
DELETE FROM provider_credential
WHERE tenant_id = ?
AND provider_name LIKE 'plugin_id/%'---

How This Solves Issue #27531

Before:

  • User deletes OpenAI plugin
  • API key remains in database
  • Plugin reinstall → old API key restored
  • User frustrated, wants to clear it

After:

  • User deletes OpenAI plugin
  • API key automatically deleted from database
  • Plugin reinstall → requires new API key
  • Clean state, issue resolved

Related Issue

Fixes #27531


Checklist

  • Code follows project coding standards
  • Self-reviewed the code
  • Added necessary comments
  • Changes generate no new warnings
  • Tested locally with Docker
  • CI checks passing
  • Addressed all review feedback
**Original Pull Request:** https://github.com/langgenius/dify/pull/28201 **State:** closed **Merged:** Yes --- ## Description ### Summary This PR automatically deletes plugin credentials when uninstalling a plugin, preventing orphaned API keys from persisting in the database. **Updated Implementation (Nov 29, 2025)**: Following review feedback from @Yeuoly, this PR has been **completely refactored** to use automatic forced deletion instead of user selection. > "IMO, whether to delete credentials should not be an option to users, I prefer deleting them forcefully." - @Yeuoly --- ### Problem (Issue #27531) When users deleted a plugin, associated API keys remained in the database: - ❌ Credentials silently retained after plugin deletion - ❌ No cleanup of unused credentials - ❌ API keys automatically restored on plugin reinstall - ❌ Users confused about credential persistence **User complaint:** > "I deleted the OpenAI plugin, and after reinstalling it, the original API key was automatically restored. I want to clear it." --- ### Solution **Simple automatic deletion:** - ✅ When plugin is uninstalled, all associated credentials are **automatically deleted** - ✅ No user interaction required - ✅ Clean database state after uninstall - ✅ Plugin reinstall requires fresh API key configuration --- ### Changes #### Backend (`api/services/plugin/plugin_service.py`) Modified the `uninstall()` method to: 1. Get plugin information before uninstalling 2. Query all associated `ProviderCredential` entries (matching `plugin_id/%` pattern) 3. Delete all found credentials 4. Proceed with plugin uninstall @staticmethod def uninstall(tenant_id: str, plugin_installation_id: str) -> bool: # Get plugin info before uninstalling plugins = manager.list_plugins(tenant_id) plugin = next((p for p in plugins if p.installation_id == plugin_installation_id), None) if plugin: # Delete all credentials matching plugin_id/% credentials = db.session.scalars( select(ProviderCredential).where( ProviderCredential.tenant_id == tenant_id, ProviderCredential.provider_name.like(f"{plugin_id}/%"), ) ).all() for cred in credentials: db.session.delete(cred) db.session.commit() return manager.uninstall(tenant_id, plugin_installation_id)**Key Features:** - Uses lazy `%` logging format (Python best practice) - Graceful error handling - continues uninstall even if credential deletion fails - Logs credential deletion operations for debugging --- ### Code Comparison **Previous approach:** +435 −23 lines (user selection UI, multiple components) **Current approach:** +57 −0 lines (automatic deletion, backend only) **Simplification:** - ✅ 87% less code - ✅ No frontend changes needed - ✅ Simpler to maintain - ✅ Follows reviewer recommendation --- ### Testing **Manual Testing:** 1. ✅ Install plugin with API key configuration 2. ✅ Uninstall plugin → credentials automatically deleted 3. ✅ Reinstall plugin → requires new API key (old one gone) 4. ✅ Database queries confirm no orphaned credentials **Tested with Docker deployment:** - All services running correctly - Plugin uninstall flow working as expected - No database orphaned records --- ### Database Impact **Affected Tables:** - `provider_credential` - Provider-level credentials deleted via pattern match `plugin_id/%` **Query Pattern:** DELETE FROM provider_credential WHERE tenant_id = ? AND provider_name LIKE 'plugin_id/%'--- ### How This Solves Issue #27531 **Before:** - User deletes OpenAI plugin - API key remains in database - Plugin reinstall → old API key restored - ❌ User frustrated, wants to clear it **After:** - User deletes OpenAI plugin - API key automatically deleted from database - Plugin reinstall → requires new API key - ✅ Clean state, issue resolved --- ### Related Issue Fixes #27531 --- ## Checklist - [x] Code follows project coding standards - [x] Self-reviewed the code - [x] Added necessary comments - [x] Changes generate no new warnings - [x] Tested locally with Docker - [x] CI checks passing - [x] Addressed all review feedback
yindo added the pull-request label 2026-02-21 20:50:31 -05:00
yindo closed this issue 2026-02-21 20:50:31 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langgenius/dify#31983