diff --git a/keystone/identity/shadow_backends/sql.py b/keystone/identity/shadow_backends/sql.py
index e1cb186551..a91902b1c7 100644
--- a/keystone/identity/shadow_backends/sql.py
+++ b/keystone/identity/shadow_backends/sql.py
@@ -158,7 +158,8 @@ class ShadowUsers(base.ShadowUsersDriverBase):
         if CONF.security_compliance.disable_user_account_days_inactive:
             with sql.session_for_write() as session:
                 user_ref = session.get(model.User, user_id)
-                user_ref.last_active_at = datetime.datetime.utcnow().date()
+                if user_ref:
+                    user_ref.last_active_at = datetime.datetime.utcnow().date()
 
     @sql.handle_conflicts(conflict_type='federated_user')
     def update_federated_user_display_name(self, idp_id, protocol_id,
diff --git a/keystone/tests/unit/identity/shadow_users/test_backend.py b/keystone/tests/unit/identity/shadow_users/test_backend.py
index f929f29eb4..38300443ce 100644
--- a/keystone/tests/unit/identity/shadow_users/test_backend.py
+++ b/keystone/tests/unit/identity/shadow_users/test_backend.py
@@ -11,6 +11,7 @@
 # under the License.
 
 import datetime
+from unittest import mock
 import uuid
 
 from keystone.common import provider_api
@@ -18,6 +19,7 @@ from keystone.common import sql
 import keystone.conf
 from keystone import exception
 from keystone.identity.backends import sql_model as model
+from keystone.identity.shadow_backends import sql as shadow_sql
 from keystone.tests import unit
 
 
@@ -128,6 +130,30 @@ class ShadowUsersBackendTests(object):
         user_ref = self._get_user_ref(user_auth['id'])
         self.assertGreaterEqual(now, user_ref.last_active_at)
 
+    def test_set_last_active_at_on_non_existing_user(self):
+        self.config_fixture.config(group='security_compliance',
+                                   disable_user_account_days_inactive=90)
+        password = uuid.uuid4().hex
+        user = self._create_user(password)
+
+        # the user can be deleted while authentication is running; to imitate
+        # this, set_last_active_at is mocked to delete the user and then run
+        # normally
+        real_last_active_at = shadow_sql.ShadowUsers.set_last_active_at
+        test_self = self
+
+        def fake_last_active_at(self, user_id):
+            test_self._delete_user(user_id)
+            real_last_active_at(self, user_id)
+
+        with mock.patch.object(shadow_sql.ShadowUsers, 'set_last_active_at',
+                               fake_last_active_at):
+            with self.make_request():
+                # the call is expected to just succeed without exceptions
+                PROVIDERS.identity_api.authenticate(
+                    user_id=user['id'],
+                    password=password)
+
     def test_set_last_active_at_when_config_setting_is_none(self):
         self.config_fixture.config(group='security_compliance',
                                    disable_user_account_days_inactive=None)
@@ -154,6 +180,9 @@ class ShadowUsersBackendTests(object):
         }
         return PROVIDERS.identity_api.create_user(user)
 
+    def _delete_user(self, user_id):
+        return PROVIDERS.identity_api.delete_user(user_id)
+
     def _get_user_ref(self, user_id):
         with sql.session_for_read() as session:
             return session.get(model.User, user_id)