Skip to content

fix(seedless-onboarding): update tests for optional refreshToken and proactive renewal#8148

Merged
ieow merged 11 commits intomainfrom
fix/seedless-onboarding-refresh-token-tests
Mar 18, 2026
Merged

fix(seedless-onboarding): update tests for optional refreshToken and proactive renewal#8148
ieow merged 11 commits intomainfrom
fix/seedless-onboarding-refresh-token-tests

Conversation

@himanshuchawla009
Copy link
Contributor

@himanshuchawla009 himanshuchawla009 commented Mar 9, 2026

  • Replace stale concurrent-rotation test with assertion that authenticate never receives refreshToken from the token-refresh path
  • Add tests verifying renewRefreshToken is called proactively after vault update when vault is unlocked, and skipped when locked
  • Add test covering the renewRefreshToken error-swallow path in refreshAuthTokens (catch block log call)
  • Add renewRefreshToken tests for vault-unlocked (no password) path and vault-locked-no-password error path
  • Fix two existing tests that captured state.refreshToken after the call; proactive renewal now rotates the token, so capture it before the call

Explanation

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Touches authentication/token-refresh logic and changes refresh-token lifecycle (including a breaking API removal), which could impact session continuity if edge cases are missed.

Overview
Updates refreshAuthTokens to re-authenticate via a new private #reAuthenticate that only accepts JWT-refresh outputs, and (when the vault is unlocked) proactively rotates the refresh/revoke token pair via new public rotateRefreshToken while queuing the old pair for later revocation.

Token expiry checks now use a proactive 90%-lifetime threshold for accessToken/metadataAccessToken (via new isTokenNearExpiry), while node auth tokens use exact expiry; fetchMetadataAccessCreds aligns with this logic.

Fixes token-refresh ordering in addNewSecretData/changePassword by moving #assertPasswordInSync inside the token-refresh wrapper, adds a new VaultLocked error, removes the public renewRefreshToken API, and refreshes/expands tests to cover the new behaviors.

Written by Cursor Bugbot for commit 25d2d45. This will update automatically on new commits. Configure here.

…proactive renewal

- Replace stale concurrent-rotation test with assertion that authenticate
  never receives refreshToken from the token-refresh path
- Add tests verifying renewRefreshToken is called proactively after vault
  update when vault is unlocked, and skipped when locked
- Add test covering the renewRefreshToken error-swallow path in
  refreshAuthTokens (catch block log call)
- Add renewRefreshToken tests for vault-unlocked (no password) path and
  vault-locked-no-password error path
- Fix two existing tests that captured state.refreshToken after the call;
  proactive renewal now rotates the token, so capture it before the call
Token expiry helpers now use a 90% lifetime threshold:
- Access/metadata tokens refresh when <10% lifetime remains (uses iat)
- Node auth tokens fall back to exact-expiry check (no reliable iat)

Introduces module-level isTokenNearExpiry(exp, iat?) helper used by
checkNodeAuthTokenExpired, checkMetadataAccessTokenExpired, and
checkAccessTokenExpired.
Cover all source changes introduced in this branch:
- authenticate() optional refreshToken param
- proactive renewRefreshToken after JWT refresh in #doRefreshAuthTokens
- renewRefreshToken vault-unlock path (optional password + skipLock)
- 90% lifetime threshold for proactive token expiry checks
@himanshuchawla009 himanshuchawla009 marked this pull request as ready for review March 10, 2026 07:41
@himanshuchawla009 himanshuchawla009 requested review from a team as code owners March 10, 2026 07:41
@himanshuchawla009
Copy link
Contributor Author

@metamaskbot publish-preview

vaultData: updatedVaultData,
pwEncKey,
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add option to skip proactive renewRefreshToken ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good suggestion, would be useful to skip in some cases.

*/
async renewRefreshToken(password: string): Promise<void> {
return await this.#withControllerLock(async () => {
async renewRefreshToken(
Copy link
Contributor

@ieow ieow Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we consider to allow renewRefreshToken happend only when the seedless controller vault is unlocked?

we can always call submitPassword before renewRefreshToken if there are scenario where the vault is locked and we have password

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with this. We can get rid of password from the params and rely on the cached encryptionKey only (which is only available when the vault is unlocked).

// NOTE: refreshToken is intentionally omitted — renewRefreshToken is the
// sole owner of state.refreshToken. Passing it here would risk overwriting
// a token that renewRefreshToken rotated concurrently during the async
// toprfClient.authenticate() call below.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it cleaner if we have re-authenticate function which only accept

    idTokens,
    accessToken,
    metadataAccessToken,

Instead making the authenticate accept different input structure type

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think refactor the authenticate would be better, so that we can make stricter validation, i.e. first time authentication must provide accessToken and revokeToken while re-authenticate may not.

*/
async renewRefreshToken(password: string): Promise<void> {
return await this.#withControllerLock(async () => {
async renewRefreshToken(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed we call this method internally, inside #doRefreshAuthTokens() so, I think we can make this private already.

Clients should not decide when to call this method, imo.

// NOTE: refreshToken is intentionally omitted — renewRefreshToken is the
// sole owner of state.refreshToken. Passing it here would risk overwriting
// a token that renewRefreshToken rotated concurrently during the async
// toprfClient.authenticate() call below.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think refactor the authenticate would be better, so that we can make stricter validation, i.e. first time authentication must provide accessToken and revokeToken while re-authenticate may not.

- Make renewRefreshToken private (#rotateRefreshToken): vault-unlock only,
  no password param — callers must unlock first via submitPassword
- Add #reAuthenticate private method for JWT-refresh re-auth path, accepting
  only {idTokens, accessToken, metadataAccessToken} from the refresh service
- Add skipRenewRefreshToken option to refreshAuthTokens to allow callers to
  opt out of the proactive rotation
- Update tests: renewRefreshToken describe converted to #rotateRefreshToken
  (tested via refreshAuthTokens), add skipRenewRefreshToken test, add
  skipLock branch coverage for authenticate
- fetchMetadataAccessCreds: use isTokenNearExpiry(exp, iat) instead of
  exact expiry so it is consistent with checkMetadataAccessTokenExpired
- isTokenNearExpiry: guard against malformed tokens where iat >= exp
  (zero or negative lifetime) by falling back to exact-expiry check
… explicit options

Callers passing explicit options (e.g. skipRenewRefreshToken: true) must
not be served by an in-flight request that was started with different
options. Only default-options calls (no explicit flags) share the pending
promise; explicit-options calls each get their own independent request.
*
* @returns A Promise that resolves to void.
*/
async #rotateRefreshToken(): Promise<void> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to confirm the private behaviour
with this private, the parent cannot rotateRefreshToken (renewRefreshToken), it can now only revokePendingToken.

…ents and logging

- Remove redundant `skipRenewRefreshToken` option from `refreshAuthTokens`
  since the `#cachedDecryptedVaultData` guard already prevents rotation
  when the vault is locked
- Use project `log()` instead of `console.error` for rotation failure
- Fix stale JSDoc on `authenticate` that referenced removed internal usage
- Update `checkMetadataAccessTokenExpired` and `checkAccessTokenExpired`
  JSDoc to mention the 90% proactive refresh threshold
- Add `VaultLocked` error constant with defensive guard in
  `#rotateRefreshToken`
- Remove related tests for the deleted option
if (newRevokeToken && newRefreshToken) {
// Persist the new revoke token to the vault first. Only update state after
// the vault write succeeds, so state and vault stay in sync if the write fails.
await this.#updateVault({
Copy link
Contributor

@ieow ieow Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

info - the vault is not persisted yet after await.
It broadcast state:change message only.
same message is broadcasted during the addRefreshTokenToRevokeList

await this.authenticate({
// Re-authenticate with the refreshed id tokens to update node auth
// tokens, accessToken, and metadataAccessToken in state.
await this.#reAuthenticate({
Copy link
Contributor

@ieow ieow Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: access token is in the non-vault state,
do we still need it in the vault data?

check line 2183

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, accessToken is not persisted in plain state, we only persist in vault. That's why we need it.

Copy link
Contributor

@ieow ieow Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it is not persisted in the plain state, how does the check for password outdated works when the wallet is locked / app reopen ?

ieow
ieow previously approved these changes Mar 16, 2026
Copy link
Contributor

@ieow ieow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@ieow
Copy link
Contributor

ieow commented Mar 16, 2026

@metamaskbot publish-preview

@github-actions
Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "4.1.1-preview-d3b0814a7",
  "@metamask-previews/accounts-controller": "36.0.1-preview-d3b0814a7",
  "@metamask-previews/address-book-controller": "7.0.1-preview-d3b0814a7",
  "@metamask-previews/ai-controllers": "0.2.0-preview-d3b0814a7",
  "@metamask-previews/analytics-controller": "1.0.0-preview-d3b0814a7",
  "@metamask-previews/analytics-data-regulation-controller": "0.0.0-preview-d3b0814a7",
  "@metamask-previews/announcement-controller": "8.0.0-preview-d3b0814a7",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-d3b0814a7",
  "@metamask-previews/approval-controller": "8.0.0-preview-d3b0814a7",
  "@metamask-previews/assets-controller": "2.2.0-preview-d3b0814a7",
  "@metamask-previews/assets-controllers": "100.1.0-preview-d3b0814a7",
  "@metamask-previews/base-controller": "9.0.0-preview-d3b0814a7",
  "@metamask-previews/base-data-service": "0.0.0-preview-d3b0814a7",
  "@metamask-previews/bridge-controller": "68.0.0-preview-d3b0814a7",
  "@metamask-previews/bridge-status-controller": "68.0.0-preview-d3b0814a7",
  "@metamask-previews/build-utils": "3.0.4-preview-d3b0814a7",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-d3b0814a7",
  "@metamask-previews/claims-controller": "0.4.2-preview-d3b0814a7",
  "@metamask-previews/client-controller": "1.0.0-preview-d3b0814a7",
  "@metamask-previews/compliance-controller": "1.0.1-preview-d3b0814a7",
  "@metamask-previews/composable-controller": "12.0.0-preview-d3b0814a7",
  "@metamask-previews/config-registry-controller": "0.1.0-preview-d3b0814a7",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-d3b0814a7",
  "@metamask-previews/controller-utils": "11.19.0-preview-d3b0814a7",
  "@metamask-previews/core-backend": "6.0.0-preview-d3b0814a7",
  "@metamask-previews/delegation-controller": "2.0.1-preview-d3b0814a7",
  "@metamask-previews/earn-controller": "11.1.1-preview-d3b0814a7",
  "@metamask-previews/eip-5792-middleware": "3.0.0-preview-d3b0814a7",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-d3b0814a7",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-d3b0814a7",
  "@metamask-previews/ens-controller": "19.0.3-preview-d3b0814a7",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-d3b0814a7",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-d3b0814a7",
  "@metamask-previews/eth-json-rpc-middleware": "23.1.0-preview-d3b0814a7",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-d3b0814a7",
  "@metamask-previews/foundryup": "1.0.1-preview-d3b0814a7",
  "@metamask-previews/gas-fee-controller": "26.0.3-preview-d3b0814a7",
  "@metamask-previews/gator-permissions-controller": "2.0.0-preview-d3b0814a7",
  "@metamask-previews/geolocation-controller": "0.1.1-preview-d3b0814a7",
  "@metamask-previews/json-rpc-engine": "10.2.3-preview-d3b0814a7",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-d3b0814a7",
  "@metamask-previews/keyring-controller": "25.1.0-preview-d3b0814a7",
  "@metamask-previews/logging-controller": "7.0.1-preview-d3b0814a7",
  "@metamask-previews/message-manager": "14.1.0-preview-d3b0814a7",
  "@metamask-previews/messenger": "0.3.0-preview-d3b0814a7",
  "@metamask-previews/multichain-account-service": "7.0.0-preview-d3b0814a7",
  "@metamask-previews/multichain-api-middleware": "1.2.7-preview-d3b0814a7",
  "@metamask-previews/multichain-network-controller": "3.0.4-preview-d3b0814a7",
  "@metamask-previews/multichain-transactions-controller": "7.0.1-preview-d3b0814a7",
  "@metamask-previews/name-controller": "9.0.0-preview-d3b0814a7",
  "@metamask-previews/network-controller": "30.0.0-preview-d3b0814a7",
  "@metamask-previews/network-enablement-controller": "4.2.0-preview-d3b0814a7",
  "@metamask-previews/notification-services-controller": "22.0.0-preview-d3b0814a7",
  "@metamask-previews/permission-controller": "12.2.0-preview-d3b0814a7",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-d3b0814a7",
  "@metamask-previews/perps-controller": "1.0.0-preview-d3b0814a7",
  "@metamask-previews/phishing-controller": "16.3.0-preview-d3b0814a7",
  "@metamask-previews/polling-controller": "16.0.3-preview-d3b0814a7",
  "@metamask-previews/preferences-controller": "22.1.0-preview-d3b0814a7",
  "@metamask-previews/profile-metrics-controller": "3.0.1-preview-d3b0814a7",
  "@metamask-previews/profile-sync-controller": "27.1.0-preview-d3b0814a7",
  "@metamask-previews/ramps-controller": "10.2.0-preview-d3b0814a7",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-d3b0814a7",
  "@metamask-previews/remote-feature-flag-controller": "4.1.0-preview-d3b0814a7",
  "@metamask-previews/sample-controllers": "4.0.3-preview-d3b0814a7",
  "@metamask-previews/seedless-onboarding-controller": "8.1.0-preview-d3b0814a7",
  "@metamask-previews/selected-network-controller": "26.0.3-preview-d3b0814a7",
  "@metamask-previews/shield-controller": "5.0.1-preview-d3b0814a7",
  "@metamask-previews/signature-controller": "39.0.4-preview-d3b0814a7",
  "@metamask-previews/storage-service": "1.0.0-preview-d3b0814a7",
  "@metamask-previews/subscription-controller": "6.0.0-preview-d3b0814a7",
  "@metamask-previews/transaction-controller": "62.20.0-preview-d3b0814a7",
  "@metamask-previews/transaction-pay-controller": "16.4.0-preview-d3b0814a7",
  "@metamask-previews/user-operation-controller": "41.0.3-preview-d3b0814a7"
}

Expose rotateRefreshToken as a public method so callers can
trigger refresh token rotation directly. Update tests to exercise
the method via its public API and add coverage for edge cases.
@ieow
Copy link
Contributor

ieow commented Mar 16, 2026

@metamaskbot publish-preview

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

refreshToken,
revokeToken,
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Public rotateRefreshToken lost controller lock protection

Medium Severity

The old renewRefreshToken was wrapped in this.#withControllerLock(), preventing concurrent execution with other locked operations like authenticate or submitPassword. The new rotateRefreshToken has no lock protection but remains public (the CHANGELOG says it "can also be called directly"). An external caller invoking rotateRefreshToken while refreshAuthTokens is in-flight could cause both to read the same revokeToken from the vault and the same refreshToken from state, leading to a double-rotation race: duplicate HTTP renewal calls, last-writer-wins on vault and state updates, and the same old token pair queued for revocation twice.

Fix in Cursor Fix in Web

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@himanshuchawla009 , should we add the pendingRequest variable like we did for the refresh access token ( refreshToken )

@github-actions
Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "4.1.1-preview-fe5e4090d",
  "@metamask-previews/accounts-controller": "36.0.1-preview-fe5e4090d",
  "@metamask-previews/address-book-controller": "7.0.1-preview-fe5e4090d",
  "@metamask-previews/ai-controllers": "0.2.0-preview-fe5e4090d",
  "@metamask-previews/analytics-controller": "1.0.0-preview-fe5e4090d",
  "@metamask-previews/analytics-data-regulation-controller": "0.0.0-preview-fe5e4090d",
  "@metamask-previews/announcement-controller": "8.0.0-preview-fe5e4090d",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-fe5e4090d",
  "@metamask-previews/approval-controller": "8.0.0-preview-fe5e4090d",
  "@metamask-previews/assets-controller": "2.2.0-preview-fe5e4090d",
  "@metamask-previews/assets-controllers": "100.1.0-preview-fe5e4090d",
  "@metamask-previews/base-controller": "9.0.0-preview-fe5e4090d",
  "@metamask-previews/base-data-service": "0.0.0-preview-fe5e4090d",
  "@metamask-previews/bridge-controller": "68.0.0-preview-fe5e4090d",
  "@metamask-previews/bridge-status-controller": "68.0.0-preview-fe5e4090d",
  "@metamask-previews/build-utils": "3.0.4-preview-fe5e4090d",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-fe5e4090d",
  "@metamask-previews/claims-controller": "0.4.2-preview-fe5e4090d",
  "@metamask-previews/client-controller": "1.0.0-preview-fe5e4090d",
  "@metamask-previews/compliance-controller": "1.0.1-preview-fe5e4090d",
  "@metamask-previews/composable-controller": "12.0.0-preview-fe5e4090d",
  "@metamask-previews/config-registry-controller": "0.1.0-preview-fe5e4090d",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-fe5e4090d",
  "@metamask-previews/controller-utils": "11.19.0-preview-fe5e4090d",
  "@metamask-previews/core-backend": "6.0.0-preview-fe5e4090d",
  "@metamask-previews/delegation-controller": "2.0.1-preview-fe5e4090d",
  "@metamask-previews/earn-controller": "11.1.1-preview-fe5e4090d",
  "@metamask-previews/eip-5792-middleware": "3.0.0-preview-fe5e4090d",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-fe5e4090d",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-fe5e4090d",
  "@metamask-previews/ens-controller": "19.0.3-preview-fe5e4090d",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-fe5e4090d",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-fe5e4090d",
  "@metamask-previews/eth-json-rpc-middleware": "23.1.0-preview-fe5e4090d",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-fe5e4090d",
  "@metamask-previews/foundryup": "1.0.1-preview-fe5e4090d",
  "@metamask-previews/gas-fee-controller": "26.0.3-preview-fe5e4090d",
  "@metamask-previews/gator-permissions-controller": "2.0.0-preview-fe5e4090d",
  "@metamask-previews/geolocation-controller": "0.1.1-preview-fe5e4090d",
  "@metamask-previews/json-rpc-engine": "10.2.3-preview-fe5e4090d",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-fe5e4090d",
  "@metamask-previews/keyring-controller": "25.1.0-preview-fe5e4090d",
  "@metamask-previews/logging-controller": "7.0.1-preview-fe5e4090d",
  "@metamask-previews/message-manager": "14.1.0-preview-fe5e4090d",
  "@metamask-previews/messenger": "0.3.0-preview-fe5e4090d",
  "@metamask-previews/multichain-account-service": "7.0.0-preview-fe5e4090d",
  "@metamask-previews/multichain-api-middleware": "1.2.7-preview-fe5e4090d",
  "@metamask-previews/multichain-network-controller": "3.0.4-preview-fe5e4090d",
  "@metamask-previews/multichain-transactions-controller": "7.0.1-preview-fe5e4090d",
  "@metamask-previews/name-controller": "9.0.0-preview-fe5e4090d",
  "@metamask-previews/network-controller": "30.0.0-preview-fe5e4090d",
  "@metamask-previews/network-enablement-controller": "4.2.0-preview-fe5e4090d",
  "@metamask-previews/notification-services-controller": "22.0.0-preview-fe5e4090d",
  "@metamask-previews/permission-controller": "12.2.0-preview-fe5e4090d",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-fe5e4090d",
  "@metamask-previews/perps-controller": "1.0.0-preview-fe5e4090d",
  "@metamask-previews/phishing-controller": "16.3.0-preview-fe5e4090d",
  "@metamask-previews/polling-controller": "16.0.3-preview-fe5e4090d",
  "@metamask-previews/preferences-controller": "22.1.0-preview-fe5e4090d",
  "@metamask-previews/profile-metrics-controller": "3.0.1-preview-fe5e4090d",
  "@metamask-previews/profile-sync-controller": "27.1.0-preview-fe5e4090d",
  "@metamask-previews/ramps-controller": "10.2.0-preview-fe5e4090d",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-fe5e4090d",
  "@metamask-previews/remote-feature-flag-controller": "4.1.0-preview-fe5e4090d",
  "@metamask-previews/sample-controllers": "4.0.3-preview-fe5e4090d",
  "@metamask-previews/seedless-onboarding-controller": "8.1.0-preview-fe5e4090d",
  "@metamask-previews/selected-network-controller": "26.0.3-preview-fe5e4090d",
  "@metamask-previews/shield-controller": "5.0.1-preview-fe5e4090d",
  "@metamask-previews/signature-controller": "39.0.4-preview-fe5e4090d",
  "@metamask-previews/storage-service": "1.0.0-preview-fe5e4090d",
  "@metamask-previews/subscription-controller": "6.0.0-preview-fe5e4090d",
  "@metamask-previews/transaction-controller": "62.20.0-preview-fe5e4090d",
  "@metamask-previews/transaction-pay-controller": "16.4.0-preview-fe5e4090d",
  "@metamask-previews/user-operation-controller": "41.0.3-preview-fe5e4090d"
}

@ieow
Copy link
Contributor

ieow commented Mar 16, 2026

@metamaskbot publish-preview

ieow
ieow previously approved these changes Mar 16, 2026
Copy link
Contributor

@ieow ieow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

…esh wrapper

assertPasswordInSync was called outside executeWithTokenRefresh in
addNewSecretData and changePassword, causing fetchAuthPubKey to send
expired nodeAuthTokens to the backend (FailedToFetchAuthPubKey errors
in Sentry). Moving it inside the callback ensures tokens are refreshed
before the call and retried on expiry.
* @param params.groupedAuthConnectionId - Optional grouped authConnectionId to be used for the authenticate request.
* @param params.socialLoginEmail - The user email from Social login.
* @param params.refreshToken - refresh token for refreshing expired nodeAuthTokens.
* @param params.refreshToken - Refresh token issued during OAuth login. Written to state when provided.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can make revokeToken as required after we refactor re-authenticate logic to #reAuthenticate

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reauthenticate function can be called before unlock when revokeToken is not available, so don't think it can be required.

@lwin-kyaw
Copy link
Contributor

@metamaskbot publish-preview

@github-actions
Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "4.1.1-preview-25d2d4502",
  "@metamask-previews/accounts-controller": "36.0.1-preview-25d2d4502",
  "@metamask-previews/address-book-controller": "7.0.1-preview-25d2d4502",
  "@metamask-previews/ai-controllers": "0.2.0-preview-25d2d4502",
  "@metamask-previews/analytics-controller": "1.0.0-preview-25d2d4502",
  "@metamask-previews/analytics-data-regulation-controller": "0.0.0-preview-25d2d4502",
  "@metamask-previews/announcement-controller": "8.0.0-preview-25d2d4502",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-25d2d4502",
  "@metamask-previews/approval-controller": "8.0.0-preview-25d2d4502",
  "@metamask-previews/assets-controller": "2.2.0-preview-25d2d4502",
  "@metamask-previews/assets-controllers": "100.1.0-preview-25d2d4502",
  "@metamask-previews/base-controller": "9.0.0-preview-25d2d4502",
  "@metamask-previews/base-data-service": "0.0.0-preview-25d2d4502",
  "@metamask-previews/bridge-controller": "68.0.0-preview-25d2d4502",
  "@metamask-previews/bridge-status-controller": "68.0.0-preview-25d2d4502",
  "@metamask-previews/build-utils": "3.0.4-preview-25d2d4502",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-25d2d4502",
  "@metamask-previews/claims-controller": "0.4.2-preview-25d2d4502",
  "@metamask-previews/client-controller": "1.0.0-preview-25d2d4502",
  "@metamask-previews/compliance-controller": "1.0.1-preview-25d2d4502",
  "@metamask-previews/composable-controller": "12.0.0-preview-25d2d4502",
  "@metamask-previews/config-registry-controller": "0.1.0-preview-25d2d4502",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-25d2d4502",
  "@metamask-previews/controller-utils": "11.19.0-preview-25d2d4502",
  "@metamask-previews/core-backend": "6.0.0-preview-25d2d4502",
  "@metamask-previews/delegation-controller": "2.0.1-preview-25d2d4502",
  "@metamask-previews/earn-controller": "11.1.1-preview-25d2d4502",
  "@metamask-previews/eip-5792-middleware": "3.0.0-preview-25d2d4502",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-25d2d4502",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-25d2d4502",
  "@metamask-previews/ens-controller": "19.0.3-preview-25d2d4502",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-25d2d4502",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-25d2d4502",
  "@metamask-previews/eth-json-rpc-middleware": "23.1.0-preview-25d2d4502",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-25d2d4502",
  "@metamask-previews/foundryup": "1.0.1-preview-25d2d4502",
  "@metamask-previews/gas-fee-controller": "26.0.3-preview-25d2d4502",
  "@metamask-previews/gator-permissions-controller": "2.0.0-preview-25d2d4502",
  "@metamask-previews/geolocation-controller": "0.1.1-preview-25d2d4502",
  "@metamask-previews/json-rpc-engine": "10.2.3-preview-25d2d4502",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-25d2d4502",
  "@metamask-previews/keyring-controller": "25.1.0-preview-25d2d4502",
  "@metamask-previews/logging-controller": "7.0.1-preview-25d2d4502",
  "@metamask-previews/message-manager": "14.1.0-preview-25d2d4502",
  "@metamask-previews/messenger": "0.3.0-preview-25d2d4502",
  "@metamask-previews/multichain-account-service": "7.0.0-preview-25d2d4502",
  "@metamask-previews/multichain-api-middleware": "1.2.7-preview-25d2d4502",
  "@metamask-previews/multichain-network-controller": "3.0.4-preview-25d2d4502",
  "@metamask-previews/multichain-transactions-controller": "7.0.1-preview-25d2d4502",
  "@metamask-previews/name-controller": "9.0.0-preview-25d2d4502",
  "@metamask-previews/network-controller": "30.0.0-preview-25d2d4502",
  "@metamask-previews/network-enablement-controller": "4.2.0-preview-25d2d4502",
  "@metamask-previews/notification-services-controller": "22.0.0-preview-25d2d4502",
  "@metamask-previews/permission-controller": "12.2.0-preview-25d2d4502",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-25d2d4502",
  "@metamask-previews/perps-controller": "1.0.0-preview-25d2d4502",
  "@metamask-previews/phishing-controller": "16.3.0-preview-25d2d4502",
  "@metamask-previews/polling-controller": "16.0.3-preview-25d2d4502",
  "@metamask-previews/preferences-controller": "22.1.0-preview-25d2d4502",
  "@metamask-previews/profile-metrics-controller": "3.0.1-preview-25d2d4502",
  "@metamask-previews/profile-sync-controller": "27.1.0-preview-25d2d4502",
  "@metamask-previews/ramps-controller": "10.2.0-preview-25d2d4502",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-25d2d4502",
  "@metamask-previews/remote-feature-flag-controller": "4.1.0-preview-25d2d4502",
  "@metamask-previews/sample-controllers": "4.0.3-preview-25d2d4502",
  "@metamask-previews/seedless-onboarding-controller": "8.1.0-preview-25d2d4502",
  "@metamask-previews/selected-network-controller": "26.0.3-preview-25d2d4502",
  "@metamask-previews/shield-controller": "5.0.1-preview-25d2d4502",
  "@metamask-previews/signature-controller": "39.0.4-preview-25d2d4502",
  "@metamask-previews/storage-service": "1.0.0-preview-25d2d4502",
  "@metamask-previews/subscription-controller": "6.0.0-preview-25d2d4502",
  "@metamask-previews/transaction-controller": "62.20.0-preview-25d2d4502",
  "@metamask-previews/transaction-pay-controller": "16.4.0-preview-25d2d4502",
  "@metamask-previews/user-operation-controller": "41.0.3-preview-25d2d4502"
}

});

const performBackup = async (): Promise<void> => {
await this.#assertPasswordInSync({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we move this.#assertPasswordInSync in side the performBackup/attemptChangePassword ?

Copy link
Contributor

@ieow ieow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@ieow ieow added this pull request to the merge queue Mar 18, 2026
Merged via the queue into main with commit 803917d Mar 18, 2026
322 checks passed
@ieow ieow deleted the fix/seedless-onboarding-refresh-token-tests branch March 18, 2026 14:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants