Remove legacy API endpoints and compatibility code dropped upstream (#7809)

Remove endpoints, response fields, feature flags and compatibility code
that upstream Bitwarden has removed and that no current client uses:

- Legacy POST /identity/accounts/register and POST /api/accounts/prelogin;
  register/finish is rate limited like the other unauthenticated endpoints.
- Legacy organization routes: PUT groups/{id}/users, PUT policies/{type}/vnext,
  PUT users/{id}/reset-password, GET billing/metadata, and the POST aliases of
  the organization, collection, group and member PUT/DELETE routes.
- POST /two-factor/disable (PUT stays).
- ResetMasterPassword, captchaBypassToken, the legacy registration name field,
  userIsManagedByOrganization/managedByOrganization and accessAll.
- The automatic email 2FA code for clients older than 2025.5.0.
- DUO_USE_IFRAME and the Duo Traditional Prompt code.
- Experimental client feature flags that no client reads anymore.
- Web vault CSS selectors for web vaults older than 2025.5.1.

Also accept the PascalCase fields Android 2026.9.0+ sends on registration
and password change, and align a few account, login/2FA and organization
checks with upstream.

Adds tests for the registration body formats.
This commit is contained in:
Daniel García authored and GitHub committed 2026-10-04 22:16:55 +02:00
1 parent f4f1a8e105
commit 1f99d9d276
17 files changed
+218 -662

No files matched your search

-21
View File
@@ -104,7 +104,6 @@ impl Group {
"id": self.uuid,
"organizationId": self.organizations_uuid,
"name": self.name,
"accessAll": self.access_all,
"externalId": self.external_id,
"collections": collections_groups,
"object": "groupDetails"
@@ -574,26 +573,6 @@ impl GroupUser {
}
}
pub async fn delete_by_group_and_member(
group_uuid: &GroupId,
member_uuid: &MembershipId,
conn: &DbConn,
) -> EmptyResult {
match Membership::find_by_uuid(member_uuid, conn).await {
Some(member) => User::update_uuid_revision(&member.user_uuid, conn).await,
None => warn!("Member could not be found!"),
}
conn.run(move |conn| {
diesel::delete(groups_users::table)
.filter(groups_users::groups_uuid.eq(group_uuid))
.filter(groups_users::users_organizations_uuid.eq(member_uuid))
.execute(conn)
.map_res("Error deleting group users")
})
.await
}
pub async fn delete_all_by_group(group_uuid: &GroupId, org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
let group_users = GroupUser::find_by_group(group_uuid, org_uuid, conn).await;
for group_user in group_users {
+3 -5
View File
@@ -515,8 +515,7 @@ impl Membership {
"limitCollectionDeletion": true,
"limitItemDeletion": false,
"allowAdminAccessToAllCollectionItems": true,
"userIsManagedByOrganization": false, // Means not managed via the Members UI, like SSO
"userIsClaimedByOrganization": false, // The new key instead of the obsolete userIsManagedByOrganization
"userIsClaimedByOrganization": false, // Means not managed via the Members UI, like SSO
"permissions": permissions,
@@ -626,7 +625,6 @@ impl Membership {
"status": status,
"type": membership_type,
"accessAll": self.access_all,
"twoFactorEnabled": twofactor_enabled,
"resetPasswordEnrolled": self.reset_password_key.is_some(),
"hasMasterPassword": !user.password_hash.is_empty(),
@@ -634,7 +632,6 @@ impl Membership {
"permissions": permissions,
"ssoBound": false, // Not supported
"managedByOrganization": false, // This key is obsolete replaced by claimedByOrganization
"claimedByOrganization": false, // Means not managed via the Members UI, like SSO
"usesKeyConnector": false, // Not supported
"accessSecretsManager": false, // Not supported (Not AGPLv3 Licensed)
@@ -685,7 +682,6 @@ impl Membership {
"status": status,
"type": self.atype,
"accessAll": self.access_all,
"collections": coll_uuids,
"object": "organizationUserDetails",
@@ -1028,6 +1024,7 @@ impl Membership {
conn.run(move |conn| {
users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid))
.filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32))
.left_join(users_collections::table.on(users_collections::user_uuid.eq(users_organizations::user_uuid)))
.left_join(
ciphers_collections::table.on(ciphers_collections::collection_uuid
@@ -1054,6 +1051,7 @@ impl Membership {
conn.run(move |conn| {
users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid))
.filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32))
.inner_join(
groups_users::table.on(groups_users::users_organizations_uuid.eq(users_organizations::uuid)),
)