diff --git a/src/Core/Auth/UserFeatures/EmergencyAccess/Commands/DeleteEmergencyAccessCommand.cs b/src/Core/Auth/UserFeatures/EmergencyAccess/Commands/DeleteEmergencyAccessCommand.cs
new file mode 100644
index 0000000000..e7ae1ec14f
--- /dev/null
+++ b/src/Core/Auth/UserFeatures/EmergencyAccess/Commands/DeleteEmergencyAccessCommand.cs
@@ -0,0 +1,63 @@
+using Bit.Core.Auth.Models.Data;
+using Bit.Core.Auth.UserFeatures.EmergencyAccess.Interfaces;
+using Bit.Core.Auth.UserFeatures.EmergencyAccess.Mail;
+using Bit.Core.Exceptions;
+using Bit.Core.Platform.Mail.Mailer;
+using Bit.Core.Repositories;
+
+namespace Bit.Core.Auth.UserFeatures.EmergencyAccess.Commands;
+
+public class DeleteEmergencyAccessCommand(
+ IEmergencyAccessRepository _emergencyAccessRepository,
+ IMailer mailer) : IDeleteEmergencyAccessCommand
+{
+ ///
+ public async Task DeleteByIdGrantorIdAsync(Guid emergencyAccessId, Guid grantorId)
+ {
+ var emergencyAccess = await _emergencyAccessRepository.GetDetailsByIdGrantorIdAsync(emergencyAccessId, grantorId);
+
+ if (emergencyAccess == null || emergencyAccess.GrantorId != grantorId)
+ {
+ throw new BadRequestException("Emergency Access not valid.");
+ }
+
+ await _emergencyAccessRepository.DeleteAsync(emergencyAccess);
+
+ // Send notification email to grantor
+ await SendEmailAsync(emergencyAccess.GrantorEmail, [emergencyAccess.GranteeName]);
+ return emergencyAccess;
+ }
+
+ ///
+ public async Task> DeleteAllByGrantorIdAsync(Guid grantorId)
+ {
+ var emergencyAccessDetails = await _emergencyAccessRepository.GetManyDetailsByGrantorIdAsync(grantorId);
+
+ foreach (var details in emergencyAccessDetails)
+ {
+ var emergencyAccess = details.ToEmergencyAccess();
+ await _emergencyAccessRepository.DeleteAsync(emergencyAccess);
+ }
+
+ // Send notification email to grantor
+ await SendEmailAsync(
+ emergencyAccessDetails.FirstOrDefault()?.GrantorEmail ?? string.Empty,
+ [.. emergencyAccessDetails.Select(e => e.GranteeName)]);
+
+ return emergencyAccessDetails;
+ }
+
+ private async Task SendEmailAsync(string grantorEmail, string[] granteeNames)
+ {
+ var email = new EmergencyAccessRemoveGranteesMail
+ {
+ ToEmails = [grantorEmail],
+ View = new EmergencyAccessRemoveGranteesMailView
+ {
+ RemovedGranteeNames = granteeNames
+ }
+ };
+
+ await mailer.SendEmail(email);
+ }
+}
diff --git a/src/Core/Auth/UserFeatures/EmergencyAccess/Interfaces/IDeleteEmergencyAccessCommand.cs b/src/Core/Auth/UserFeatures/EmergencyAccess/Interfaces/IDeleteEmergencyAccessCommand.cs
new file mode 100644
index 0000000000..5c6838efab
--- /dev/null
+++ b/src/Core/Auth/UserFeatures/EmergencyAccess/Interfaces/IDeleteEmergencyAccessCommand.cs
@@ -0,0 +1,28 @@
+using Bit.Core.Auth.Models.Data;
+using Bit.Core.Exceptions;
+
+namespace Bit.Core.Auth.UserFeatures.EmergencyAccess.Interfaces;
+
+///
+/// Command for deleting emergency access records based on the grantor's user ID.
+///
+public interface IDeleteEmergencyAccessCommand
+{
+ ///
+ /// Deletes a single emergency access record for the specified grantor.
+ ///
+ /// The ID of the emergency access record to delete.
+ /// The ID of the grantor user who owns the emergency access record.
+ /// A task representing the asynchronous operation.
+ ///
+ /// Thrown when the emergency access record is not found or does not belong to the specified grantor.
+ ///
+ Task DeleteByIdGrantorIdAsync(Guid emergencyAccessId, Guid grantorId);
+
+ ///
+ /// Deletes all emergency access records for the specified grantor.
+ ///
+ /// The ID of the grantor user whose emergency access records should be deleted.
+ /// A collection of the deleted emergency access records.
+ Task> DeleteAllByGrantorIdAsync(Guid grantorId);
+}
diff --git a/src/Core/Auth/UserFeatures/UserServiceCollectionExtensions.cs b/src/Core/Auth/UserFeatures/UserServiceCollectionExtensions.cs
index 6249d1cb1c..356d5bf2bc 100644
--- a/src/Core/Auth/UserFeatures/UserServiceCollectionExtensions.cs
+++ b/src/Core/Auth/UserFeatures/UserServiceCollectionExtensions.cs
@@ -1,5 +1,7 @@
using Bit.Core.Auth.Sso;
using Bit.Core.Auth.UserFeatures.DeviceTrust;
+using Bit.Core.Auth.UserFeatures.EmergencyAccess.Commands;
+using Bit.Core.Auth.UserFeatures.EmergencyAccess.Interfaces;
using Bit.Core.Auth.UserFeatures.Registration;
using Bit.Core.Auth.UserFeatures.Registration.Implementations;
using Bit.Core.Auth.UserFeatures.TdeOffboardingPassword.Interfaces;
@@ -23,6 +25,7 @@ public static class UserServiceCollectionExtensions
{
services.AddScoped();
services.AddDeviceTrustCommands();
+ services.AddEmergencyAccessCommands();
services.AddUserPasswordCommands();
services.AddUserRegistrationCommands();
services.AddWebAuthnLoginCommands();
@@ -36,6 +39,11 @@ public static class UserServiceCollectionExtensions
services.AddScoped();
}
+ private static void AddEmergencyAccessCommands(this IServiceCollection services)
+ {
+ services.AddScoped();
+ }
+
public static void AddUserKeyCommands(this IServiceCollection services, IGlobalSettings globalSettings)
{
services.AddScoped();