diff --git a/pallets/identity/src/lib.rs b/pallets/identity/src/lib.rs
index 567ebc9dfef3b06123aeb25e79dd2c52b8d286da..1a755d6fef7a8943d789976050e013489e6e1636 100644
--- a/pallets/identity/src/lib.rs
+++ b/pallets/identity/src/lib.rs
@@ -36,7 +36,7 @@ use crate::traits::*;
 use codec::Codec;
 use frame_support::dispatch::Weight;
 use frame_system::RawOrigin;
-use sp_runtime::traits::{AtLeast32BitUnsigned, One, Saturating, Zero};
+use sp_runtime::traits::{AtLeast32BitUnsigned, IdentifyAccount, One, Saturating, Zero};
 use sp_std::fmt::Debug;
 use sp_std::prelude::*;
 
@@ -45,6 +45,7 @@ pub mod pallet {
     use super::*;
     use frame_support::pallet_prelude::*;
     use frame_support::traits::StorageVersion;
+    use frame_system::offchain::SignedPayload;
     use frame_system::pallet_prelude::*;
     use sp_membership::traits::MembershipAction as _;
 
@@ -59,7 +60,7 @@ pub mod pallet {
     // CONFIG //
 
     #[pallet::config]
-    pub trait Config: frame_system::Config {
+    pub trait Config: frame_system::Config + frame_system::offchain::SigningTypes {
         #[pallet::constant]
         /// Period during which the owner can confirm the new identity.
         type ConfirmPeriod: Get<Self::BlockNumber>;
@@ -251,7 +252,11 @@ pub mod pallet {
     // These functions materialize as "extrinsics", which are often compared to transactions.
     // Dispatchable functions must be annotated with a weight and must return a DispatchResult.
     #[pallet::call]
-    impl<T: Config> Pallet<T> {
+    impl<T: Config> Pallet<T>
+    where
+        <T as frame_system::offchain::SigningTypes>::Public:
+            IdentifyAccount<AccountId = T::AccountId>,
+    {
         #[pallet::weight(0)]
         pub fn create_identity(
             origin: OriginFor<T>,
@@ -549,6 +554,65 @@ pub mod pallet {
             }
         }
         #[pallet::weight(0)]
+        pub fn revoke_identity(
+            origin: OriginFor<T>,
+            payload: RevocationPayload<
+                <T as frame_system::offchain::SigningTypes>::Public,
+                T::IdtyIndex,
+            >,
+            payload_sig: <T as frame_system::offchain::SigningTypes>::Signature,
+        ) -> DispatchResultWithPostInfo {
+            let who = ensure_signed(origin)?;
+            if !payload.verify(payload_sig) {
+                return Err(Error::<T>::BadProof.into());
+            }
+            let idty_index = payload.idty;
+
+            if let Ok(mut idty_value) = <Identities<T>>::try_get(idty_index) {
+                if idty_value.owner_key != payload.owner_key.into_account() {
+                    return Err(Error::<T>::RequireToBeOwner.into());
+                }
+                if idty_value.status != IdtyStatus::Validated {
+                    return Err(Error::<T>::IdtyNotValidated.into());
+                }
+
+                for (right, subkey_opt) in idty_value.rights.drain(..) {
+                    let name = idty_value.name.clone();
+
+                    let old_key_opt = if let Some(ref subkey) = subkey_opt {
+                        Some(subkey.clone())
+                    } else if right.allow_owner_key() {
+                        Some(idty_value.owner_key.clone())
+                    } else {
+                        None
+                    };
+
+                    Self::deposit_event(Event::<T>::IdtyLostRight(name, right));
+                    if old_key_opt.is_some() {
+                        T::OnRightKeyChange::on_right_key_change(
+                            idty_index,
+                            right,
+                            old_key_opt,
+                            None,
+                        );
+                    }
+                }
+
+                let block_number = frame_system::pallet::Pallet::<T>::block_number();
+                let removable_on = block_number + T::MaxNoRightPeriod::get();
+                idty_value.removable_on = removable_on;
+                <IdentitiesRemovableOn<T>>::append(
+                    removable_on,
+                    (idty_index, IdtyStatus::Validated),
+                );
+                <Identities<T>>::insert(idty_index, idty_value);
+
+                Ok(().into())
+            } else {
+                Err(Error::<T>::IdtyNotFound.into())
+            }
+        }
+        #[pallet::weight(0)]
         pub fn set_right_subkey(
             origin: OriginFor<T>,
             idty_index: T::IdtyIndex,
@@ -608,6 +672,7 @@ pub mod pallet {
 
     #[pallet::error]
     pub enum Error<T> {
+        BadProof,
         /// Creator not exist
         CreatorNotExist,
         /// Creator not allowed to create identities
diff --git a/pallets/identity/src/tests.rs b/pallets/identity/src/tests.rs
index 264b324c27088c4e643ef6aea9ceb9fe1aaaa374..eb4f386e114cc7aafbaf7beec4292bcf83f6d408 100644
--- a/pallets/identity/src/tests.rs
+++ b/pallets/identity/src/tests.rs
@@ -247,3 +247,58 @@ fn test_two_identities() {
         assert_eq!(idty2.removable_on, 7);
     });
 }
+
+#[test]
+fn test_revoke_identity() {
+    let identities = vec![crate::IdtyValue {
+        name: IdtyName(vec![0]),
+        expire_on: 5,
+        owner_key: 1,
+        removable_on: 0,
+        renewable_on: 3,
+        rights: vec![(Right::Right1, None), (Right::Right2, Some(10))],
+        status: crate::IdtyStatus::Validated,
+        data: (),
+    }];
+
+    new_test_ext(IdentityConfig { identities }).execute_with(|| {
+        // Should have one identity
+        assert_eq!(Identity::identities_count(), 1);
+
+        // We need to initialize at least one block before any call
+        run_to_block(1);
+
+        // Delete right Right1 for IdtyName(vec![1])
+        // Should succes and trigger the correct event
+        assert_ok!(Identity::revoke_identity(Origin::root(), 1));
+        let events = System::events();
+        assert_eq!(events.len(), 2);
+        assert_eq!(
+            events[0],
+            EventRecord {
+                phase: Phase::Initialization,
+                event: Event::Identity(crate::Event::IdtyLostRight(
+                    IdtyName(vec![0]),
+                    Right::Right1
+                )),
+                topics: vec![],
+            }
+        );
+        assert_eq!(
+            events[1],
+            EventRecord {
+                phase: Phase::Initialization,
+                event: Event::Identity(crate::Event::IdtyLostRight(
+                    IdtyName(vec![0]),
+                    Right::Right2
+                )),
+                topics: vec![],
+            }
+        );
+
+        // The identity has no more rights, the inactivity period must start to run
+        let idty = Identity::identity(1).expect("idty not found");
+        assert!(idty.rights.is_empty());
+        assert_eq!(idty.removable_on, 5);
+    });
+}
diff --git a/pallets/identity/src/types.rs b/pallets/identity/src/types.rs
index b4231157bae636c39221a8d7f7bd10c305c83912..d7d59a687ea3b5db6f7fc9555e05ab4308c6bae9 100644
--- a/pallets/identity/src/types.rs
+++ b/pallets/identity/src/types.rs
@@ -113,3 +113,21 @@ where
         }
     }
 }
+
+#[derive(Clone, Encode, Decode, PartialEq, Eq, TypeInfo, RuntimeDebug)]
+pub struct RevocationPayload<
+    AccountId: Decode + Encode + TypeInfo,
+    IdtyIndex: Decode + Encode + TypeInfo,
+> {
+    pub owner_key: AccountId,
+    pub idty: IdtyIndex,
+}
+
+impl<T: frame_system::offchain::SigningTypes, IdtyIndex: Decode + Encode + TypeInfo>
+    frame_system::offchain::SignedPayload<T>
+    for RevocationPayload<<T as frame_system::offchain::SigningTypes>::Public, IdtyIndex>
+{
+    fn public(&self) -> <T as frame_system::offchain::SigningTypes>::Public {
+        self.owner_key.clone()
+    }
+}
diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs
index 8fb5add3976024f04e99472b2ee4a8cb777bb506..3bd61aacf2832d03c06dd23a6df690be485dac7a 100644
--- a/runtime/common/src/pallets_config.rs
+++ b/runtime/common/src/pallets_config.rs
@@ -202,6 +202,10 @@ macro_rules! pallets_config {
             type MaxNoRightPeriod = MaxNoRightPeriod;
 			type Membership = Membership;
         }
+        impl SigningTypes for Runtime {
+            type Public = <Signature as Verify>::Signer;
+            type Signature = Signature;
+        }
 
 		impl pallet_membership::Config<frame_support::instances::Instance1> for Runtime {
 			type IsIdtyAllowedToClaimMembership = ();