diff --git a/app/models/token.rb b/app/models/token.rb
index b98c6dbf3..d425e2b11 100644
--- a/app/models/token.rb
+++ b/app/models/token.rb
@@ -94,9 +94,15 @@ class Token < ApplicationRecord
 
   # Returns the active user who owns the key for the given action
   def self.find_active_user(action, key, validity_days=nil)
-    user = find_user(action, key, validity_days)
-    if user && user.active?
-      user
+    token = find_token(action, key, validity_days)
+    if token
+      user = token.user
+      if user&.active?
+        if token.updated_on.nil? || token.updated_on <= 1.minute.ago
+          token.update_column(:updated_on, Time.now.utc)
+        end
+        user
+      end
     end
   end
 
diff --git a/app/views/my/_sidebar.html.erb b/app/views/my/_sidebar.html.erb
index 82a22cef2..8bae9ddc0 100644
--- a/app/views/my/_sidebar.html.erb
+++ b/app/views/my/_sidebar.html.erb
@@ -38,9 +38,14 @@
   <%= javascript_tag("$('#api-access-key').hide();") %>
   <p>
   <% if @user.api_token %>
-  <%= l(:label_api_access_key_created_on, distance_of_time_in_words(Time.now, @user.api_token.created_on)) %>
+    <%= l(:label_api_access_key_created_on, distance_of_time_in_words(Time.now, @user.api_token.created_on)) %><br />
+    <% if @user.api_token.updated_on > @user.api_token.created_on %>
+      <%= l(:label_api_access_key_last_used_on, distance_of_time_in_words(Time.now, @user.api_token.updated_on)) %>
+    <% else %>
+      <%= l(:label_api_access_key_never_used) %>
+    <% end %>
   <% else %>
-  <%= l(:label_missing_api_access_key) %>
+    <%= l(:label_missing_api_access_key) %>
   <% end %>
   (<%= link_to l(:button_reset), my_api_key_path, :method => :post %>)
   </p>
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 2b3a6042a..69f9ef211 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -434,6 +434,8 @@ de:
   label_any_issues_not_in_project: irgendein Ticket nicht im Projekt
   label_api_access_key: API-Zugriffsschlüssel
   label_api_access_key_created_on: Der API-Zugriffsschlüssel wurde vor %{value} erstellt
+  label_api_access_key_last_used_on: "Zuletzt verwendet: vor %{value}"
+  label_api_access_key_never_used: Nie verwendet
   label_applied_status: Zugewiesener Status
   label_ascending: Aufsteigend
   label_ask: Nachfragen
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 90e67bfd8..b97471e90 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1035,6 +1035,8 @@ en:
   label_api_access_key: API access key
   label_missing_api_access_key: Missing an API access key
   label_api_access_key_created_on: "API access key created %{value} ago"
+  label_api_access_key_last_used_on: "Last used: %{value} ago"
+  label_api_access_key_never_used: Never used
   label_profile: Profile
   label_subtask: Subtask
   label_subtask_plural: Subtasks
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 014a47072..74df46e58 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -921,6 +921,8 @@ fr:
   label_api_access_key: Clé d'accès API
   label_missing_api_access_key: Clé d'accès API manquante
   label_api_access_key_created_on: Clé d'accès API créée il y a %{value}
+  label_api_access_key_last_used_on: "Dernier usage : il y a %{value}"
+  label_api_access_key_never_used: Jamais utilisée
   label_profile: Profil
   label_subtask_plural: Sous-tâches
   label_project_copy_notifications: Envoyer les notifications durant la copie du projet
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index 51fc2c164..4cfa02716 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -806,6 +806,8 @@ ja:
   label_api_access_key: APIアクセスキー
   label_missing_api_access_key: APIアクセスキーが見つかりません
   label_api_access_key_created_on: "APIアクセスキーは%{value}前に作成されました"
+  label_api_access_key_last_used_on: "最終使用：%{value}前"
+  label_api_access_key_never_used: 未使用
   label_subtask_plural: 子チケット
   label_project_copy_notifications: コピーしたチケットのメール通知を送信する
   label_principal_search: "ユーザーまたはグループの検索:"
diff --git a/test/integration/api_test/authentication_test.rb b/test/integration/api_test/authentication_test.rb
index 4145fb969..b1b28d20c 100644
--- a/test/integration/api_test/authentication_test.rb
+++ b/test/integration/api_test/authentication_test.rb
@@ -161,4 +161,40 @@ class Redmine::ApiTest::AuthenticationTest < Redmine::ApiTest::Base
     assert_response :success
     assert_select 'h2', :text => "#{user.initials} #{user.name}"
   end
+
+  def test_api_key_usage_via_header_should_update_updated_on
+    user = User.generate!
+    token = Token.create!(:user => user, :action => 'api', :updated_on => 2.minutes.ago)
+    updated = token.updated_on
+    get '/users/current.xml', :headers => {'X-Redmine-API-Key' => token.value}
+    assert_response :ok
+    assert token.reload.updated_on > updated
+  end
+
+  def test_api_key_usage_via_parameter_should_update_updated_on
+    user = User.generate!
+    token = Token.create!(:user => user, :action => 'api', :updated_on => 2.minutes.ago)
+    updated = token.updated_on
+    get "/users/current.xml?key=#{token.value}"
+    assert_response :ok
+    assert token.reload.updated_on > updated
+  end
+
+  def test_api_key_usage_via_basic_auth_should_update_updated_on
+    user = User.generate!
+    token = Token.create!(:user => user, :action => 'api', :updated_on => 2.minutes.ago)
+    updated = token.updated_on
+    get '/users/current.xml', :headers => credentials(token.value, 'X')
+    assert_response :ok
+    assert token.reload.updated_on > updated
+  end
+
+  def test_failed_api_auth_should_not_update_updated_on
+    user = User.generate!
+    token = Token.create!(:user => user, :action => 'api', :updated_on => 2.minutes.ago)
+    updated = token.updated_on
+    get '/users/current.xml', :headers => {'X-Redmine-API-Key' => 'wrong_key'}
+    assert_response :unauthorized
+    assert_equal updated.to_i, token.reload.updated_on.to_i
+  end
 end
diff --git a/test/unit/token_test.rb b/test/unit/token_test.rb
index d40b3a2a1..754f0ad04 100644
--- a/test/unit/token_test.rb
+++ b/test/unit/token_test.rb
@@ -137,4 +137,25 @@ class TokenTest < ActiveSupport::TestCase
     token = Token.create!(:user_id => 999, :action => 'api', :created_on => 2.days.ago)
     assert_nil Token.find_token('api', token.value, 1)
   end
+
+  def test_find_active_user_should_bump_updated_on_when_not_recently_updated
+    token = Token.create!(:user_id => 1, :action => 'api', :updated_on => 2.minutes.ago)
+    updated = token.updated_on
+    Token.find_active_user('api', token.value)
+    assert token.reload.updated_on > updated
+  end
+
+  def test_find_active_user_should_not_bump_updated_on_within_one_minute
+    token = Token.create!(:user_id => 1, :action => 'api', :updated_on => 1.second.ago)
+    updated = token.reload.updated_on
+    Token.find_active_user('api', token.value)
+    assert_equal updated.to_i, token.reload.updated_on.to_i
+  end
+
+  def test_find_active_user_should_bump_updated_on_for_feeds_token
+    token = Token.create!(:user_id => 1, :action => 'feeds', :updated_on => 2.minutes.ago)
+    updated = token.updated_on
+    Token.find_active_user('feeds', token.value)
+    assert token.reload.updated_on > updated
+  end
 end
