Skip to content
Merged

FOCI #957

Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix a serialization issue and add better assert in ut
  • Loading branch information
bgavrilMS committed Mar 16, 2019
commit f94f9460b8e62491557a20dae9050d9be78d578e
15 changes: 15 additions & 0 deletions src/Microsoft.Identity.Client/Cache/CacheSessionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,24 @@ public MsalIdTokenCacheItem GetIdTokenCacheItem(MsalIdTokenCacheKey idTokenCache
return TokenCacheInternal.GetIdTokenCacheItem(idTokenCacheKey, _requestParams.RequestContext);
}

public Task<MsalRefreshTokenCacheItem> FindFamilyRefreshTokenAsync(string familyId)
{
if (String.IsNullOrEmpty(familyId))
{
throw new ArgumentNullException(nameof(familyId));
}

return TokenCacheInternal.FindRefreshTokenAsync(_requestParams, familyId);
}

public Task<MsalRefreshTokenCacheItem> FindRefreshTokenAsync()
{
return TokenCacheInternal.FindRefreshTokenAsync(_requestParams);
}

public Task<bool?> IsAppFociMemberAsync(string familyId)
{
return TokenCacheInternal.CheckAppIsFamilyMemberAsync(_requestParams, familyId);
}
}
}
2 changes: 2 additions & 0 deletions src/Microsoft.Identity.Client/Cache/ICacheSessionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,7 @@ internal interface ICacheSessionManager
Tuple<MsalAccessTokenCacheItem, MsalIdTokenCacheItem> SaveTokenResponse(MsalTokenResponse tokenResponse);
MsalIdTokenCacheItem GetIdTokenCacheItem(MsalIdTokenCacheKey idTokenCacheKey);
Task<MsalRefreshTokenCacheItem> FindRefreshTokenAsync();
Task<MsalRefreshTokenCacheItem> FindFamilyRefreshTokenAsync(string familyId);
Task<bool?> IsAppFociMemberAsync(string familyId);
}
}
8 changes: 3 additions & 5 deletions src/Microsoft.Identity.Client/Cache/ITokenCacheAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ internal interface ITokenCacheAccessor

MsalAccountCacheItem GetAccount(MsalAccountCacheKey accountKey);

MsalAppMetadataCacheItem GetAppMetadata(MsalAppMetadataCacheKey appMetadataKey);

void DeleteAccessToken(MsalAccessTokenCacheKey cacheKey);

void DeleteRefreshToken(MsalRefreshTokenCacheKey cacheKey);
Expand All @@ -66,13 +68,9 @@ internal interface ITokenCacheAccessor
IEnumerable<MsalIdTokenCacheItem> GetAllIdTokens();

IEnumerable<MsalAccountCacheItem> GetAllAccounts();

/// <remarks>
/// MSAL should not rely on this method except for serialization
/// </remarks>

IEnumerable<MsalAppMetadataCacheItem> GetAllAppMetadata();

MsalAppMetadataCacheItem ReadAppMetadata(MsalAppMetadataCacheKey appMetadataKey);

#if iOS
void SetiOSKeychainSecurityGroup(string keychainSecurityGroup);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,20 @@ internal static CacheSerializationContract FromJsonString(string json)
}
}

// App Metadata
if (root.ContainsKey(StorageJsonValues.AppMetadata))
{
foreach (var token in root[StorageJsonValues.AppMetadata]
.Values())
{
if (token is JObject j)
{
var item = MsalAppMetadataCacheItem.FromJObject(j);
contract.AppMetadata[item.GetKey().ToString()] = item;
}
}
}

return contract;
}

Expand Down Expand Up @@ -158,6 +172,9 @@ internal string ToJsonString()
appMetadataRoot[kvp.Key] = kvp.Value.ToJObject();
}

root[StorageJsonValues.AppMetadata] = appMetadataRoot;


return root.ToString();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,6 @@ internal MsalAccountCacheItem()
AuthorityType = Cache.AuthorityType.MSSTS.ToString();
}

internal MsalAccountCacheItem(string environment, MsalTokenResponse response)
: this()
{
var idToken = IdToken.Parse(response.IdToken);

Init(
environment,
idToken?.ObjectId,
response.ClientInfo,
idToken.Name,
idToken.PreferredUsername,
idToken.TenantId,
idToken.GivenName,
idToken.FamilyName);
}

internal MsalAccountCacheItem(
string environment,
MsalTokenResponse response,
Expand All @@ -77,7 +61,7 @@ internal MsalAccountCacheItem(
idToken.FamilyName);
}

internal MsalAccountCacheItem(
internal /* for test */ MsalAccountCacheItem(
string environment,
string localAccountId,
string rawClientInfo,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ internal MsalRefreshTokenCacheItem(

internal MsalRefreshTokenCacheKey GetKey()
{
return new MsalRefreshTokenCacheKey(Environment, ClientId, HomeAccountId);
return new MsalRefreshTokenCacheKey(Environment, ClientId, HomeAccountId, FamilyId);
}

internal static MsalRefreshTokenCacheItem FromJsonString(string json)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,17 @@ public MsalAppMetadataCacheKey(string clientId, string environment)
/// <returns></returns>
public override string ToString()
{
return $"{StorageJsonValues.AppMetadata}{MsalCacheKeys.CacheKeyDelimiter}{_environment}{MsalCacheKeys.CacheKeyDelimiter}{_clientId}";
return ($"{StorageJsonKeys.AppMetadata}{MsalCacheKeys.CacheKeyDelimiter}" +
$"{_environment}{MsalCacheKeys.CacheKeyDelimiter}{_clientId}").ToLowerInvariant();
}

#region iOS

public string iOSService => $"{StorageJsonValues.AppMetadata}{MsalCacheKeys.CacheKeyDelimiter}{_clientId}";
public string iOSService => $"{StorageJsonValues.AppMetadata}{MsalCacheKeys.CacheKeyDelimiter}{_clientId}".ToLowerInvariant();

public string iOSGeneric => "1";

public string iOSAccount => $"{_environment}";
public string iOSAccount => $"{_environment}".ToLowerInvariant();

public int iOSType => (int)MsalCacheKeys.iOSCredentialAttrType.AppMetadata;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,25 @@ namespace Microsoft.Identity.Client.Cache.Keys
/// An object representing the key of the token cache RT dictionary. The
/// format of the key is not important for this library, as long as it is unique.
/// </summary>
internal class MsalRefreshTokenCacheKey : IiOSKey
/// <remarks>
/// Normal RTs are scoped by env, account_id and clientID
/// FRTs are scoped by env, account_id and familyID (clientID exists, but is irrelevant)
/// </remarks>
internal class MsalRefreshTokenCacheKey : IiOSKey //TODO bogavril: add a base class with FRT key?
{
private readonly string _environment;
private readonly string _homeAccountId;
private readonly string _clientId;
private readonly string _familyId;

internal MsalRefreshTokenCacheKey(string environment, string clientId, string userIdentifier)
/// <summary>
/// Constructor
/// </summary>
/// <param name="environment"></param>
/// <param name="clientId"></param>
/// <param name="userIdentifier"></param>
/// <param name="familyId">Can be null or empty, denoting a normal RT. A value signifies an FRT.</param>
internal MsalRefreshTokenCacheKey(string environment, string clientId, string userIdentifier, string familyId)
{
if (string.IsNullOrEmpty(environment))
{
Expand All @@ -54,30 +66,71 @@ internal MsalRefreshTokenCacheKey(string environment, string clientId, string us
_environment = environment;
_homeAccountId = userIdentifier;
_clientId = clientId;
_familyId = familyId;
}

public override string ToString()
{
// FRT
if (!String.IsNullOrWhiteSpace(_familyId))
{
string d = MsalCacheKeys.CacheKeyDelimiter;
return $"{_homeAccountId}{d}{_environment}{d}{StorageJsonValues.CredentialTypeRefreshToken}{d}{_familyId}{d}{d}".ToLowerInvariant();

}

return MsalCacheKeys.GetCredentialKey(
_homeAccountId,
_environment,
StorageJsonValues.CredentialTypeRefreshToken,
_clientId,
tenantId: null,
scopes: null);
_homeAccountId,
_environment,
StorageJsonValues.CredentialTypeRefreshToken,
_clientId,
tenantId: null,
scopes: null);
}

#region iOS

public string iOSAccount
{
get
{
return MsalCacheKeys.GetiOSAccountKey(_homeAccountId, _environment);
}
}

public string iOSAccount => MsalCacheKeys.GetiOSAccountKey(_homeAccountId, _environment);
public string iOSGeneric
{
get
{
// FRT
if (!String.IsNullOrWhiteSpace(_familyId))
{
return $"{StorageJsonValues.CredentialTypeRefreshToken}{MsalCacheKeys.CacheKeyDelimiter}{_familyId}{MsalCacheKeys.CacheKeyDelimiter}".ToLowerInvariant();
}

public string iOSGeneric => MsalCacheKeys.GetiOSGenericKey(StorageJsonValues.CredentialTypeRefreshToken, _clientId, tenantId: null);
return MsalCacheKeys.GetiOSGenericKey(StorageJsonValues.CredentialTypeRefreshToken, _clientId, tenantId: null);

public string iOSService => MsalCacheKeys.GetiOSServiceKey(StorageJsonValues.CredentialTypeRefreshToken, _clientId, tenantId: null, scopes: null);
}
}

public string iOSService
{
get
{
// FRT
if (!String.IsNullOrWhiteSpace(_familyId))
{
return $"{StorageJsonValues.CredentialTypeRefreshToken}{MsalCacheKeys.CacheKeyDelimiter}{_familyId}{MsalCacheKeys.CacheKeyDelimiter}{MsalCacheKeys.CacheKeyDelimiter}".ToLowerInvariant();
}

return MsalCacheKeys.GetiOSServiceKey(StorageJsonValues.CredentialTypeRefreshToken, _clientId, tenantId: null, scopes: null);
}
}

public int iOSType => (int)MsalCacheKeys.iOSCredentialAttrType.RefreshToken;



#endregion
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/Microsoft.Identity.Client/Cache/StorageJsonKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ internal static class StorageJsonKeys
public const string ExtendedExpiresOn = "extended_expires_on";
public const string ClientInfo = "client_info";
public const string FamilyId = "family_id";
public const string AppMetadata = "appmetadata";


// todo(cache): this needs to be added to the spec. needed for OBO flow on .NET.
public const string UserAssertionHash = "user_assertion_hash";
Expand All @@ -58,4 +60,4 @@ internal static class StorageJsonKeys
// this is here for back compat
public const string ExtendedExpiresOn_MsalCompat = "ext_expires_on";
}
}
}
2 changes: 1 addition & 1 deletion src/Microsoft.Identity.Client/Cache/StorageJsonValues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ internal static class StorageJsonValues
public const string AccountRootKey = "Account";
public const string CredentialTypeOther = "Other";

public const string AppMetadata = "appmetadata";
public const string AppMetadata = "AppMetadata";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ public IEnumerable<MsalAppMetadataCacheItem> GetAllAppMetadata()
{
throw new NotImplementedException();
}

public MsalAppMetadataCacheItem GetAppMetadata(MsalAppMetadataCacheKey appMetadataKey)
{
throw new NotImplementedException();
}
#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ internal class iOSFeatureFlags : IFeatureFlags
{
/// <summary>
/// FOCI has not been tested on iOS
/// <summary>
/// </summary>
public bool IsFociEnabled => false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,11 @@ public IEnumerable<MsalAppMetadataCacheItem> GetAllAppMetadata()
{
throw new NotImplementedException();
}

public MsalAppMetadataCacheItem GetAppMetadata(MsalAppMetadataCacheKey appMetadataKey)
{
throw new NotImplementedException();
}
#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,4 @@ private void ValidateCacheEntitiesEnvironment(ITokenCacheInternal cache, string
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ private ITokenCacheAccessor CreateTokenCacheAccessor()
accessor.SaveRefreshToken(item);
}

// Create an FRT
var frt = CreateRefreshTokenItem();
frt.FamilyId = "1";
accessor.SaveRefreshToken(frt);

for (int i = 1; i <= NumIdTokens; i++)
{
var item = CreateIdTokenItem();
Expand Down Expand Up @@ -421,21 +426,31 @@ public void TestDictionarySerialization()
#region JSON SERIALIZATION TESTS

[TestMethod]
[DeploymentItem(@"Resources\ExpectedTokenCache.json")]
public void TestJsonSerialization()
{
string expectedJson = File.ReadAllText(ResourceHelper.GetTestResourceRelativePath("ExpectedTokenCache.json"));
var accessor = CreateTokenCacheAccessor();

var s1 = new TokenCacheJsonSerializer(accessor);
byte[] bytes = s1.Serialize();
string json = new UTF8Encoding().GetString(bytes);
string actualJson = new UTF8Encoding().GetString(bytes);

Assert.IsTrue(JToken.DeepEquals(JObject.Parse(actualJson), JObject.Parse(expectedJson)));

var otherAccessor = new InMemoryTokenCacheAccessor();
var s2 = new TokenCacheJsonSerializer(otherAccessor);
s2.Deserialize(bytes);

AssertAccessorsAreEqual(accessor, otherAccessor);

// serialize again to detect errors that come from deserialization
byte[] bytes2 = s2.Serialize();
string actualJson2 = new UTF8Encoding().GetString(bytes2);
Assert.IsTrue(JToken.DeepEquals(JObject.Parse(actualJson2), JObject.Parse(expectedJson)));
}


#endregion // JSON SERIALIZATION TESTS

[TestMethod]
Expand Down Expand Up @@ -653,7 +668,17 @@ private void AssertAccessTokenCacheItemsAreEqual(MsalAccessTokenCacheItem expect
private void AssertRefreshTokenCacheItemsAreEqual(MsalRefreshTokenCacheItem expected, MsalRefreshTokenCacheItem actual)
{
AssertCredentialCacheItemBaseItemsAreEqual(expected, actual);
Assert.AreEqual(expected.FamilyId, actual.FamilyId);

if (string.IsNullOrEmpty(expected.FamilyId))
{
Assert.IsTrue(string.IsNullOrEmpty(actual.FamilyId));
}
else
{
Assert.AreEqual(expected.FamilyId, actual.FamilyId);
}


}

private void AssertIdTokenCacheItemsAreEqual(MsalIdTokenCacheItem expected, MsalIdTokenCacheItem actual)
Expand Down
Loading