Low chrome Authorization Error 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMissing authorization in SignIn
DescriptionMissing authorization in SignIn
ComponentSignIn
Sub-componentSignin
Bug ClassAuthorization Error
Tracker502111211
Fix commit7e482c0020ad (chromium/src) +134/-8
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-06

Files Changed

  • chrome/browser/extensions/api/identity/identity_apitest.cc
  • chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
From 7e482c0020ad26c962927f2e473ccbcbcdf93706 Mon Sep 17 00:00:00 2001
From: Ernest Nguyen Hung <[email protected]>
Date: Wed, 09 Sep 2026 03:55:03 -0700
Subject: [PATCH] [signin] Update `chrome.identity.getAuthToken` to check for access

When `chrome.identity.getAuthToken` receives an explicit `account.id`
(or has a cached Gaia ID for the extension), resolve the account against
`IdentityAPI::GetAccountsWithRefreshTokensForExtensions` instead of
`IdentityManager` directly.

Fixed: 502111211
Change-Id: I8947eb3bbbe241336012d0160d2884820e27d23a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8378261
Reviewed-by: David Roger <[email protected]>
Commit-Queue: Ernest Nguyen Hung <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1694485}
---
diff --git a/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc b/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
index 3d5d593..c4af70f 100644
--- a/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
+++ b/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "base/check.h"
+#include "base/check_deref.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/location.h"
@@ -357,20 +358,25 @@
     const GaiaId& gaia_id) {
   refresh_tokens_loaded_waiter_.reset();
 
+  IdentityAPI& identity_api = CHECK_DEREF(
+      CHECK_DEREF(IdentityAPI::GetFactoryInstance()).Get(GetProfile()));
+
   selected_gaia_id_ = gaia_id;
   if (gaia_id.empty()) {
-    selected_gaia_id_ = IdentityAPI::GetFactoryInstance()
-                            ->Get(GetProfile())
-                            ->GetGaiaIdForExtension(token_key_.extension_id)
-                            .value_or(GaiaId());
+    selected_gaia_id_ =
+        identity_api.GetGaiaIdForExtension(token_key_.extension_id)
+            .value_or(GaiaId());
   }
 
   CoreAccountInfo selected_account;
   if (!selected_gaia_id_.empty()) {
-    // TODO(msalama): Check has access to accounts.
-    selected_account = IdentityManagerFactory::GetForProfile(GetProfile())
-                           ->FindExtendedAccountInfoByGaiaId(selected_gaia_id_)
-                           .GetCoreAccountInfo();
+    const std::vector<CoreAccountInfo> accounts =
+        identity_api.GetAccountsWithRefreshTokensForExtensions();
+    auto it =
+        std::ranges::find(accounts, selected_gaia_id_, &CoreAccountInfo::gaia);
+    if (it != accounts.end()) {
+      selected_account = *it;
+    }
   } else {
     selected_account = GetSigninPrimaryAccount(GetProfile());
   }
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc
index aa0688b..f1f3b69 100644
--- a/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -1628,6 +1628,126 @@
 }
 
 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
+                       SignedInWebOnlyNonInteractiveWithAccount) {
+  const AccountInfo account_info = identity_test_env()->MakeAccountAvailable(
+      "[email protected]", {.set_cookie = true});
+  ASSERT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount(
+      signin::ConsentLevel::kSignin));
+  ASSERT_FALSE(identity_test_env()
+                   ->identity_manager()
+                   ->GetAccountsWithRefreshTokens()
+                   .empty());
+
+  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
+  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
+  func->set_extension(extension.get());
+
+  const std::string args =
+      base::StringPrintf(R"([{"interactive": false, "account": {"id": "%s"}}])",
+                         account_info.GetGaiaId().ToString().c_str());
+  const std::string error =
+      utils::RunFunctionAndReturnError(func.get(), args, profile());
+  EXPECT_EQ(error, std::string(errors::kUserNotSignedIn));
+  EXPECT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount(
+      signin::ConsentLevel::kSignin));
+
+  EXPECT_FALSE(func->login_ui_shown());
+  EXPECT_FALSE(func->scope_ui_shown());
+  histogram_tester()->ExpectUniqueSample(
+      kGetAuthTokenResultHistogramName,
+      IdentityGetAuthTokenError::State::kUserNotSignedIn, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
+                       SignedInWebOnlyAcceptPromptWithAccount) {
+  const AccountInfo account_info = identity_test_env()->MakeAccountAvailable(
+      "[email protected]", {.set_cookie = true});
+  ASSERT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount(
+      signin::ConsentLevel::kSignin));
+  ASSERT_FALSE(identity_test_env()
+                   ->identity_manager()
+                   ->GetAccountsWithRefreshTokens()
+                   .empty());
+
+  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
+  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
+  func->set_extension(extension.get());
+  func->push_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS);
+
+  views::NamedWidgetShownWaiter widget_waiter(
+      views::test::AnyWidgetTestPasskey{},
+      "ChromeSigninChoiceForExtensionsPrompt");
+
+  const std::string args =
+      base::StringPrintf(R"([{"interactive": true, "account": {"id": "%s"}}])",
+                         account_info.GetGaiaId().ToString().c_str());
+  RunFunctionAsync(func.get(), args);
+  views::Widget* confirmation_prompt = widget_waiter.WaitIfNeededAndGet();
+  ASSERT_NE(confirmation_prompt, nullptr);
+  views::DialogDelegate* dialog_delegate =
+      confirmation_prompt->widget_delegate()->AsDialogDelegate();
+  ASSERT_NE(dialog_delegate, nullptr);
+  dialog_delegate->AcceptDialog();
+
+  std::string access_token;
+  std::set<std::string> granted_scopes;
+  WaitForGetAuthTokenResults(func.get(), &access_token, &granted_scopes);
+  EXPECT_EQ(access_token, std::string(kAccessToken));
+  EXPECT_EQ(granted_scopes, func->GetExtensionTokenKeyForTest()->scopes);
+  EXPECT_EQ(account_info.GetAccountId(),
+            identity_test_env()->identity_manager()->GetPrimaryAccountId(
+                signin::ConsentLevel::kSignin));
+
+  EXPECT_FALSE(func->login_ui_shown());
+  EXPECT_FALSE(func->scope_ui_shown());
+  histogram_tester()->ExpectUniqueSample(
+      kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone,
+      1);
+}
+
+IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
+                       SignedInWebOnlyDeclinePromptWithAccount) {
+  const AccountInfo account_info = identity_test_env()->MakeAccountAvailable(
+      "[email protected]", {.set_cookie = true});
+  ASSERT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount(
+      signin::ConsentLevel::kSignin));
+  ASSERT_FALSE(identity_test_env()
+                   ->identity_manager()
+                   ->GetAccountsWithRefreshTokens()
+                   .empty());
+
+  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
+  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
+  func->set_extension(extension.get());
+  func->push_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS);
+
+  views::NamedWidgetShownWaiter widget_waiter(
+      views::test::AnyWidgetTestPasskey{},
+      "ChromeSigninChoiceForExtensionsPrompt");
+
+  const std::string args =
+      base::StringPrintf(R"([{"interactive": true, "account": {"id": "%s"}}])",
+                         account_info.GetGaiaId().ToString().c_str());
+  RunFunctionAsync(func.get(), args);
+  views::Widget* confirmation_prompt = widget_waiter.WaitIfNeededAndGet();
+  ASSERT_NE(confirmation_prompt, nullptr);
+  views::DialogDelegate* dialog_delegate =
+      confirmation_prompt->widget_delegate()->AsDialogDelegate();
+  ASSERT_NE(dialog_delegate, nullptr);
+  dialog_delegate->CancelDialog();
+
+  EXPECT_EQ(WaitForError(func.get()), std::string(errors::kUserNotSignedIn));
+  EXPECT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount(
+      signin::ConsentLevel::kSignin));
+
+  EXPECT_FALSE(func->login_ui_shown());
+  EXPECT_FALSE(func->scope_ui_shown());
+  histogram_tester()->ExpectUniqueSample(
+      kGetAuthTokenResultHistogramName,
+      IdentityGetAuthTokenError::State::kSignInFailed, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                        SignedInWebOnlyAcceptPromptMultipleFunctions) {
   identity_test_env()->MakeAccountAvailable("[email protected]",
                                             {.set_cookie = true});
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc
index aa0688b..f1f3b69 100644
--- a/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -1628,6 +1628,126 @@
 }
 
 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
+                       SignedInWebOnlyNonInteractiveWithAccount) {
+  const AccountInfo account_info = identity_test_env()->MakeAccountAvailable(
+      "[email protected]", {.set_cookie = true});
+  ASSERT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount(
+      signin::ConsentLevel::kSignin));
+  ASSERT_FALSE(identity_test_env()
+                   ->identity_manager()
+                   ->GetAccountsWithRefreshTokens()
+                   .empty());
+
+  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
+  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
+  func->set_extension(extension.get());
+
+  const std::string args =
+      base::StringPrintf(R"([{"interactive": false, "account": {"id": "%s"}}])",
+                         account_info.GetGaiaId().ToString().c_str());
+  const std::string error =
+      utils::RunFunctionAndReturnError(func.get(), args, profile());
+  EXPECT_EQ(error, std::string(errors::kUserNotSignedIn));
+  EXPECT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount(
+      signin::ConsentLevel::kSignin));
+
+  EXPECT_FALSE(func->login_ui_shown());
+  EXPECT_FALSE(func->scope_ui_shown());
+  histogram_tester()->ExpectUniqueSample(
+      kGetAuthTokenResultHistogramName,
+      IdentityGetAuthTokenError::State::kUserNotSignedIn, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
+                       SignedInWebOnlyAcceptPromptWithAccount) {
+  const AccountInfo account_info = identity_test_env()->MakeAccountAvailable(
+      "[email protected]", {.set_cookie = true});
+  ASSERT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount(
+      signin::ConsentLevel::kSignin));
+  ASSERT_FALSE(identity_test_env()
+                   ->identity_manager()
+                   ->GetAccountsWithRefreshTokens()
+                   .empty());
+
+  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
+  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
+  func->set_extension(extension.get());
+  func->push_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS);
+
+  views::NamedWidgetShownWaiter widget_waiter(
+      views::test::AnyWidgetTestPasskey{},
+      "ChromeSigninChoiceForExtensionsPrompt");
+
+  const std::string args =
+      base::StringPrintf(R"([{"interactive": true, "account": {"id": "%s"}}])",
+                         account_info.GetGaiaId().ToString().c_str());
+  RunFunctionAsync(func.get(), args);
+  views::Widget* confirmation_prompt = widget_waiter.WaitIfNeededAndGet();
+  ASSERT_NE(confirmation_prompt, nullptr);
+  views::DialogDelegate* dialog_delegate =
+      confirmation_prompt->widget_delegate()->AsDialogDelegate();
+  ASSERT_NE(dialog_delegate, nullptr);
+  dialog_delegate->AcceptDialog();
+
+  std::string access_token;
+  std::set<std::string> granted_scopes;
+  WaitForGetAuthTokenResults(func.get(), &access_token, &granted_scopes);
+  EXPECT_EQ(access_token, std::string(kAccessToken));
+  EXPECT_EQ(granted_scopes, func->GetExtensionTokenKeyForTest()->scopes);
+  EXPECT_EQ(account_info.GetAccountId(),
+            identity_test_env()->identity_manager()->GetPrimaryAccountId(
+                signin::ConsentLevel::kSignin));
+
+  EXPECT_FALSE(func->login_ui_shown());
+  EXPECT_FALSE(func->scope_ui_shown());
+  histogram_tester()->ExpectUniqueSample(
+      kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone,
+      1);
+}
+
+IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
+                       SignedInWebOnlyDeclinePromptWithAccount) {
+  const AccountInfo account_info = identity_test_env()->MakeAccountAvailable(
+      "[email protected]", {.set_cookie = true});
+  ASSERT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount(
+      signin::ConsentLevel::kSignin));
+  ASSERT_FALSE(identity_test_env()
+                   ->identity_manager()
+                   ->GetAccountsWithRefreshTokens()
+                   .empty());
+
+  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
+  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
+  func->set_extension(extension.get());
+  func->push_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS);
+
+  views::NamedWidgetShownWaiter widget_waiter(
+      views::test::AnyWidgetTestPasskey{},
+      "ChromeSigninChoiceForExtensionsPrompt");
+
+  const std::string args =
+      base::StringPrintf(R"([{"interactive": true, "account": {"id": "%s"}}])",
+                         account_info.GetGaiaId().ToString().c_str());
+  RunFunctionAsync(func.get(), args);
+  views::Widget* confirmation_prompt = widget_waiter.WaitIfNeededAndGet();
+  ASSERT_NE(confirmation_prompt, nullptr);
+  views::DialogDelegate* dialog_delegate =
+      confirmation_prompt->widget_delegate()->AsDialogDelegate();
+  ASSERT_NE(dialog_delegate, nullptr);
+  dialog_delegate->CancelDialog();
+
+  EXPECT_EQ(WaitForError(func.get()), std::string(errors::kUserNotSignedIn));
+  EXPECT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount(
+      signin::ConsentLevel::kSignin));
+
+  EXPECT_FALSE(func->login_ui_shown());
+  EXPECT_FALSE(func->scope_ui_shown());
+  histogram_tester()->ExpectUniqueSample(
+      kGetAuthTokenResultHistogramName,
+      IdentityGetAuthTokenError::State::kSignInFailed, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                        SignedInWebOnlyAcceptPromptMultipleFunctions) {
   identity_test_env()->MakeAccountAvailable("[email protected]",
                                             {.set_cookie = true});
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.