[signin] Plumb and gate mTLS token binding in the identity stack [chromium/src : main]

0 views
Skip to first unread message

Ali Hijazi (Gerrit)

unread,
Mar 31, 2026, 7:39:39 AM (2 days ago) Mar 31
to Alex Ilin, Chromium LUCI CQ, chromium...@chromium.org, druber...@chromium.org, chromium-a...@chromium.org, extension...@chromium.org, gcasto+w...@chromium.org, vasilii+watchlis...@chromium.org
Attention needed from Alex Ilin

Ali Hijazi added 1 comment

Patchset-level comments
File-level comment, Patchset 20 (Latest):
Ali Hijazi . resolved

Hi @alex...@chromium.org,

Could you please take a look?

Thanks,

Open in Gerrit

Related details

Attention is currently required from:
  • Alex Ilin
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: I2e7ca0e138f65105f0d0027ceb34e1b3a3492862
Gerrit-Change-Number: 7657808
Gerrit-PatchSet: 20
Gerrit-Owner: Ali Hijazi <ahi...@chromium.org>
Gerrit-Reviewer: Alex Ilin <alex...@chromium.org>
Gerrit-Reviewer: Ali Hijazi <ahi...@chromium.org>
Gerrit-Attention: Alex Ilin <alex...@chromium.org>
Gerrit-Comment-Date: Tue, 31 Mar 2026 11:39:28 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy

Alex Ilin (Gerrit)

unread,
Mar 31, 2026, 10:13:10 AM (2 days ago) Mar 31
to Ali Hijazi, Alex Ilin, Chromium LUCI CQ, chromium...@chromium.org, druber...@chromium.org, chromium-a...@chromium.org, extension...@chromium.org, gcasto+w...@chromium.org, vasilii+watchlis...@chromium.org
Attention needed from Ali Hijazi

Alex Ilin added 2 comments

Patchset-level comments
Alex Ilin . resolved

I haven't done an in-depth review yet, just wanted to share an immediate concern with the feature setup.

Commit Message
Line 20, Patchset 20 (Latest):2. The `IsRefreshTokenBoundToMtls()` method will return false if the
feature is disabled, causing the system to treat all tokens as
unbound to mTLS, even if they were previously marked as bound in the
local database.
Alex Ilin . unresolved

I'm not sure we want to implement this second bit.

Based on my prior experience with DBSC, this will make potential Finch group reassignments much more painful because a feature rollback will mean signing-out for already enrolled users.

We had the same strategy for DBSC and I wished we had done it differently, so let's avoid making the same mistakes.

I think it'd be reasonable to guard the database reads with a separate kill-switch flag that we'd deploy only in emergency scenarios. This kill-switch should also reset the value in the database so that we don't have to keep it enabled forever. (I'd implement it in a follow-up CL)

Open in Gerrit

Related details

Attention is currently required from:
  • Ali Hijazi
Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: I2e7ca0e138f65105f0d0027ceb34e1b3a3492862
    Gerrit-Change-Number: 7657808
    Gerrit-PatchSet: 20
    Gerrit-Owner: Ali Hijazi <ahi...@chromium.org>
    Gerrit-Reviewer: Alex Ilin <alex...@chromium.org>
    Gerrit-Reviewer: Ali Hijazi <ahi...@chromium.org>
    Gerrit-Attention: Ali Hijazi <ahi...@chromium.org>
    Gerrit-Comment-Date: Tue, 31 Mar 2026 14:12:56 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Alex Ilin (Gerrit)

    unread,
    Mar 31, 2026, 1:57:29 PM (2 days ago) Mar 31
    to Ali Hijazi, Alex Ilin, Chromium LUCI CQ, chromium...@chromium.org, druber...@chromium.org, chromium-a...@chromium.org, extension...@chromium.org, gcasto+w...@chromium.org, vasilii+watchlis...@chromium.org
    Attention needed from Ali Hijazi

    Alex Ilin added 13 comments

    Commit Message
    Line 20, Patchset 20:2. The `IsRefreshTokenBoundToMtls()` method will return false if the

    feature is disabled, causing the system to treat all tokens as
    unbound to mTLS, even if they were previously marked as bound in the
    local database.
    Alex Ilin . resolved

    I'm not sure we want to implement this second bit.

    Based on my prior experience with DBSC, this will make potential Finch group reassignments much more painful because a feature rollback will mean signing-out for already enrolled users.

    We had the same strategy for DBSC and I wished we had done it differently, so let's avoid making the same mistakes.

    I think it'd be reasonable to guard the database reads with a separate kill-switch flag that we'd deploy only in emergency scenarios. This kill-switch should also reset the value in the database so that we don't have to keep it enabled forever. (I'd implement it in a follow-up CL)

    Alex Ilin

    Discussed offline that it still makes sense to keep this check % avoiding the feature activation on every startup (already done).

    File chrome/browser/signin/dice_response_handler_unittest.cc
    File-level comment, Patchset 21 (Latest):
    Alex Ilin . unresolved

    please add mTLS test to this file

    We need to check that mtls_token_binding is plumbed correctly to `GaiaAuthFetcher` and to `AddOrUpdateAccount()`

    File components/signin/core/browser/dice_header_helper.cc
    Line 115, Patchset 21 (Latest): mtls_token_binding = true;
    Alex Ilin . unresolved

    Unrelated to this CL, but I'm worried that this sets `true` directly instead of checking the attribute value. The code would work unexpectedly if the server sends `mtls_token_binding=false`

    File components/signin/core/browser/dice_header_helper_unittest.cc
    Line 207, Patchset 21 (Latest): base::test::ScopedFeatureList scoped_feature_list;
    Alex Ilin . unresolved

    Could you please also test that "mtls_token_binding=true" is ignored if the feature is disabled?

    File components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.h
    Line 332, Patchset 21 (Latest): // In memory store mapping account_id to mtls_token_binding info.
    std::map<CoreAccountId, bool> mtls_token_bindings_;
    Alex Ilin . unresolved

    I don't necessarily like the idea of having a separate mapping for this.

    I see two better alternatives:

    1. store these values in `token_binding_helper_`; the main disadvantage is that mTLS binding won't work without enabling the TPM key binding first, as otherwise `token_binding_helper_` will be null. We could refactor this logic to make `token_binding_helper_` non-null
    2. extend the `refresh_token` map to also store a `bool` next to `crypto::ProcessBoundString`. This way we'll ensure that every refresh token will store an `mtls_token_binding` value

    Option 2 seems to be more straightforward to me and it avoid coupling mTLS and TPM binding together.

    Line 133, Patchset 21 (Latest): bool IsRefreshTokenBoundToMtls(
    Alex Ilin . unresolved

    I feel like this creates a conflict between `IsRefreshTokenBound()` and `IsRefreshTokenBoundToMtls()`.

    It sounds like `IsRefreshTokenBound` should be a superset of both but it is not, i.e. `IsRefreshTokenBound() == false && IsRefreshTokenBoundToMtls() == true` is a valid state.

    Possible solutions:
    1. Rename `IsRefreshTokenBound()` to `IsRefreshTokenBoundToKey()`
    2. Say `ShouldUseMtlsForAccessTokenFetches()` instead of `IsRefreshTokenBoundToMtls()`
    3. Combine both functions into one returning an EnumSet (syntax is not accurate): `EnumSet<mtls_bound, key_bound> GetRefreshTokenBindingState()`

    File components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc
    Line 2194, Patchset 21 (Latest): PersistenceLoadMtlsBoundToken) {
    Alex Ilin . unresolved

    Could we also test what happens if the feature gets disabled after a restart?

    File components/signin/internal/identity_manager/profile_oauth2_token_service.h
    Line 299, Patchset 21 (Latest): bool IsRefreshTokenBoundToMtls(const CoreAccountId& account_id) const;
    Alex Ilin . unresolved

    I don't see a need for publicly exposing this method from `ProfileOAuth2TokenService`. Can this be kept as an implementation detail within `MutableProfileOAuth2TokenServiceDelegate`?

    File components/signin/public/base/signin_switches.cc
    Line 292, Patchset 21 (Latest):BASE_FEATURE(kEnableMtlsTokenBinding, base::FEATURE_DISABLED_BY_DEFAULT);
    Alex Ilin . unresolved

    nit: Could you please add a short feature description in a comment?

    File components/signin/public/identity_manager/token_binding_info.h
    Line 35, Patchset 21 (Latest): // Whether the refresh token is bound to mTLS endpoints. If true,
    // access token requests and other Gaia interactions using this token
    // should use mTLS-specific endpoints.
    Alex Ilin . unresolved

    nit: minor corrections

    "other Gaia interactions" is too vague and may be misinterpreted. For now, we are only redirecting access token requests to mTLS endpoints. The auth_code -> LST exchange doesn't count because it happens before the `TokenBindingInfo` struct can be even created.

    ```suggestion
    // Whether the refresh token is bound to an mTLS certificate. If true,
    // access token requests using this token should use mTLS-specific
    // endpoints.
    ```
    File components/signin/public/webdata/token_service_table.cc
    Line 208, Patchset 21 (Latest): sql::Statement s(db()->GetUniqueStatement(
    "SELECT service, encrypted_token, binding_key, mtls_token_binding "
    "FROM token_service"));
    Alex Ilin . unresolved

    nit: This is a pretty random formatting change Can it be rolled back?

    File google_apis/gaia/oauth2_mint_access_token_fetcher_adapter.h
    Line 81, Patchset 21 (Latest): bool use_mtls_endpoints_for_fetching_tokens_;
    Alex Ilin . unresolved

    nit: could be made `const`

    also, please give it a default value (and to the `is_refresh_token_bound_` param below)

    ```suggestion
    const bool use_mtls_endpoints_for_fetching_tokens_ = false;
    ```
    File google_apis/gaia/oauth2_mint_access_token_fetcher_adapter_unittest.cc
    Line 195, Patchset 21 (Latest): /*use_mtls_endpoints_for_fetching_tokens=*/false,
    Alex Ilin . unresolved

    nit: it'd be good to have a test when this parameter is true

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Ali Hijazi
    Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: I2e7ca0e138f65105f0d0027ceb34e1b3a3492862
    Gerrit-Change-Number: 7657808
    Gerrit-PatchSet: 21
    Gerrit-Owner: Ali Hijazi <ahi...@chromium.org>
    Gerrit-Reviewer: Alex Ilin <alex...@chromium.org>
    Gerrit-Reviewer: Ali Hijazi <ahi...@chromium.org>
    Gerrit-Attention: Ali Hijazi <ahi...@chromium.org>
    Gerrit-Comment-Date: Tue, 31 Mar 2026 17:57:17 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: Alex Ilin <alex...@chromium.org>
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Ali Hijazi (Gerrit)

    unread,
    5:28 AM (10 hours ago) 5:28 AM
    to Alex Ilin, Chromium LUCI CQ, chromium...@chromium.org, druber...@chromium.org, chromium-a...@chromium.org, extension...@chromium.org, gcasto+w...@chromium.org, vasilii+watchlis...@chromium.org
    Attention needed from Alex Ilin

    Ali Hijazi added 12 comments

    File chrome/browser/signin/dice_response_handler_unittest.cc

    please add mTLS test to this file

    We need to check that mtls_token_binding is plumbed correctly to `GaiaAuthFetcher` and to `AddOrUpdateAccount()`

    Ali Hijazi

    Done

    File components/signin/core/browser/dice_header_helper.cc
    Line 115, Patchset 21: mtls_token_binding = true;
    Alex Ilin . resolved

    Unrelated to this CL, but I'm worried that this sets `true` directly instead of checking the attribute value. The code would work unexpectedly if the server sends `mtls_token_binding=false`

    Ali Hijazi

    The server responds with "mtls_token_binding=true" or doesn't add mtls_token_binding attribute at all. It should be okay to check against "true"

    File components/signin/core/browser/dice_header_helper_unittest.cc
    Line 207, Patchset 21: base::test::ScopedFeatureList scoped_feature_list;
    Alex Ilin . resolved

    Could you please also test that "mtls_token_binding=true" is ignored if the feature is disabled?

    Ali Hijazi

    Done

    File components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.h
    Line 332, Patchset 21: // In memory store mapping account_id to mtls_token_binding info.

    std::map<CoreAccountId, bool> mtls_token_bindings_;
    Alex Ilin . resolved

    I don't necessarily like the idea of having a separate mapping for this.

    I see two better alternatives:

    1. store these values in `token_binding_helper_`; the main disadvantage is that mTLS binding won't work without enabling the TPM key binding first, as otherwise `token_binding_helper_` will be null. We could refactor this logic to make `token_binding_helper_` non-null
    2. extend the `refresh_token` map to also store a `bool` next to `crypto::ProcessBoundString`. This way we'll ensure that every refresh token will store an `mtls_token_binding` value

    Option 2 seems to be more straightforward to me and it avoid coupling mTLS and TPM binding together.

    Ali Hijazi

    Done

    Line 133, Patchset 21: bool IsRefreshTokenBoundToMtls(
    Alex Ilin . resolved

    I feel like this creates a conflict between `IsRefreshTokenBound()` and `IsRefreshTokenBoundToMtls()`.

    It sounds like `IsRefreshTokenBound` should be a superset of both but it is not, i.e. `IsRefreshTokenBound() == false && IsRefreshTokenBoundToMtls() == true` is a valid state.

    Possible solutions:
    1. Rename `IsRefreshTokenBound()` to `IsRefreshTokenBoundToKey()`
    2. Say `ShouldUseMtlsForAccessTokenFetches()` instead of `IsRefreshTokenBoundToMtls()`
    3. Combine both functions into one returning an EnumSet (syntax is not accurate): `EnumSet<mtls_bound, key_bound> GetRefreshTokenBindingState()`

    Ali Hijazi

    Done

    File components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc
    Line 2194, Patchset 21: PersistenceLoadMtlsBoundToken) {
    Alex Ilin . resolved

    Could we also test what happens if the feature gets disabled after a restart?

    Ali Hijazi

    Done

    File components/signin/internal/identity_manager/profile_oauth2_token_service.h
    Line 299, Patchset 21: bool IsRefreshTokenBoundToMtls(const CoreAccountId& account_id) const;
    Alex Ilin . resolved

    I don't see a need for publicly exposing this method from `ProfileOAuth2TokenService`. Can this be kept as an implementation detail within `MutableProfileOAuth2TokenServiceDelegate`?

    Ali Hijazi

    Done

    File components/signin/public/base/signin_switches.cc
    Line 292, Patchset 21:BASE_FEATURE(kEnableMtlsTokenBinding, base::FEATURE_DISABLED_BY_DEFAULT);
    Alex Ilin . resolved

    nit: Could you please add a short feature description in a comment?

    Ali Hijazi

    Done

    File components/signin/public/identity_manager/token_binding_info.h
    Line 35, Patchset 21: // Whether the refresh token is bound to mTLS endpoints. If true,

    // access token requests and other Gaia interactions using this token
    // should use mTLS-specific endpoints.
    Alex Ilin . resolved

    nit: minor corrections

    "other Gaia interactions" is too vague and may be misinterpreted. For now, we are only redirecting access token requests to mTLS endpoints. The auth_code -> LST exchange doesn't count because it happens before the `TokenBindingInfo` struct can be even created.

    ```suggestion
    // Whether the refresh token is bound to an mTLS certificate. If true,
    // access token requests using this token should use mTLS-specific
    // endpoints.
    ```
    Ali Hijazi

    Done

    File components/signin/public/webdata/token_service_table.cc
    Line 208, Patchset 21: sql::Statement s(db()->GetUniqueStatement(

    "SELECT service, encrypted_token, binding_key, mtls_token_binding "
    "FROM token_service"));
    Alex Ilin . resolved

    nit: This is a pretty random formatting change Can it be rolled back?

    Ali Hijazi

    Done

    File google_apis/gaia/oauth2_mint_access_token_fetcher_adapter.h
    Line 81, Patchset 21: bool use_mtls_endpoints_for_fetching_tokens_;
    Alex Ilin . resolved

    nit: could be made `const`

    also, please give it a default value (and to the `is_refresh_token_bound_` param below)

    ```suggestion
    const bool use_mtls_endpoints_for_fetching_tokens_ = false;
    ```
    Ali Hijazi

    Done

    File google_apis/gaia/oauth2_mint_access_token_fetcher_adapter_unittest.cc
    Line 195, Patchset 21: /*use_mtls_endpoints_for_fetching_tokens=*/false,
    Alex Ilin . resolved

    nit: it'd be good to have a test when this parameter is true

    Ali Hijazi

    Done

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Alex Ilin
    Submit Requirements:
      • requirement satisfiedCode-Coverage
      • requirement is not satisfiedCode-Owners
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedReview-Enforcement
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: chromium/src
      Gerrit-Branch: main
      Gerrit-Change-Id: I2e7ca0e138f65105f0d0027ceb34e1b3a3492862
      Gerrit-Change-Number: 7657808
      Gerrit-PatchSet: 23
      Gerrit-Owner: Ali Hijazi <ahi...@chromium.org>
      Gerrit-Reviewer: Alex Ilin <alex...@chromium.org>
      Gerrit-Reviewer: Ali Hijazi <ahi...@chromium.org>
      Gerrit-Attention: Alex Ilin <alex...@chromium.org>
      Gerrit-Comment-Date: Thu, 02 Apr 2026 09:27:48 +0000
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Alex Ilin (Gerrit)

      unread,
      8:34 AM (6 hours ago) 8:34 AM
      to Ali Hijazi, Alex Ilin, Chromium LUCI CQ, chromium...@chromium.org, druber...@chromium.org, chromium-a...@chromium.org, extension...@chromium.org, gcasto+w...@chromium.org, vasilii+watchlis...@chromium.org
      Attention needed from Ali Hijazi

      Alex Ilin voted and added 8 comments

      Votes added by Alex Ilin

      Code-Review+1

      8 comments

      Patchset-level comments
      File-level comment, Patchset 23 (Latest):
      Alex Ilin . resolved

      Nice, LGTM!

      Let's add a browser test to `chrome/browser/signin/dice_browsertest.cc` next to ensure that the whole flow works end-to-end.

      File chrome/browser/signin/dice_response_handler_unittest.cc
      Line 471, Patchset 23 (Latest): const auto* account = dice_params.signin_info()->GetInitiator();
      ASSERT_TRUE(account);
      const auto& account_info = account->account_info;
      CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
      account_info.gaia_id, account_info.email);
      Alex Ilin . unresolved

      nit: you already have a local `account_information` object that should be identical to `account_info` you're extracting

      ```suggestion
      CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
      account_information.gaia_id, account_information.email);
      ```
      Line 498, Patchset 23 (Latest): EXPECT_TRUE(auth_error_email_.empty());
      EXPECT_EQ(GoogleServiceAuthError::NONE, auth_error_.state());
      // Check HandleTokenExchangeSuccess parameters.
      EXPECT_EQ(token_exchange_account_id_, account_id);
      EXPECT_TRUE(token_exchange_is_new_account_);
      // Check that the reconcilor was blocked and unblocked exactly once.
      EXPECT_EQ(1, reconcilor_blocked_count_);
      EXPECT_EQ(1, reconcilor_unblocked_count_);
      // Check that the AccountInfo::is_under_advanced_protection is set.
      AccountInfo extended_account_info =
      identity_manager()->FindExtendedAccountInfoByAccountId(account_id);
      EXPECT_TRUE(extended_account_info.is_under_advanced_protection);
      // Check that the AccessPoint was propagated from the delegate.
      EXPECT_EQ(extended_account_info.access_point,
      signin_metrics::AccessPoint::kSettings);
      EXPECT_EQ(
      identity_test_env_.GetNumCallsToPrepareForFetchingAccountCapabilities(),
      1);
      histogram_tester_.ExpectUniqueSample(
      kTokenBindingOutcomeHistogram,
      DiceResponseHandler::TokenBindingOutcome::kNotBoundNotSupported,
      /*expected_bucket_count=*/1);
      Alex Ilin . unresolved

      nit: This stuff is not very relevant for this test, so I'd just remove it.

      One think that's missing is verification that the account will be using an mtls endpoint for access token fetches. Could you please add this to your test?

      File components/signin/core/browser/dice_header_helper.cc
      Line 124, Patchset 23 (Latest): if (value == "true" &&
      Alex Ilin . unresolved

      nit: let's make this comparison case-insensitive

      ```suggestion
      if (base::EqualsCaseInsensitiveASCII(value, "true") &&
      ```
      File components/signin/core/browser/dice_header_helper_unittest.cc
      Line 223, Patchset 23 (Latest): base::test::ScopedFeatureList scoped_feature_list;
      scoped_feature_list.InitAndEnableFeature(switches::kEnableMtlsTokenBinding);
      Alex Ilin . unresolved

      optional: this can be done in a single statement

      ditto L495, L575

      https://crsrc.org/c/base/test/scoped_feature_list.h;drc=4b18d0c047503e82fdfc53c8b57a397458b18958;l=90

      ```suggestion
      base::test::ScopedFeatureList
      scoped_feature_list(switches::kEnableMtlsTokenBinding);
      ```
      Line 268, Patchset 23 (Latest): EXPECT_EQ(kGaiaID, account->account_info.gaia_id);
      EXPECT_EQ(kEmail, account->account_info.email);
      EXPECT_EQ(kSessionIndex, account->account_info.session_index);
      EXPECT_EQ(kAuthorizationCode, account->authorization_code);
      EXPECT_EQ(kSupportedTokenBindingAlgorithms,
      account->supported_algorithms_for_token_binding);
      EXPECT_FALSE(account->mtls_token_binding);
      histogram_tester.ExpectUniqueSample("Signin.DiceAuthorizationCode", true, 1);
      Alex Ilin . unresolved

      nit: Most of these comparisons are unnecessary for this test. The test will be much more readable if you focus it on a single purpose of verifying the `mtls_token_binding` parameter.

      ```suggestion
      EXPECT_FALSE(account->mtls_token_binding);
      ```
      File components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h
      Line 128, Patchset 23 (Latest): // Maps account ids to mtls_token_binding.

      std::map<CoreAccountId, bool> mtls_token_bindings_;
      Alex Ilin . unresolved

      nit: `mtls_token_bindings_` isn't used for anything, please remove

      File components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.cc
      Line 315, Patchset 23 (Latest):
      MutableProfileOAuth2TokenServiceDelegate::TokenData::TokenData()
      : refresh_token(std::string()) {}
      Alex Ilin . unresolved

      nit: do we need a default constructor here at all? `crypto::ProcessBoundString` doesn't have one, so I'd say that `TokenData` doesn't need one either.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Ali Hijazi
      Submit Requirements:
      • requirement satisfiedCode-Coverage
      • requirement satisfiedCode-Owners
      • requirement satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement satisfiedReview-Enforcement
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: chromium/src
      Gerrit-Branch: main
      Gerrit-Change-Id: I2e7ca0e138f65105f0d0027ceb34e1b3a3492862
      Gerrit-Change-Number: 7657808
      Gerrit-PatchSet: 23
      Gerrit-Owner: Ali Hijazi <ahi...@chromium.org>
      Gerrit-Reviewer: Alex Ilin <alex...@chromium.org>
      Gerrit-Reviewer: Ali Hijazi <ahi...@chromium.org>
      Gerrit-Attention: Ali Hijazi <ahi...@chromium.org>
      Gerrit-Comment-Date: Thu, 02 Apr 2026 12:34:21 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: Yes
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Ali Hijazi (Gerrit)

      unread,
      1:00 PM (2 hours ago) 1:00 PM
      to Alex Ilin, Chromium LUCI CQ, chromium...@chromium.org, druber...@chromium.org, chromium-a...@chromium.org, extension...@chromium.org, gcasto+w...@chromium.org, vasilii+watchlis...@chromium.org
      Attention needed from Alex Ilin

      Ali Hijazi added 7 comments

      File chrome/browser/signin/dice_response_handler_unittest.cc
      Line 471, Patchset 23: const auto* account = dice_params.signin_info()->GetInitiator();

      ASSERT_TRUE(account);
      const auto& account_info = account->account_info;
      CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
      account_info.gaia_id, account_info.email);
      Alex Ilin . resolved

      nit: you already have a local `account_information` object that should be identical to `account_info` you're extracting

      ```suggestion
      CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
      account_information.gaia_id, account_information.email);
      ```
      Ali Hijazi

      Done

      Line 498, Patchset 23: EXPECT_TRUE(auth_error_email_.empty());

      EXPECT_EQ(GoogleServiceAuthError::NONE, auth_error_.state());
      // Check HandleTokenExchangeSuccess parameters.
      EXPECT_EQ(token_exchange_account_id_, account_id);
      EXPECT_TRUE(token_exchange_is_new_account_);
      // Check that the reconcilor was blocked and unblocked exactly once.
      EXPECT_EQ(1, reconcilor_blocked_count_);
      EXPECT_EQ(1, reconcilor_unblocked_count_);
      // Check that the AccountInfo::is_under_advanced_protection is set.
      AccountInfo extended_account_info =
      identity_manager()->FindExtendedAccountInfoByAccountId(account_id);
      EXPECT_TRUE(extended_account_info.is_under_advanced_protection);
      // Check that the AccessPoint was propagated from the delegate.
      EXPECT_EQ(extended_account_info.access_point,
      signin_metrics::AccessPoint::kSettings);
      EXPECT_EQ(
      identity_test_env_.GetNumCallsToPrepareForFetchingAccountCapabilities(),
      1);
      histogram_tester_.ExpectUniqueSample(
      kTokenBindingOutcomeHistogram,
      DiceResponseHandler::TokenBindingOutcome::kNotBoundNotSupported,
      /*expected_bucket_count=*/1);
      Alex Ilin . unresolved

      nit: This stuff is not very relevant for this test, so I'd just remove it.

      One think that's missing is verification that the account will be using an mtls endpoint for access token fetches. Could you please add this to your test?

      Ali Hijazi

      I attempted to modify the test by adding:

      ```c++
      base::RunLoop run_loop;
      identity_test_env_.SetCallbackForNextAccessTokenRequest(
      run_loop.QuitClosure());
      signin::AccessTokenFetcher::TokenCallback callback =
      base::BindOnce([](GoogleServiceAuthError error,
      signin::AccessTokenInfo access_token_info) {});
        std::unique_ptr<signin::AccessTokenFetcher> token_fetcher =
      identity_manager()->CreateAccessTokenFetcherForAccount(
      account_id, signin::OAuthConsumerId::kExtensionsIdentityAPI,
      signin_client_.GetURLLoaderFactory(), std::move(callback),
      signin::AccessTokenFetcher::Mode::kImmediate);
        run_loop.Run();
        network::TestURLLoaderFactory* factory =
      signin_client_.GetTestURLLoaderFactory();
      size_t num_pending = factory->NumPending();
      ASSERT_GE(num_pending, 1u);
      EXPECT_EQ(factory->GetPendingRequest(num_pending - 1)->request.url,
      GaiaUrls::GetInstance()->mtls_oauth2_token_url());
      ```

      Unfortunately this didn't work as expected as we end up calling `FakeOAuth2AccessTokenManager::FetchOAuth2Token` instead of `OAuth2AccessTokenManager::FetchOAuth2Token`.

      If you have another approach in mind, please let me know. It seems we might need a browsertest for this.

      File components/signin/core/browser/dice_header_helper.cc
      Line 124, Patchset 23: if (value == "true" &&
      Alex Ilin . resolved

      nit: let's make this comparison case-insensitive

      ```suggestion
      if (base::EqualsCaseInsensitiveASCII(value, "true") &&
      ```
      Ali Hijazi

      Done

      File components/signin/core/browser/dice_header_helper_unittest.cc
      Line 223, Patchset 23: base::test::ScopedFeatureList scoped_feature_list;
      scoped_feature_list.InitAndEnableFeature(switches::kEnableMtlsTokenBinding);
      Alex Ilin . resolved

      optional: this can be done in a single statement

      ditto L495, L575

      https://crsrc.org/c/base/test/scoped_feature_list.h;drc=4b18d0c047503e82fdfc53c8b57a397458b18958;l=90

      ```suggestion
      base::test::ScopedFeatureList
      scoped_feature_list(switches::kEnableMtlsTokenBinding);
      ```
      Ali Hijazi

      Done

      Line 268, Patchset 23: EXPECT_EQ(kGaiaID, account->account_info.gaia_id);

      EXPECT_EQ(kEmail, account->account_info.email);
      EXPECT_EQ(kSessionIndex, account->account_info.session_index);
      EXPECT_EQ(kAuthorizationCode, account->authorization_code);
      EXPECT_EQ(kSupportedTokenBindingAlgorithms,
      account->supported_algorithms_for_token_binding);
      EXPECT_FALSE(account->mtls_token_binding);
      histogram_tester.ExpectUniqueSample("Signin.DiceAuthorizationCode", true, 1);
      Alex Ilin . resolved

      nit: Most of these comparisons are unnecessary for this test. The test will be much more readable if you focus it on a single purpose of verifying the `mtls_token_binding` parameter.

      ```suggestion
      EXPECT_FALSE(account->mtls_token_binding);
      ```
      Ali Hijazi

      Done

      File components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h
      Line 128, Patchset 23: // Maps account ids to mtls_token_binding.

      std::map<CoreAccountId, bool> mtls_token_bindings_;
      Alex Ilin . resolved

      nit: `mtls_token_bindings_` isn't used for anything, please remove

      Ali Hijazi

      nice catch, thanks!

      File components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.cc

      MutableProfileOAuth2TokenServiceDelegate::TokenData::TokenData()
      : refresh_token(std::string()) {}
      Alex Ilin . resolved

      nit: do we need a default constructor here at all? `crypto::ProcessBoundString` doesn't have one, so I'd say that `TokenData` doesn't need one either.

      Ali Hijazi

      Done

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Alex Ilin
      Submit Requirements:
      • requirement satisfiedCode-Coverage
      • requirement satisfiedCode-Owners
      • requirement satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement satisfiedReview-Enforcement
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: chromium/src
      Gerrit-Branch: main
      Gerrit-Change-Id: I2e7ca0e138f65105f0d0027ceb34e1b3a3492862
      Gerrit-Change-Number: 7657808
      Gerrit-PatchSet: 24
      Gerrit-Owner: Ali Hijazi <ahi...@chromium.org>
      Gerrit-Reviewer: Alex Ilin <alex...@chromium.org>
      Gerrit-Reviewer: Ali Hijazi <ahi...@chromium.org>
      Gerrit-Attention: Alex Ilin <alex...@chromium.org>
      Gerrit-Comment-Date: Thu, 02 Apr 2026 17:00:27 +0000
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy
      Reply all
      Reply to author
      Forward
      0 new messages