Skip to content

Commit eb59900

Browse files
authored
restrict FOCI to PCA and improve logging (#5030)
* Fix for 4988 - restrict FOCI to PCA and improve logging * Update test
1 parent 01e79b1 commit eb59900

File tree

3 files changed

+49
-9
lines changed

3 files changed

+49
-9
lines changed

src/client/Microsoft.Identity.Client/Internal/Requests/Silent/CacheSilentStrategy.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,17 @@ private async Task<AuthenticationResult> CreateAuthenticationResultAsync(MsalAcc
174174

175175
private async Task<MsalTokenResponse> TryGetTokenUsingFociAsync(CancellationToken cancellationToken)
176176
{
177-
if (!ServiceBundle.PlatformProxy.GetFeatureFlags().IsFociEnabled)
177+
var logger = AuthenticationRequestParameters.RequestContext.Logger;
178+
179+
// FOCI is only supported on public client desktop apps
180+
if (!(ServiceBundle.PlatformProxy.GetFeatureFlags().IsFociEnabled &&
181+
ServiceBundle.Config.IsPublicClient))
178182
{
183+
logger.Verbose(() =>
184+
"[FOCI] Skip FOCI on confidential client and on mobile apps");
179185
return null;
180186
}
181187

182-
var logger = AuthenticationRequestParameters.RequestContext.Logger;
183-
184188
// If the app was just added to the family, the app metadata will reflect this
185189
// after the first RT exchanged.
186190
bool? isFamilyMember = await CacheManager.IsAppFociMemberAsync(TheOnlyFamilyId).ConfigureAwait(false);
@@ -239,7 +243,8 @@ private async Task<MsalRefreshTokenCacheItem> FindRefreshTokenOrFailAsync()
239243
var msalRefreshTokenItem = await CacheManager.FindRefreshTokenAsync().ConfigureAwait(false);
240244
if (msalRefreshTokenItem == null)
241245
{
242-
AuthenticationRequestParameters.RequestContext.Logger.Verbose(()=>"No Refresh Token was found in the cache. ");
246+
AuthenticationRequestParameters.RequestContext.Logger.Warning(
247+
"No Refresh Token was found in the cache. ");
243248

244249
throw new MsalUiRequiredException(
245250
MsalError.NoTokensFoundError,

src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,7 @@ private static void FilterRefreshTokensByHomeAccountIdOrAssertion(
901901
// version of MSAL which did not record app metadata.
902902
if (appMetadata == null)
903903
{
904-
logger.Warning("No app metadata found. Returning unknown. ");
904+
logger.Verbose(() => "No app metadata found. Returning unknown. ");
905905
return null;
906906
}
907907

@@ -917,7 +917,9 @@ async Task<IEnumerable<IAccount>> ITokenCacheInternal.GetAccountsAsync(Authentic
917917
{
918918
var logger = requestParameters.RequestContext.Logger;
919919
var environment = requestParameters.AuthorityInfo.Host;
920-
bool filterByClientId = !_featureFlags.IsFociEnabled;
920+
921+
// FOCI is only enabled on public client desktop apps
922+
bool filterByClientId = !(requestParameters.AppConfig.IsPublicClient && _featureFlags.IsFociEnabled);
921923

922924
// this will either be the home account ID or null, it can never be OBO assertion or tenant ID
923925
string partitionKey = CacheKeyFactory.GetKeyFromRequest(requestParameters);

tests/Microsoft.Identity.Test.Unit/RequestsTests/FociTests.cs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,39 @@ public async Task FociHappyPathAsync()
6969
}
7070
}
7171

72+
[TestMethod]
73+
public async Task NoFociForCca()
74+
{
75+
// Arrange
76+
using (_harness = CreateTestHarness())
77+
{
78+
InitApps();
79+
80+
var appC = ConfidentialClientApplicationBuilder
81+
.Create("other_client_id")
82+
.WithClientSecret(TestConstants.ClientSecret)
83+
.WithHttpManager(_harness.HttpManager)
84+
.WithAuthority(TestConstants.AuthorityUtidTenant)
85+
.BuildConcrete();
86+
87+
ConfigureCacheSerialization(appC);
88+
89+
// Act
90+
await InteractiveAsync(_appA, ServerTokenResponse.FociToken).ConfigureAwait(false);
91+
var appAAccount= (await _appA.GetAccountsAsync().ConfigureAwait(false)).Single();
92+
var appCAccount = await appC.GetAccountAsync(appAAccount.HomeAccountId.Identifier).ConfigureAwait(false);
93+
94+
// Assert
95+
Assert.IsNull(appCAccount);
96+
97+
// Act
98+
var ex = await AssertException.TaskThrowsAsync<MsalUiRequiredException>(
99+
() => appC.AcquireTokenSilent(TestConstants.s_scope, appAAccount).ExecuteAsync()).ConfigureAwait(false);
100+
101+
Assert.AreEqual(MsalError.NoTokensFoundError, ex.ErrorCode);
102+
}
103+
}
104+
72105
/// <summary>
73106
/// A is part of the family, B is not. B fails gracefully trying to get a token silently
74107
/// </summary>
@@ -375,15 +408,15 @@ private void InitApps()
375408
ConfigureCacheSerialization(_appB);
376409
}
377410

378-
private void ConfigureCacheSerialization(IPublicClientApplication pca)
411+
private void ConfigureCacheSerialization(IClientApplicationBase app)
379412
{
380-
pca.UserTokenCache.SetBeforeAccess(notificationArgs =>
413+
app.UserTokenCache.SetBeforeAccess(notificationArgs =>
381414
{
382415
byte[] bytes = Encoding.UTF8.GetBytes(_inMemoryCache);
383416
notificationArgs.TokenCache.DeserializeMsalV3(bytes);
384417
});
385418

386-
pca.UserTokenCache.SetAfterAccess(notificationArgs =>
419+
app.UserTokenCache.SetAfterAccess(notificationArgs =>
387420
{
388421
if (notificationArgs.HasStateChanged)
389422
{

0 commit comments

Comments
 (0)