diff --git a/resources/icons/ui/refresh.png b/resources/icons/ui/refresh.png
new file mode 100644
index 0000000000000000000000000000000000000000..642682032ab6d6ff73b0769bd2c420e89281d8a9
--- /dev/null
+++ b/resources/icons/ui/refresh.png
@@ -0,0 +1,6 @@
+‰PNG
+
+���
IHDR���@���@���ªiqÞ���	pHYs������rçn���tEXtSoftware�www.inkscape.org›î<��fIDATxœíÛO¨UUÇñÏõ)ùÊY”¤Yf`Ñrâ ÈI4h`“jPTHƒÄH‰ µ‚Ê(Ò0I¡À¢?Ð$5eÑÀHld†¾²’|¥ñÌ÷ž
ö½ê»ïþ9ûœ³¯÷Áý‚÷àìu×oÝskí³vEZúp=nÃÍX€y¸S0#8Ž¿ñ;àG|‡=8–8ÆÒ™ŠåxàtÁ><+$²«Yˆ8ª˜èVö-Ââ\„;qIc¸Û1,ðzÀjôGÄYÁË­ú8Š¥¹W™Šç0T²¸;„{«âÚ‰ßÒ`ý¾¼â—«ó%¼Þvcnño6Y7+¼‚•8Ù¢ëíOÜSï$lk³.3“±µ„¶²Q¬¾¨f·}®LÃg] 0«mÇ[¯mK?>ïQIlrñSð1îÈ’©Œü+<@«6Œé¸T¨g”øY…ÉòjgCøD(`åq+æá>¼+”ǩ<ZÐñ�žÀ¬6‚[яGðC§°H¸Uó8ü«„B©,ú°BñÞ"SúðMNg;qUyºÇq¹ðsJš€Çr:Ú$`¥Ð)–ž€™BOã`¤PÙ‚Šòê’1<¹xTxHu’
+6GÆ™)ÓÄ÷òëR©lBÙâÇ$àñÈ…_èÜož4âÇ$àûˆEƒBÁÒ)R‰?“€["½Në8*x=2¾èlˆXpRó͇¬‰ˆ-wbnÿ·Óimȯ±åJÀg7
+³ØMIåŽg "¶X;<	Wj¿©Xc'ö— *†×úÞX¾Fx{ÓŠcÂÃïP€š±wᢒüÀ|T’¿=zôèÑ£GŽZ)œ¢Ô܉KòÃÕxZØàmƈðõS¨Iªnku™Ê2²#cl‡kRöÛ¿%Úˆdoí÷65ç$h$¡ïF<){k¿»öGÊÝ–5Å0WÜèΙö?•ø­²eðRDl΍-…øÍ:+~¾°UŸ5¾µç.žèâûðeD|§Ô½Ó˜È≟ù^½ƒ‰,~EdŒ£¸µÞIâ¿ÒyñŠŸO~¿‘£²î€MÚ@•Á$¼˜#¾!¡LG™Ï€¯…éñTÌ—0bíxw2pZ8ý±–£aàj•0€•'¦½Â¨oCÊN@ÍŽà\V@øÅBCU¤_Ôæ´IªÔìv	ÃW‹µª˜ŒñpuÍ?{w·_©^ØI†pPøVÎYU[¨ÜùÂÕx¥ÝE©ï€óeë³f)‹³-ø D•.>K¶9{�a}ˆkeÃBuE+‡o_áÝ/®óê”
`Y¬xš—“Ä×X ÿ<q
+Û%Ìçb_¤ø}ÂH}ìhm™vD8[Pˆ¥Â‰«ÓB·´A\c3K˜ù«AYmP8N[ä,ÂfÇI¯+àcºp@"åyŸðT5Þ®f	^UÎIƒB§¹L‚ÑÜNôðsp»0^w­PíÍGÍÊÕBU8€_ð³ÐÀì­þŸŒÿ©žÉ¾æ¯����IEND®B`‚
\ No newline at end of file
diff --git a/resources/icons/ui/refresh.svg b/resources/icons/ui/refresh.svg
new file mode 100644
index 0000000000000000000000000000000000000000..17c41496b761b090e2df9e1c8d5e4aa5bed042e6
--- /dev/null
+++ b/resources/icons/ui/refresh.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   version="1.1"
+   viewBox="-10 0 1792 1792"
+   id="svg866"
+   width="1792"
+   height="1792"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <defs
+     id="defs870" />
+  <path
+     fill="currentColor"
+     d="m 1629,1056 q 0,5 -1,7 -64,268 -268,434.5 Q 1156,1664 882,1664 736,1664 599.5,1609 463,1554 356,1452 l -129,129 q -19,19 -45,19 -26,0 -45,-19 -19,-19 -19,-45 v -448 q 0,-26 19,-45 19,-19 45,-19 h 448 q 26,0 45,19 19,19 19,45 0,26 -19,45 l -137,137 q 71,66 161,102 90,36 187,36 134,0 250,-65 116,-65 186,-179 11,-17 53,-117 8,-23 30,-23 h 192 q 13,0 22.5,9.5 9.5,9.5 9.5,22.5 z m 25,-800 v 448 q 0,26 -19,45 -19,19 -45,19 h -448 q -26,0 -45,-19 -19,-19 -19,-45 0,-26 19,-45 L 1235,521 Q 1087,384 886,384 q -134,0 -250,65 -116,65 -186,179 -11,17 -53,117 -8,23 -30,23 H 168 q -13,0 -22.5,-9.5 Q 136,749 136,736 v -7 Q 201,461 406,294.5 611,128 886,128 q 146,0 284,55.5 138,55.5 245,156.5 l 130,-129 q 19,-19 45,-19 26,0 45,19 19,19 19,45 z"
+     id="path864" />
+</svg>
diff --git a/resources/qml/dialogs/UserProfile.qml b/resources/qml/dialogs/UserProfile.qml
index 9bf548e3cfdafa595a66bed275c169c1b1ae2799..d5442382f5b24692bb277e87522ef77db41ffc35 100644
--- a/resources/qml/dialogs/UserProfile.qml
+++ b/resources/qml/dialogs/UserProfile.qml
@@ -249,6 +249,14 @@ ApplicationWindow {
                     visible: !profile.isGlobalUserProfile && profile.room.permissions.canBan()
                 }
 
+                ImageButton {
+                    image: ":/icons/icons/ui/refresh.png"
+                    hoverEnabled: true
+                    ToolTip.visible: hovered
+                    ToolTip.text: qsTr("Refresh device list.")
+                    onClicked: profile.refreshDevices();
+                }
+
             }
         }
 
@@ -264,6 +272,9 @@ ApplicationWindow {
 
 
         delegate: RowLayout {
+            required property int verificationStatus
+            required property string deviceId
+            required property string deviceName
             width: devicelist.width
             spacing: 4
 
@@ -276,7 +287,7 @@ ApplicationWindow {
                     elide: Text.ElideRight
                     font.bold: true
                     color: Nheko.colors.text
-                    text: model.deviceId
+                    text: deviceId
                 }
 
                 Text {
@@ -284,7 +295,7 @@ ApplicationWindow {
                     Layout.alignment: Qt.AlignRight
                     elide: Text.ElideRight
                     color: Nheko.colors.text
-                    text: model.deviceName
+                    text: deviceName
                 }
 
             }
@@ -292,19 +303,30 @@ ApplicationWindow {
             Image {
                 Layout.preferredHeight: 16
                 Layout.preferredWidth: 16
-                source: ((model.verificationStatus == VerificationStatus.VERIFIED) ? "image://colorimage/:/icons/icons/ui/lock.png?green" : ((model.verificationStatus == VerificationStatus.UNVERIFIED) ? "image://colorimage/:/icons/icons/ui/unlock.png?yellow" : "image://colorimage/:/icons/icons/ui/unlock.png?red"))
+                source: {
+                    switch (verificationStatus){
+                    case VerificationStatus.VERIFIED:
+                        return "image://colorimage/:/icons/icons/ui/lock.png?green";
+                    case VerificationStatus.UNVERIFIED:
+                        return "image://colorimage/:/icons/icons/ui/unlock.png?yellow";
+                    case VerificationStatus.SELF:
+                        return "image://colorimage/:/icons/icons/ui/checkmark.png?green";
+                    default:
+                        return "image://colorimage/:/icons/icons/ui/unlock.png?red";
+                    }
+                }
             }
 
             Button {
                 id: verifyButton
 
-                visible: (!profile.userVerificationEnabled && !profile.isSelf) || (profile.isSelf && (model.verificationStatus != VerificationStatus.VERIFIED || !profile.userVerificationEnabled))
-                text: (model.verificationStatus != VerificationStatus.VERIFIED) ? qsTr("Verify") : qsTr("Unverify")
+                visible: verificationStatus == VerificationStatus.UNVERIFIED && (profile.isSelf || !profile.userVerificationEnabled)
+                text: (verificationStatus != VerificationStatus.VERIFIED) ? qsTr("Verify") : qsTr("Unverify")
                 onClicked: {
-                    if (model.verificationStatus == VerificationStatus.VERIFIED)
-                        profile.unverify(model.deviceId);
+                    if (verificationStatus == VerificationStatus.VERIFIED)
+                        profile.unverify(deviceId);
                     else
-                        profile.verify(model.deviceId);
+                        profile.verify(deviceId);
                 }
             }
 
diff --git a/resources/res.qrc b/resources/res.qrc
index a001a92907160afc16b8863d5bc2c76905a7f77b..3bd301f7afc73b49e8508c54c41c6058f096f91d 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -72,6 +72,7 @@
         <file>icons/ui/screen-share.png</file>
         <file>icons/ui/toggle-camera-view.png</file>
         <file>icons/ui/video-call.png</file>
+        <file>icons/ui/refresh.png</file>
         <file>icons/emoji-categories/people.png</file>
         <file>icons/emoji-categories/people@2x.png</file>
         <file>icons/emoji-categories/nature.png</file>
diff --git a/src/Cache.cpp b/src/Cache.cpp
index b124fe5e567f7d2cf62ad017acf88052650a2289..ee0ca0c22f3f9ed7f3da1131bc92fa58c9835cf8 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -4045,6 +4045,16 @@ Cache::updateUserKeys(const std::string &sync_token, const mtx::responses::Query
     }
 }
 
+void
+Cache::markUserKeysOutOfDate(const std::vector<std::string> &user_ids)
+{
+    auto currentBatchToken = nextBatchToken();
+    auto txn               = lmdb::txn::begin(env_);
+    auto db                = getUserKeysDb(txn);
+    markUserKeysOutOfDate(txn, db, user_ids, currentBatchToken);
+    txn.commit();
+}
+
 void
 Cache::markUserKeysOutOfDate(lmdb::txn &txn,
                              lmdb::dbi &db,
diff --git a/src/Cache_p.h b/src/Cache_p.h
index a15010e6db834a267ac86e83cff5799d98055671..52375d38774cf04a5616c08a9c70095a9425da57 100644
--- a/src/Cache_p.h
+++ b/src/Cache_p.h
@@ -50,6 +50,7 @@ public:
       const std::string &room_id,
       bool verified_only);
     void updateUserKeys(const std::string &sync_token, const mtx::responses::QueryKeys &keyQuery);
+    void markUserKeysOutOfDate(const std::vector<std::string> &user_ids);
     void markUserKeysOutOfDate(lmdb::txn &txn,
                                lmdb::dbi &db,
                                const std::vector<std::string> &user_ids,
diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp
index 58150ed799f4840a31425c786bd95f03d60c9aba..31ae9f8b83561be7d67ff4d209eadf248e19f989 100644
--- a/src/ui/UserProfile.cpp
+++ b/src/ui/UserProfile.cpp
@@ -34,6 +34,7 @@ UserProfile::UserProfile(QString roomid,
             this,
             &UserProfile::setGlobalUsername,
             Qt::QueuedConnection);
+    connect(this, &UserProfile::verificationStatiChanged, &UserProfile::updateVerificationStatus);
 
     if (isGlobalUserProfile()) {
         getGlobalProfileData();
@@ -48,22 +49,7 @@ UserProfile::UserProfile(QString roomid,
           if (user_id != this->userid_.toStdString())
               return;
 
-          auto status = cache::verificationStatus(user_id);
-          if (!status)
-              return;
-          this->isUserVerified = status->user_verified;
-          emit userStatusChanged();
-
-          for (auto &deviceInfo : deviceList_.deviceList_) {
-              deviceInfo.verification_status =
-                std::find(status->verified_devices.begin(),
-                          status->verified_devices.end(),
-                          deviceInfo.device_id.toStdString()) == status->verified_devices.end()
-                  ? verification::UNVERIFIED
-                  : verification::VERIFIED;
-          }
-          deviceList_.reset(deviceList_.deviceList_);
-          emit devicesChanged();
+          emit verificationStatiChanged();
       });
     fetchDeviceList(this->userid_);
 }
@@ -151,6 +137,13 @@ UserProfile::isSelf() const
     return this->userid_ == utils::localUser();
 }
 
+void
+UserProfile::refreshDevices()
+{
+    cache::client()->markUserKeysOutOfDate({this->userid_.toStdString()});
+    fetchDeviceList(this->userid_);
+}
+
 void
 UserProfile::fetchDeviceList(const QString &userID)
 {
@@ -161,20 +154,18 @@ UserProfile::fetchDeviceList(const QString &userID)
 
     cache::client()->query_keys(
       userID.toStdString(),
-      [other_user_id = userID.toStdString(), this](const UserKeyCache &other_user_keys,
+      [other_user_id = userID.toStdString(), this](const UserKeyCache &,
                                                    mtx::http::RequestErr err) {
           if (err) {
               nhlog::net()->warn("failed to query device keys: {},{}",
                                  mtx::errors::to_string(err->matrix_error.errcode),
                                  static_cast<int>(err->status_code));
-              return;
           }
 
           // Ensure local key cache is up to date
           cache::client()->query_keys(
             utils::localUser().toStdString(),
-            [other_user_id, other_user_keys, this](const UserKeyCache &,
-                                                   mtx::http::RequestErr err) {
+            [this](const UserKeyCache &, mtx::http::RequestErr err) {
                 using namespace mtx;
                 std::string local_user_id = utils::localUser().toStdString();
 
@@ -182,39 +173,56 @@ UserProfile::fetchDeviceList(const QString &userID)
                     nhlog::net()->warn("failed to query device keys: {},{}",
                                        mtx::errors::to_string(err->matrix_error.errcode),
                                        static_cast<int>(err->status_code));
-                    return;
                 }
 
-                this->hasMasterKey = !other_user_keys.master_keys.keys.empty();
+                emit verificationStatiChanged();
+            });
+      });
+}
 
-                std::vector<DeviceInfo> deviceInfo;
-                auto devices            = other_user_keys.device_keys;
-                auto verificationStatus = cache::client()->verificationStatus(other_user_id);
+void
+UserProfile::updateVerificationStatus()
+{
+    if (!cache::client() || !cache::client()->isDatabaseReady())
+        return;
 
-                isUserVerified = verificationStatus.user_verified;
-                emit userStatusChanged();
+    auto user_keys = cache::client()->userKeys(userid_.toStdString());
+    if (!user_keys) {
+        this->hasMasterKey   = false;
+        this->isUserVerified = crypto::Trust::Unverified;
+        this->deviceList_.reset({});
+        emit userStatusChanged();
+        return;
+    }
 
-                for (const auto &d : devices) {
-                    auto device                   = d.second;
-                    verification::Status verified = verification::Status::UNVERIFIED;
+    this->hasMasterKey = !user_keys->master_keys.keys.empty();
 
-                    if (std::find(verificationStatus.verified_devices.begin(),
-                                  verificationStatus.verified_devices.end(),
-                                  device.device_id) != verificationStatus.verified_devices.end() &&
-                        mtx::crypto::verify_identity_signature(
-                          device, DeviceId(device.device_id), UserId(other_user_id)))
-                        verified = verification::Status::VERIFIED;
+    std::vector<DeviceInfo> deviceInfo;
+    auto devices            = user_keys->device_keys;
+    auto verificationStatus = cache::client()->verificationStatus(userid_.toStdString());
 
-                    deviceInfo.push_back(
-                      {QString::fromStdString(d.first),
-                       QString::fromStdString(device.unsigned_info.device_display_name),
-                       verified});
-                }
+    this->isUserVerified = verificationStatus.user_verified;
+    emit userStatusChanged();
 
-                this->deviceList_.queueReset(std::move(deviceInfo));
-                emit devicesChanged();
-            });
-      });
+    for (const auto &d : devices) {
+        auto device = d.second;
+        verification::Status verified =
+          std::find(verificationStatus.verified_devices.begin(),
+                    verificationStatus.verified_devices.end(),
+                    device.device_id) == verificationStatus.verified_devices.end()
+            ? verification::UNVERIFIED
+            : verification::VERIFIED;
+
+        if (isSelf() && device.device_id == ::http::client()->device_id())
+            verified = verification::Status::SELF;
+
+        deviceInfo.push_back({QString::fromStdString(d.first),
+                              QString::fromStdString(device.unsigned_info.device_display_name),
+                              verified});
+    }
+
+    this->deviceList_.queueReset(std::move(deviceInfo));
+    emit devicesChanged();
 }
 
 void
diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h
index a148c431a389a709147c0b8049ccc3e9f2151cfa..68f9c21b30d43bab02aca75f761db9eb4fa5e775 100644
--- a/src/ui/UserProfile.h
+++ b/src/ui/UserProfile.h
@@ -18,6 +18,7 @@ Q_NAMESPACE
 
 enum Status
 {
+    SELF,
     VERIFIED,
     UNVERIFIED,
     BLOCKED
@@ -118,6 +119,7 @@ public:
     Q_INVOKABLE void verify(QString device = "");
     Q_INVOKABLE void unverify(QString device = "");
     Q_INVOKABLE void fetchDeviceList(const QString &userID);
+    Q_INVOKABLE void refreshDevices();
     Q_INVOKABLE void banUser();
     // Q_INVOKABLE void ignoreUser();
     Q_INVOKABLE void kickUser();
@@ -135,11 +137,15 @@ signals:
     void globalUsernameRetrieved(const QString &globalUser);
     void devicesChanged();
 
+    // internal
+    void verificationStatiChanged();
+
 public slots:
     void updateAvatarUrl();
 
-protected slots:
+private slots:
     void setGlobalUsername(const QString &globalUser);
+    void updateVerificationStatus();
 
 private:
     void updateRoomMemberState(mtx::events::state::Member member);