diff --git a/apps/web/src/app/admin-console/organizations/manage/verify-recover-delete-org.component.html b/apps/web/src/app/admin-console/organizations/manage/verify-recover-delete-org.component.html
new file mode 100644
index 0000000000..a689e98985
--- /dev/null
+++ b/apps/web/src/app/admin-console/organizations/manage/verify-recover-delete-org.component.html
@@ -0,0 +1,39 @@
+
+
+
{{ "deleteOrganization" | i18n }}
+
+
+
{{
+ "deletingOrganizationIsPermanentWarning" | i18n: name
+ }}
+
+ {{ name }}
+
+
{{ "deleteRecoverOrgConfirmDesc" | i18n }}
+
{{ "deletingOrganizationActiveUserAccountsWarning" | i18n }}
+
+
+
+
+
+
diff --git a/apps/web/src/app/admin-console/organizations/manage/verify-recover-delete-org.component.ts b/apps/web/src/app/admin-console/organizations/manage/verify-recover-delete-org.component.ts
new file mode 100644
index 0000000000..0039347dc6
--- /dev/null
+++ b/apps/web/src/app/admin-console/organizations/manage/verify-recover-delete-org.component.ts
@@ -0,0 +1,54 @@
+import { Component, OnInit } from "@angular/core";
+import { ActivatedRoute, Router } from "@angular/router";
+import { firstValueFrom } from "rxjs";
+
+import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
+import { OrganizationVerifyDeleteRecoverRequest } from "@bitwarden/common/admin-console/models/request/organization-verify-delete-recover.request";
+import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
+import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
+
+import { SharedModule } from "../../../shared/shared.module";
+
+@Component({
+ templateUrl: "verify-recover-delete-org.component.html",
+ standalone: true,
+ imports: [SharedModule],
+})
+export class VerifyRecoverDeleteOrgComponent implements OnInit {
+ loading = true;
+ name: string;
+
+ private orgId: string;
+ private token: string;
+
+ constructor(
+ private router: Router,
+ private apiService: OrganizationApiServiceAbstraction,
+ private platformUtilsService: PlatformUtilsService,
+ private i18nService: I18nService,
+ private route: ActivatedRoute,
+ ) {}
+
+ async ngOnInit() {
+ const qParams = await firstValueFrom(this.route.queryParams);
+ if (qParams.orgId != null && qParams.token != null && qParams.name != null) {
+ this.orgId = qParams.orgId;
+ this.token = qParams.token;
+ this.name = qParams.name;
+ this.loading = false;
+ } else {
+ await this.router.navigate(["/"]);
+ }
+ }
+
+ submit = async () => {
+ const request = new OrganizationVerifyDeleteRecoverRequest(this.token);
+ await this.apiService.deleteUsingToken(this.orgId, request);
+ this.platformUtilsService.showToast(
+ "success",
+ this.i18nService.t("organizationDeleted"),
+ this.i18nService.t("organizationDeletedDesc"),
+ );
+ await this.router.navigate(["/"]);
+ };
+}
diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts
index 066ed5db10..93d68d31b3 100644
--- a/apps/web/src/app/oss-routing.module.ts
+++ b/apps/web/src/app/oss-routing.module.ts
@@ -11,6 +11,7 @@ import {
import { flagEnabled, Flags } from "../utils/flags";
+import { VerifyRecoverDeleteOrgComponent } from "./admin-console/organizations/manage/verify-recover-delete-org.component";
import { AcceptFamilySponsorshipComponent } from "./admin-console/organizations/sponsorships/accept-family-sponsorship.component";
import { FamiliesForEnterpriseSetupComponent } from "./admin-console/organizations/sponsorships/families-for-enterprise-setup.component";
import { VerifyRecoverDeleteProviderComponent } from "./admin-console/providers/verify-recover-delete-provider.component";
@@ -157,6 +158,12 @@ const routes: Routes = [
canActivate: [UnauthGuard],
data: { titleId: "deleteAccount" },
},
+ {
+ path: "verify-recover-delete-org",
+ component: VerifyRecoverDeleteOrgComponent,
+ canActivate: [UnauthGuard],
+ data: { titleId: "deleteOrganization" },
+ },
{
path: "verify-recover-delete-provider",
component: VerifyRecoverDeleteProviderComponent,
diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts
index c7ae63f25c..ce5f442bd2 100644
--- a/apps/web/src/app/shared/loose-components.module.ts
+++ b/apps/web/src/app/shared/loose-components.module.ts
@@ -9,6 +9,7 @@ import { LayoutComponent, NavigationModule } from "@bitwarden/components";
import { OrganizationLayoutComponent } from "../admin-console/organizations/layouts/organization-layout.component";
import { EventsComponent as OrgEventsComponent } from "../admin-console/organizations/manage/events.component";
import { UserConfirmComponent as OrgUserConfirmComponent } from "../admin-console/organizations/manage/user-confirm.component";
+import { VerifyRecoverDeleteOrgComponent } from "../admin-console/organizations/manage/verify-recover-delete-org.component";
import { AcceptFamilySponsorshipComponent } from "../admin-console/organizations/sponsorships/accept-family-sponsorship.component";
import { ExposedPasswordsReportComponent as OrgExposedPasswordsReportComponent } from "../admin-console/organizations/tools/exposed-passwords-report.component";
import { InactiveTwoFactorReportComponent as OrgInactiveTwoFactorReportComponent } from "../admin-console/organizations/tools/inactive-two-factor-report.component";
@@ -115,6 +116,7 @@ import { SharedModule } from "./shared.module";
OrganizationLayoutComponent,
UserLayoutComponent,
PaymentMethodWarningsModule,
+ VerifyRecoverDeleteOrgComponent,
],
declarations: [
AcceptFamilySponsorshipComponent,
diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json
index e79497d25e..f2546e38ec 100644
--- a/apps/web/src/locales/en/messages.json
+++ b/apps/web/src/locales/en/messages.json
@@ -1341,6 +1341,9 @@
"accountDeletedDesc": {
"message": "Your account has been closed and all associated data has been deleted."
},
+ "deleteOrganizationWarning": {
+ "message": "Deleting your organization is permanent. It cannot be undone."
+ },
"myAccount": {
"message": "My account"
},
@@ -3388,6 +3391,9 @@
"deleteRecoverConfirmDesc": {
"message": "You have requested to delete your Bitwarden account. Use the button below to confirm."
},
+ "deleteRecoverOrgConfirmDesc": {
+ "message": "You have requested to delete your Bitwarden organization."
+ },
"myOrganization": {
"message": "My organization"
},
diff --git a/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts b/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts
index 66a05cf613..3aca2fb3f6 100644
--- a/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts
+++ b/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts
@@ -23,6 +23,7 @@ import { OrganizationCreateRequest } from "../../models/request/organization-cre
import { OrganizationKeysRequest } from "../../models/request/organization-keys.request";
import { OrganizationUpdateRequest } from "../../models/request/organization-update.request";
import { OrganizationUpgradeRequest } from "../../models/request/organization-upgrade.request";
+import { OrganizationVerifyDeleteRecoverRequest } from "../../models/request/organization-verify-delete-recover.request";
import { OrganizationApiKeyInformationResponse } from "../../models/response/organization-api-key-information.response";
import { OrganizationAutoEnrollStatusResponse } from "../../models/response/organization-auto-enroll-status.response";
import { OrganizationKeysResponse } from "../../models/response/organization-keys.response";
@@ -54,6 +55,10 @@ export class OrganizationApiServiceAbstraction {
reinstate: (id: string) => Promise;
leave: (id: string) => Promise;
delete: (id: string, request: SecretVerificationRequest) => Promise;
+ deleteUsingToken: (
+ organizationId: string,
+ request: OrganizationVerifyDeleteRecoverRequest,
+ ) => Promise;
updateLicense: (id: string, data: FormData) => Promise;
importDirectory: (organizationId: string, request: ImportDirectoryRequest) => Promise;
getOrCreateApiKey: (id: string, request: OrganizationApiKeyRequest) => Promise;
diff --git a/libs/common/src/admin-console/models/request/organization-verify-delete-recover.request.ts b/libs/common/src/admin-console/models/request/organization-verify-delete-recover.request.ts
new file mode 100644
index 0000000000..c6e08ace40
--- /dev/null
+++ b/libs/common/src/admin-console/models/request/organization-verify-delete-recover.request.ts
@@ -0,0 +1,7 @@
+export class OrganizationVerifyDeleteRecoverRequest {
+ token: string;
+
+ constructor(token: string) {
+ this.token = token;
+ }
+}
diff --git a/libs/common/src/admin-console/services/organization/organization-api.service.ts b/libs/common/src/admin-console/services/organization/organization-api.service.ts
index 262232a964..91d5e47547 100644
--- a/libs/common/src/admin-console/services/organization/organization-api.service.ts
+++ b/libs/common/src/admin-console/services/organization/organization-api.service.ts
@@ -26,6 +26,7 @@ import { OrganizationCreateRequest } from "../../models/request/organization-cre
import { OrganizationKeysRequest } from "../../models/request/organization-keys.request";
import { OrganizationUpdateRequest } from "../../models/request/organization-update.request";
import { OrganizationUpgradeRequest } from "../../models/request/organization-upgrade.request";
+import { OrganizationVerifyDeleteRecoverRequest } from "../../models/request/organization-verify-delete-recover.request";
import { OrganizationApiKeyInformationResponse } from "../../models/response/organization-api-key-information.response";
import { OrganizationAutoEnrollStatusResponse } from "../../models/response/organization-auto-enroll-status.response";
import { OrganizationKeysResponse } from "../../models/response/organization-keys.response";
@@ -198,6 +199,19 @@ export class OrganizationApiService implements OrganizationApiServiceAbstraction
await this.syncService.fullSync(true);
}
+ deleteUsingToken(
+ organizationId: string,
+ request: OrganizationVerifyDeleteRecoverRequest,
+ ): Promise {
+ return this.apiService.send(
+ "POST",
+ "/organizations/" + organizationId + "/delete-recover-token",
+ request,
+ false,
+ false,
+ );
+ }
+
async updateLicense(id: string, data: FormData): Promise {
await this.apiService.send(
"POST",