diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 693970d14a4..e524c232d9a 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -502,7 +502,14 @@ class ApplicationController < ActionController::Base
       result.find_by(find_opts)
     elsif params[:external_id]
       external_id = params[:external_id].chomp('.json')
-      SingleSignOnRecord.find_by(external_id: external_id).try(:user)
+      if provider_name = params[:external_provider]
+        raise Discourse::InvalidAccess unless guardian.is_admin? # external_id might be something sensitive
+        provider = Discourse.enabled_authenticators.find { |a| a.name == provider_name }
+        raise Discourse::NotFound if !provider&.is_managed? # Only managed authenticators use UserAssociatedAccount
+        UserAssociatedAccount.find_by(provider_name: provider_name, provider_uid: external_id)&.user
+      else
+        SingleSignOnRecord.find_by(external_id: external_id).try(:user)
+      end
     end
     raise Discourse::NotFound if user.blank?
 
diff --git a/config/routes.rb b/config/routes.rb
index 6787eb30528..b9df3de5516 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -492,6 +492,7 @@ Discourse::Application.routes.draw do
       get "#{root_path}/:username/notifications/:filter" => "users#show", constraints: { username: RouteFormat.username }
       delete "#{root_path}/:username" => "users#destroy", constraints: { username: RouteFormat.username }
       get "#{root_path}/by-external/:external_id" => "users#show", constraints: { external_id: /[^\/]+/ }
+      get "#{root_path}/by-external/:external_provider/:external_id" => "users#show", constraints: { external_id: /[^\/]+/ }
       get "#{root_path}/:username/flagged-posts" => "users#show", constraints: { username: RouteFormat.username }
       get "#{root_path}/:username/deleted-posts" => "users#show", constraints: { username: RouteFormat.username }
       get "#{root_path}/:username/topic-tracking-state" => "users#topic_tracking_state", constraints: { username: RouteFormat.username }
diff --git a/lib/auth/managed_authenticator.rb b/lib/auth/managed_authenticator.rb
index 8cb182094a9..b0477c5f087 100644
--- a/lib/auth/managed_authenticator.rb
+++ b/lib/auth/managed_authenticator.rb
@@ -1,6 +1,12 @@
 # frozen_string_literal: true
 
 class Auth::ManagedAuthenticator < Auth::Authenticator
+  def is_managed?
+    # Tells core that it can safely assume this authenticator
+    # uses UserAssociatedAccount
+    true
+  end
+
   def description_for_user(user)
     associated_account = UserAssociatedAccount.find_by(provider_name: name, user_id: user.id)
     return "" if associated_account.nil?
diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb
index b455b0dc2f9..261bd59d793 100644
--- a/spec/requests/users_controller_spec.rb
+++ b/spec/requests/users_controller_spec.rb
@@ -3246,6 +3246,37 @@ describe UsersController do
           get "/u/by-external/99.json"
           expect(response).not_to be_successful
         end
+
+        context "for an external provider" do
+          before do
+            sign_in(Fabricate(:admin))
+            SiteSetting.enable_google_oauth2_logins = true
+            UserAssociatedAccount.create!(user: user, provider_uid: 'myuid', provider_name: 'google_oauth2')
+          end
+
+          it "doesn't work for non-admin" do
+            sign_in(user)
+            get "/u/by-external/google_oauth2/myuid.json"
+            expect(response.status).to eq(403)
+          end
+
+          it "can fetch the user" do
+            get "/u/by-external/google_oauth2/myuid.json"
+            expect(response.status).to eq(200)
+            expect(response.parsed_body["user"]["username"]).to eq(user.username)
+          end
+
+          it "fails for disabled provider" do
+            SiteSetting.enable_google_oauth2_logins = false
+            get "/u/by-external/google_oauth2/myuid.json"
+            expect(response.status).to eq(404)
+          end
+
+          it "returns 404 for missing user" do
+            get "/u/by-external/google_oauth2/myotheruid.json"
+            expect(response.status).to eq(404)
+          end
+        end
       end
 
       describe "include_post_count_for" do