Skip to content

Common: gate silent shared-FOCI refresh-token redemption on caller authorization (AB#3687466), Fixes AB#3687466#3188

Open
Prvnkmr337 wants to merge 2 commits into
devfrom
prsaminathan/validate-caller-service-p3
Open

Common: gate silent shared-FOCI refresh-token redemption on caller authorization (AB#3687466), Fixes AB#3687466#3188
Prvnkmr337 wants to merge 2 commits into
devfrom
prsaminathan/validate-caller-service-p3

Conversation

@Prvnkmr337

@Prvnkmr337 Prvnkmr337 commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Problem

BaseController.getAccountWithFRTIfAvailable redeems the device-wide shared family-of-client-id (FOCI) refresh token for any silent request whose named client id belongs to a token family, without checking whether the calling app is authorized to share FOCI tokens. On the brokered silent path this lets an unauthorized local app mint a token from another app's shared FOCI refresh token by naming a first-party family client id.

Phased delivery

AB#3687466 is delivered in phases, each as its own independently reviewable and independently flighted PR:

  • Phase 1: re-pin the silent bound-service caller on acquireTokenSilently.
  • Phase 2: gate device-wide FOCI account enumeration on getAccounts.
  • Phase 3 (this PR): gate silent shared-FOCI token redemption (broker-side backstop).
  • Phase 4: staged flight rollout (observe -> enforce). Cross-repo changes merge common-first, then broker; this Common change is the base dependency for the corresponding broker PR.

Solution

Carry the caller's FOCI authorization on the silent command parameters and gate the shared-FOCI redemption on it, while leaving the caller's own UID-partitioned cache path untouched:

  • Add SilentTokenCommandParameters.callerAuthorizedForFoci, a @Builder.Default boolean defaulting to true. Default-true keeps non-brokered silent flows (e.g. LocalMSALController, which owns its tokens and has no cross-app "authorized to share" concept) and every existing caller unaffected; no interface change.
  • In BaseController.getAccountWithFRTIfAvailable, before redeeming the shared family refresh token, return null when callerAuthorizedForFoci is false. This falls through to the caller's own UID-partitioned cache only (same as the "no FRT found" path), so an unauthorized caller never has the device-wide shared FOCI RT redeemed on its behalf.

The authorization decision itself is evaluated in the broker layer and set on the parameters; this change is a pure, backward-compatible controller-layer gate with no schema change.

Testing

Unit tests (BaseControllerFociRedemptionGateTest):

  • callerAuthorizedForFoci = false -> shared FoCI RT redemption skipped (returns null, falls through to the caller's own cache);
  • callerAuthorizedForFoci = true -> redemption proceeds (no regression for legitimate family apps and non-brokered callers).

Work item: AB#3687466

Copilot-Session: 0436fb4a-df54-48de-a086-55aadb1eb200

…thorization (AB#3687466)

Problem
-------
BaseController.getAccountWithFRTIfAvailable redeems the device-wide shared
family-of-client-id (FOCI) refresh token for any silent request whose named
client id belongs to a token family, without checking whether the calling app
is authorized to share FOCI tokens. On the brokered silent path this lets an
unauthorized local app mint a token from another app's shared FOCI refresh
token by naming a first-party family client id.

Phased delivery
---------------
AB#3687466 is delivered in phases, each as its own independently reviewable
and independently flighted PR:
  - Phase 1: re-pin the silent bound-service caller on acquireTokenSilently.
  - Phase 2: gate device-wide FOCI account enumeration on getAccounts.
  - Phase 3 (this PR): gate silent shared-FOCI token redemption (broker-side
    backstop).
  - Phase 4: staged flight rollout (observe -> enforce).
Cross-repo changes merge common-first, then broker; this Common change is the
base dependency for the corresponding broker PR.

Solution
--------
Carry the caller's FOCI authorization on the silent command parameters and
gate the shared-FOCI redemption on it, while leaving the caller's own
UID-partitioned cache path untouched:

- Add SilentTokenCommandParameters.callerAuthorizedForFoci, a @Builder.Default
  boolean defaulting to true. Default-true keeps non-brokered silent flows
  (e.g. LocalMSALController, which owns its tokens and has no cross-app
  "authorized to share" concept) and every existing caller unaffected; no
  interface change.
- In BaseController.getAccountWithFRTIfAvailable, before redeeming the shared
  family refresh token, return null when callerAuthorizedForFoci is false. This
  falls through to the caller's own UID-partitioned cache only (same as the
  "no FRT found" path), so an unauthorized caller never has the device-wide
  shared FOCI RT redeemed on its behalf.

The authorization decision itself is evaluated in the broker layer and set on
the parameters; this change is a pure, backward-compatible controller-layer
gate with no schema change.

Testing
-------
Unit tests (BaseControllerFociRedemptionGateTest):
- callerAuthorizedForFoci = false -> shared FoCI RT redemption skipped
  (returns null, falls through to the caller's own cache);
- callerAuthorizedForFoci = true -> redemption proceeds (no regression for
  legitimate family apps and non-brokered callers).

Work item: AB#3687466

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0436fb4a-df54-48de-a086-55aadb1eb200
@github-actions

Copy link
Copy Markdown

✅ Work item link check complete. Description contains link AB#3687466 to an Azure Boards work item.

@github-actions

Copy link
Copy Markdown

❌ Work item link check failed. Description contains AB#3687466 but the Bot could not link it to an Azure Boards work item.

Click here to learn more.

@github-actions github-actions Bot changed the title Common: gate silent shared-FOCI refresh-token redemption on caller authorization (AB#3687466) Common: gate silent shared-FOCI refresh-token redemption on caller authorization (AB#3687466), Fixes AB#3687466 Jul 16, 2026
@Prvnkmr337
Prvnkmr337 requested a review from Copilot July 16, 2026 22:36
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Some pipeline(s) encountered errors during trigger evaluation.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

…FoCI redemption gate (AB#3687466)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0436fb4a-df54-48de-a086-55aadb1eb200

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@Prvnkmr337
Prvnkmr337 marked this pull request as ready for review July 17, 2026 20:44
@Prvnkmr337
Prvnkmr337 requested review from a team as code owners July 17, 2026 20:44
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.

2 participants