Secure Asset Delete

1. Front Matter

  • Title: Secure Asset Delete

  • Author: Joshua Uriel Tribiana

  • Reviewers: Joylynne Esportuno (teruterubozuuu)

  • Creation Date: 2026-06-29

  • Status: Approved

  • References:

    • Issue: [3.7] Secure Asset Delete

2. Introduction & Goals

  • Problem Summary
    Creators need a secure and irreversible method to permanently remove assets from their Asset Library. This is essential for managing storage space, complying with data retention policies, and removing obsolete or incorrect content. To prevent accidental data loss, this destructive action must be protected by a robust confirmation mechanism that cannot be bypassed on the client side.

  • Goals

    • Provide an API to permanently delete an asset file from Supabase Storage.

    • Delete the corresponding record from the asset_metadata table upon successful file deletion.

    • Implement a "typed challenge" confirmation modal where the user must type "DELETE" to proceed.

    • Crucially, enforce the typed challenge server-side to prevent malicious or accidental bypass.

    • Ensure only the asset's owner can perform the deletion.

    • Log the permanent deletion event for auditing purposes.

  • Non-Goals

    • A soft-delete or "trash bin" functionality for assets (this is handled by the "Archive Asset" feature).

    • The ability to restore a permanently deleted asset.

    • Bulk deletion of multiple assets in a single request.

  • Glossary

    • Asset: Any file (image, video, document) uploaded by a creator to their library.

    • Asset Library: The UI where creators manage their uploaded assets.

    • Typed Challenge: A security confirmation where the user must type a specific word (e.g., "DELETE") to authorize a destructive action.

    • RLS: Row-Level Security in PostgreSQL, used to enforce data access policies at the database layer.

3. High-Level Architecture

  • System Diagram
    +----------------------+

    | Asset Card |

    | (Asset Library) |

    +----------------------+

    |

    v

    +----------------------+

    | Confirmation Dialog |

    | Type "DELETE" |

    +----------------------+

    |

    v

    +----------------------+

    | Client Validation |

    +----------------------+

    |

    v

    +----------------------+

    | DELETE API Request |

    +----------------------+

    |

    v

    +----------------------+

    | Authentication |

    +----------------------+

    |

    v

    +----------------------+

    | Server Validation |

    | Verify "DELETE" |

    +----------------------+

    |

    v

    +----------------------+

    | Verify Ownership |

    +----------------------+

    |

    v

    +----------------------+

    | Delete File from |

    | Storage |

    +----------------------+

    |

    v

    +----------------------+

    | Remove Metadata |

    | from Database |

    +----------------------+

    |

    v

    +----------------------+

    | Audit Log |

    +----------------------+

    |

    v

    +----------------------+

    | Success Response |

    | Asset Removed |

    +----------------------+
    +----------------------+

    | Confirmation Dialog |

    +----------------------+

    |

    v

    +----------------------+

    | DELETE API Request |

    +----------------------+

    |

    +-----------------+-----------------+

    | | |

    v v v

    +----------------+ +----------------+ +----------------+

    | Invalid Input | | Not Owner | | Valid Request |

    | 400 Bad Req. | | 403 Forbidden | | Continue |

    +----------------+ +----------------+ +----------------+

    |

    v

    +----------------------+

    | Delete Storage File |

    +----------------------+

    |

    v

    +----------------------+

    | Delete DB Metadata |

    +----------------------+

    |

    v

    +----------------------+

    | Audit Log |

    +----------------------+

    |

    v

    +----------------------+

    | 200 OK |

    +----------------------+

  • Technologies Used

    • Next.js: For API routes.

    • Clerk: For user authentication.

    • Supabase:

      • PostgreSQL: For storing asset metadata in the asset_metadata table.

      • Storage: For storing the actual asset files.

    • Zod: For request body schema validation.

    • React: For the frontend confirmation modal.

4. Detailed Design & Implementation

  • Data Model / Schema
    This feature performs a DELETE operation on the asset_metadata table and its corresponding file in Supabase Storage. No schema changes are required.

    asset_metadata Table (Relevant Columns):

    Column

    Type

    Description

    id

    UUID

    Primary Key for the asset metadata.

    creator_id

    TEXT

    The Clerk ID of the user who owns the asset.

    file_path

    TEXT

    The path to the file in Supabase Storage.

    deleted_at

    TIMESTAMPTZ

    Used for soft-deleting (archiving), not this feature.

  • API Specification

    • Endpoint: DELETE /api/creator/background-assets/purge-assets

    • Authentication: Required (Creator role).

    • Request Body:

    {
    "assetId": "uuid-of-the-asset-to-delete",
    "challengeText": "DELETE"
    }
  • Zod Schema (purgeAssetSchema):

    typescript

    export const purgeAssetSchema = z.object({

    assetId: z.string().uuid("Invalid asset ID"),

    challengeText: z.literal("DELETE", {

    errorMap: () => ({ message: "Confirmation text must be 'DELETE'" }),

    }),

    });

  • Success Response (200 OK):

    json

    {

    "success": true,

    "message": "Asset permanently deleted successfully."

    }


  • Error Responses:

    • 400 Bad Request: assetId is missing/invalid or challengeText is not "DELETE".

    • 401 Unauthorized: User is not authenticated.

    • 403 Forbidden: User does not own the asset.

    • 404 Not Found: The asset does not exist.

    • 500 Internal Server Error: Failed to delete the file from storage or the record from the database.

Logic & Workflows

  • Server-side Deletion Flow (.../purge-assets/route.ts):

  1. Authenticate: The user's session is verified via authenticateUser().

  2. Validate Request: The request body is parsed and validated against purgeAssetSchema. This provides the critical server-side check that challengeText is exactly "DELETE".

  3. Fetch Asset Metadata: The API queries the asset_metadata table for the given assetId. The query includes an eq("creator_id", creatorId) clause, which, combined with RLS, ensures the user owns the asset.

  4. Handle Not Found: If no record is returned, respond with a 404 Not Found error.

  5. Delete from Storage: The file_path from the metadata is used to delete the object from Supabase Storage. This is done first to avoid orphaned files if the database delete fails.

  6. Delete from Database: If storage deletion is successful, the record is deleted from the asset_metadata table.

  7. Log and Respond: The action is logged, and a 200 OK success response is returned. If any step fails, an appropriate error is logged and a 500 response is sent.

  • Key Files:

    • app/api/creator/(background-assets)/purge-assets/route.ts: The main API endpoint handler.

    • components/creator/asset-library/DeleteAssetModal.tsx: The frontend modal that implements the typed challenge UI.

    • lib/schemas/asset.schema.ts: Contains the purgeAssetSchema for Zod validation.

5. Infrastructure & Operations

Dependencies

  • Internal: Relies on the existing asset_metadata table and Clerk authentication service.

  • External: Supabase (PostgreSQL and Storage).

Monitoring & Alerting

  • Logging: Successful deletions and, more importantly, any failures during the deletion process (both storage and database steps) should be logged with high severity.

  • Alerts:

    • Configure alerts for a high rate of 5xx errors on the DELETE /.../purge-assets endpoint, as this could indicate a problem with Supabase Storage or database permissions.

    • Monitor for 403 Forbidden errors, which could signify attempts to delete assets not owned by the user.

Deployment Plan

  • Database Migrations: No schema changes are required.

  • Rollout: This is a core destructive action. The API endpoint and UI components can be deployed together. Ensure that the server-side validation of the challengeText is confirmed to be active before merging to production.

6. Testing & Quality Assurance

Test Strategy

  • Unit Tests:

    • Test the purgeAssetSchema to ensure it correctly validates the challengeText.

  • Integration Tests:

    • Test the API endpoint by mocking Supabase calls.

    • Verify that a request with an incorrect challengeText (e.g., "delete", "DELETE ") is rejected with a 400 error.

    • Verify that a request to delete an asset not owned by the user is rejected with a 403 or 404 error.

    • Verify that a successful request calls the storage remove() method before the database delete() method.

  • End-to-End (E2E) / QA:

    • Flow: In the Asset Library, click "Delete Permanently" -> Modal appears -> Type "DELETE" -> Click confirm -> Verify the asset is gone from the library and a success toast appears.

    • Negative Test: Attempt to confirm deletion by typing an incorrect word; verify the confirm button remains disabled or shows an error.

    • Security Test: Attempt to bypass the UI by sending a DELETE request directly to the API without the correct challengeText; verify it is rejected.

Known Limitations

  • As noted in the feature audit, the primary risk was a lack of server-side enforcement. This design explicitly addresses that. Once implemented, there are no other known limitations.

7. Maintenance & Support

Troubleshooting

  • User reports asset is still visible after deletion:

    1. This likely means the client-side state was not updated. A page refresh should resolve it.

    2. If it persists, check the server logs for errors during the API call. The database DELETE might have failed after the storage remove succeeded, leaving an orphaned metadata record.

  • API returns 500 Internal Server Error:

    1. Check Supabase Storage permissions. The service role key used by the API must have delete permissions on the assets bucket.

    2. Check database RLS policies and permissions for the asset_metadata table.

Changelog

1.0 - Approved, Technical guide created, 2026-06-29



Was this article helpful?