From 446258dbf198af9fd59fe8ca740591dae2f19dc4 Mon Sep 17 00:00:00 2001 From: sameerag Date: Sun, 10 May 2020 22:01:11 -0700 Subject: [PATCH 01/17] Edit tests to match the new key generation --- .../src/unifiedCache/UnifiedCacheManager.ts | 6 +-- .../entities/AccessTokenEntity.ts | 20 +------ .../src/unifiedCache/entities/Credential.ts | 53 ++++++++++++++++++- .../unifiedCache/entities/IdTokenEntity.ts | 20 +------ .../entities/RefreshTokenEntity.ts | 24 +-------- .../unifiedCache/UnifiedCacheManager.spec.ts | 4 +- .../entities/AccessTokenEntity.spec.ts | 2 +- .../entities/IdTokenEntity.spec.ts | 2 +- .../entities/RefreshTokenEntity.spec.ts | 4 +- .../unifiedCache/entities/cacheConstants.ts | 10 ++-- 10 files changed, 71 insertions(+), 74 deletions(-) diff --git a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts index 44d78f0842..3fdc2a135a 100644 --- a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts +++ b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts @@ -89,9 +89,9 @@ export class UnifiedCacheManager { idToken: IdTokenEntity, refreshToken: RefreshTokenEntity ): void { - this.inMemoryCache.accessTokens[accessToken.generateAccessTokenEntityKey()] = accessToken; - this.inMemoryCache.idTokens[idToken.generateIdTokenEntityKey()] = idToken; - this.inMemoryCache.refreshTokens[refreshToken.generateRefreshTokenEntityKey()] = refreshToken; + this.inMemoryCache.accessTokens[accessToken.generateCredentialKey()] = accessToken; + this.inMemoryCache.idTokens[idToken.generateCredentialKey()] = idToken; + this.inMemoryCache.refreshTokens[refreshToken.generateCredentialKey()] = refreshToken; } /** diff --git a/lib/msal-common/src/unifiedCache/entities/AccessTokenEntity.ts b/lib/msal-common/src/unifiedCache/entities/AccessTokenEntity.ts index 586316bf19..72268536d3 100644 --- a/lib/msal-common/src/unifiedCache/entities/AccessTokenEntity.ts +++ b/lib/msal-common/src/unifiedCache/entities/AccessTokenEntity.ts @@ -4,7 +4,7 @@ */ import { Credential } from "./Credential"; -import { Separators } from "../../utils/Constants"; +import { CredentialType } from "../../utils/Constants"; import { AuthenticationResult } from "../../response/AuthenticationResult"; /** @@ -20,22 +20,6 @@ export class AccessTokenEntity extends Credential { keyId?: string; // for POP and SSH tokenTypes tokenType?: string; - /** - * Generate Account Cache Key as per the schema: -- - */ - public generateAccessTokenEntityKey(): string { - const accessTokenKeyArray: Array = [ - this.homeAccountId, - this.environment, - this.credentialType, - this.clientId, - this.realm, - this.target - ]; - - return accessTokenKeyArray.join(Separators.CACHE_KEY_SEPARATOR).toLowerCase(); - } - /** * Create AccessTokenEntity * @param homeAccountId @@ -52,7 +36,7 @@ export class AccessTokenEntity extends Credential { const atEntity: AccessTokenEntity = new AccessTokenEntity(); atEntity.homeAccountId = homeAccountId; - atEntity.credentialType = "AccessToken"; + atEntity.credentialType = CredentialType.ACCESS_TOKEN; atEntity.secret = authenticationResult.accessToken; const date = new Date(); diff --git a/lib/msal-common/src/unifiedCache/entities/Credential.ts b/lib/msal-common/src/unifiedCache/entities/Credential.ts index 78309fd362..e384585713 100644 --- a/lib/msal-common/src/unifiedCache/entities/Credential.ts +++ b/lib/msal-common/src/unifiedCache/entities/Credential.ts @@ -3,13 +3,62 @@ * Licensed under the MIT License. */ +import { Separators, CredentialType } from "../../utils/Constants"; + /** * Base type for credentials to be stored in the cache: eg: ACCESS_TOKEN, ID_TOKEN etc */ export class Credential { homeAccountId: string; environment: string; - credentialType: string; + credentialType: CredentialType; clientId: string; secret: string; -}; + familyId?: string; + realm?: string; + target?: string; + + /** + * Generate Account Id key component as per the schema: - + */ + generateAccountId(): string { + const accountId: Array = [this.homeAccountId, this.environment]; + return accountId.join(Separators.CACHE_KEY_SEPARATOR).toLowerCase(); + } + + /** + * Generate Credential Id key component as per the schema: -- + */ + generateCredentialId(): string { + const clientOrFamilyId = CredentialType.REFRESH_TOKEN + ? this.familyId || this.clientId + : this.clientId; + const credentialId: Array = [ + this.credentialType, + clientOrFamilyId, + this.realm || "", + ]; + + return credentialId.join(Separators.CACHE_KEY_SEPARATOR).toLowerCase(); + } + + /** + * Generate target key component as per schema: + */ + generateTarget(): string { + return (this.target || "").toLowerCase(); + } + + /** + * generates credential key + */ + generateCredentialKey(): string { + const credentialKey = [ + this.generateAccountId(), + this.generateCredentialId(), + this.generateTarget(), + ]; + + return credentialKey.join(Separators.CACHE_KEY_SEPARATOR).toLowerCase(); + } +} diff --git a/lib/msal-common/src/unifiedCache/entities/IdTokenEntity.ts b/lib/msal-common/src/unifiedCache/entities/IdTokenEntity.ts index fd674ac5d5..e1addac5cd 100644 --- a/lib/msal-common/src/unifiedCache/entities/IdTokenEntity.ts +++ b/lib/msal-common/src/unifiedCache/entities/IdTokenEntity.ts @@ -4,7 +4,7 @@ */ import { Credential } from "./Credential"; -import { Separators } from "../../utils/Constants"; +import { CredentialType } from "../../utils/Constants"; import { AuthenticationResult } from "../../response/AuthenticationResult"; /** @@ -13,22 +13,6 @@ import { AuthenticationResult } from "../../response/AuthenticationResult"; export class IdTokenEntity extends Credential { realm: string; - /** - * Generate Account Cache Key as per the schema: -- - */ - generateIdTokenEntityKey(): string { - const idTokenKeyArray: Array = [ - this.homeAccountId, - this.environment, - this.credentialType, - this.clientId, - this.realm, - "" // target - ]; - - return idTokenKeyArray.join(Separators.CACHE_KEY_SEPARATOR).toLowerCase(); - } - /** * Create IdTokenEntity * @param homeAccountId @@ -44,7 +28,7 @@ export class IdTokenEntity extends Credential { ): IdTokenEntity { const idTokenEntity = new IdTokenEntity(); - idTokenEntity.credentialType = "IdToken"; + idTokenEntity.credentialType = CredentialType.ID_TOKEN; idTokenEntity.homeAccountId = homeAccountId; idTokenEntity.environment = environment; idTokenEntity.clientId = clientId; diff --git a/lib/msal-common/src/unifiedCache/entities/RefreshTokenEntity.ts b/lib/msal-common/src/unifiedCache/entities/RefreshTokenEntity.ts index ce81f4d98b..2397e59473 100644 --- a/lib/msal-common/src/unifiedCache/entities/RefreshTokenEntity.ts +++ b/lib/msal-common/src/unifiedCache/entities/RefreshTokenEntity.ts @@ -4,7 +4,7 @@ */ import { Credential } from "./Credential"; -import { Separators } from "../../utils/Constants"; +import { CredentialType } from "../../utils/Constants"; import { AuthenticationResult } from "../../response/AuthenticationResult"; /** @@ -13,26 +13,6 @@ import { AuthenticationResult } from "../../response/AuthenticationResult"; export class RefreshTokenEntity extends Credential { familyId?: string; - /** - * Generate Account Cache Key as per the schema: -- - */ - generateRefreshTokenEntityKey(): string { - const refreshTokenKeyArray: Array = [ - this.homeAccountId, - this.environment, - this.credentialType - ]; - - // append familyId if populted, else fallback to clientId - refreshTokenKeyArray.push(this.familyId || this.clientId); - - // realm and target - empty string "" for REFRESH_TOKEN type; target (scopes) is added only if it is resource specific refresh token - refreshTokenKeyArray.push(""); - refreshTokenKeyArray.push(""); - - return refreshTokenKeyArray.join(Separators.CACHE_KEY_SEPARATOR).toLowerCase(); - } - /** * Create RefreshTokenEntity * @param homeAccountId @@ -50,7 +30,7 @@ export class RefreshTokenEntity extends Credential { const rtEntity = new RefreshTokenEntity(); rtEntity.clientId = clientId; - rtEntity.credentialType = "RefreshToken"; + rtEntity.credentialType = CredentialType.REFRESH_TOKEN; rtEntity.environment = environment; rtEntity.homeAccountId = homeAccountId; rtEntity.secret = refreshToken; diff --git a/lib/msal-common/test/unifiedCache/UnifiedCacheManager.spec.ts b/lib/msal-common/test/unifiedCache/UnifiedCacheManager.spec.ts index f65d723117..ec1064f249 100644 --- a/lib/msal-common/test/unifiedCache/UnifiedCacheManager.spec.ts +++ b/lib/msal-common/test/unifiedCache/UnifiedCacheManager.spec.ts @@ -49,9 +49,9 @@ describe("UnifiedCacheManager test cases", () => { // create mock AccessToken const atOne = mockCache.createMockATOne(); - const atOneKey = atOne.generateAccessTokenEntityKey(); + const atOneKey = atOne.generateCredentialKey(); const atTwo = mockCache.createMockATTwo(); - const atTwoKey = atTwo.generateAccessTokenEntityKey(); + const atTwoKey = atTwo.generateCredentialKey(); expect(Object.keys(unifiedCacheManager.getCacheInMemory().accessTokens).length).to.equal(2); expect(unifiedCacheManager.getCacheInMemory().accessTokens[atOneKey]).to.eql(atOne); diff --git a/lib/msal-common/test/unifiedCache/entities/AccessTokenEntity.spec.ts b/lib/msal-common/test/unifiedCache/entities/AccessTokenEntity.spec.ts index 14bb19b43c..918899dbfb 100644 --- a/lib/msal-common/test/unifiedCache/entities/AccessTokenEntity.spec.ts +++ b/lib/msal-common/test/unifiedCache/entities/AccessTokenEntity.spec.ts @@ -11,7 +11,7 @@ describe("AccessTokenEntity.ts Unit Tests", () => { it("Create an AccessTokenCacheEntity entity", () => { let at = mockCache.createMockATOne(); - expect(at.generateAccessTokenEntityKey()).to.eql( + expect(at.generateCredentialKey()).to.eql( "uid.utid-login.microsoftonline.com-accesstoken-mock_client_id-microsoft-scope1 scope2 scope3" ); }); diff --git a/lib/msal-common/test/unifiedCache/entities/IdTokenEntity.spec.ts b/lib/msal-common/test/unifiedCache/entities/IdTokenEntity.spec.ts index 922f0ebc9c..1e3357db44 100644 --- a/lib/msal-common/test/unifiedCache/entities/IdTokenEntity.spec.ts +++ b/lib/msal-common/test/unifiedCache/entities/IdTokenEntity.spec.ts @@ -11,7 +11,7 @@ describe("IdTokenEntity.ts Unit Tests", () => { it("Create an IdTokenEntity", () => { let idT = new IdTokenEntity(); Object.assign(idT, mockIdTokenEntity); - expect(idT.generateIdTokenEntityKey()).to.eql( + expect(idT.generateCredentialKey()).to.eql( "uid.utid-login.microsoftonline.com-idtoken-mock_client_id-microsoft-" ); }); diff --git a/lib/msal-common/test/unifiedCache/entities/RefreshTokenEntity.spec.ts b/lib/msal-common/test/unifiedCache/entities/RefreshTokenEntity.spec.ts index f8a1d99a32..1b0539ec85 100644 --- a/lib/msal-common/test/unifiedCache/entities/RefreshTokenEntity.spec.ts +++ b/lib/msal-common/test/unifiedCache/entities/RefreshTokenEntity.spec.ts @@ -11,7 +11,7 @@ describe("RefreshTokenEntity.ts Unit Tests", () => { it("Create a RefreshTokenEntity", () => { let rt = new RefreshTokenEntity(); Object.assign(rt, mockRefreshTokenEntity); - expect(rt.generateRefreshTokenEntityKey()).to.eql( + expect(rt.generateCredentialKey()).to.eql( "uid.utid-login.microsoftonline.com-refreshtoken-mock_client_id--" ); }); @@ -19,7 +19,7 @@ describe("RefreshTokenEntity.ts Unit Tests", () => { it("Create a RefreshTokenEntity with familyId", () => { let rt = new RefreshTokenEntity(); Object.assign(rt, mockRefreshTokenEntityWithFamilyId); - expect(rt.generateRefreshTokenEntityKey()).to.eql( + expect(rt.generateCredentialKey()).to.eql( "uid.utid-login.microsoftonline.com-refreshtoken-1--" ); }); diff --git a/lib/msal-common/test/unifiedCache/entities/cacheConstants.ts b/lib/msal-common/test/unifiedCache/entities/cacheConstants.ts index 2a2348f314..5bf59cbb1a 100644 --- a/lib/msal-common/test/unifiedCache/entities/cacheConstants.ts +++ b/lib/msal-common/test/unifiedCache/entities/cacheConstants.ts @@ -129,15 +129,15 @@ export class mockCache { export const MockCache = { atOne: mockCache.createMockATOne(), - atOneKey: mockCache.createMockATOne().generateAccessTokenEntityKey(), + atOneKey: mockCache.createMockATOne().generateCredentialKey(), atTwo: mockCache.createMockATTwo(), - atTwoKey: mockCache.createMockATTwo().generateAccessTokenEntityKey(), + atTwoKey: mockCache.createMockATTwo().generateCredentialKey(), idT: mockCache.createMockIdT(), - idTKey: mockCache.createMockIdT().generateIdTokenEntityKey(), + idTKey: mockCache.createMockIdT().generateCredentialKey(), rt: mockCache.createMockRT(), - rtKey: mockCache.createMockRT().generateRefreshTokenEntityKey(), + rtKey: mockCache.createMockRT().generateCredentialKey(), rtF: mockCache.createMockRTWithFamilyId(), - rtFKey: mockCache.createMockRTWithFamilyId().generateRefreshTokenEntityKey(), + rtFKey: mockCache.createMockRTWithFamilyId().generateCredentialKey(), acc: mockCache.createMockAcc(), accKey: mockCache.createMockAcc().generateAccountEntityKey(), amdt: mockCache.createMockAmdt(), From 8b86a3d15e527baf69458eda9affeaa924bb1458 Mon Sep 17 00:00:00 2001 From: sameerag Date: Sun, 10 May 2020 22:38:39 -0700 Subject: [PATCH 02/17] update test case desc --- .../test/unifiedCache/entities/AccessTokenEntity.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/msal-common/test/unifiedCache/entities/AccessTokenEntity.spec.ts b/lib/msal-common/test/unifiedCache/entities/AccessTokenEntity.spec.ts index 918899dbfb..95aee7fa8d 100644 --- a/lib/msal-common/test/unifiedCache/entities/AccessTokenEntity.spec.ts +++ b/lib/msal-common/test/unifiedCache/entities/AccessTokenEntity.spec.ts @@ -9,10 +9,12 @@ describe("AccessTokenEntity.ts Unit Tests", () => { expect(at instanceof AccessTokenEntity); }); - it("Create an AccessTokenCacheEntity entity", () => { + it("Generate AccessTokenEntity key", () => { let at = mockCache.createMockATOne(); expect(at.generateCredentialKey()).to.eql( "uid.utid-login.microsoftonline.com-accesstoken-mock_client_id-microsoft-scope1 scope2 scope3" ); }); + + }); From 82e11c8b8edb08b7dba46842ec1c4a41c646e111 Mon Sep 17 00:00:00 2001 From: sameerag Date: Mon, 11 May 2020 00:25:50 -0700 Subject: [PATCH 03/17] Add accountKey changes --- .../src/unifiedCache/UnifiedCacheManager.ts | 2 +- .../unifiedCache/entities/AccountEntity.ts | 26 ++++++++++++++----- .../entities/AccessTokenEntity.spec.ts | 2 -- .../entities/AccountEntity.spec.ts | 4 +-- .../unifiedCache/entities/cacheConstants.ts | 2 +- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts index 3fdc2a135a..dc34524db0 100644 --- a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts +++ b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts @@ -99,7 +99,7 @@ export class UnifiedCacheManager { * @param account */ addAccountEntity(account: AccountEntity): void { - const accKey = account.generateAccountEntityKey(); + const accKey = account.generateAccountKey(); if (!this.inMemoryCache.accounts[accKey]) { this.inMemoryCache.accounts[accKey] = account; } diff --git a/lib/msal-common/src/unifiedCache/entities/AccountEntity.ts b/lib/msal-common/src/unifiedCache/entities/AccountEntity.ts index 0515fb3b76..c2682fdf69 100644 --- a/lib/msal-common/src/unifiedCache/entities/AccountEntity.ts +++ b/lib/msal-common/src/unifiedCache/entities/AccountEntity.ts @@ -25,17 +25,31 @@ export class AccountEntity { lastModificationTime?: string; lastModificationApp?: string; + /** + * Generate Account Id key component as per the schema: - + */ + generateAccountId(): string { + const accountId: Array = [this.homeAccountId, this.environment]; + return accountId.join(Separators.CACHE_KEY_SEPARATOR).toLowerCase(); + } + + /** + * Generate Account Id key component as per the schema: - + */ + generateRealm(): string { + return (this.realm || "").toLowerCase(); + } + /** * Generate Account Cache Key as per the schema: -- */ - public generateAccountEntityKey(): string { - const accountCacheKeyArray: Array = [ - this.homeAccountId, - this.environment, - this.realm + public generateAccountKey(): string { + const accountKey = [ + this.generateAccountId(), + this.generateRealm() ]; - return accountCacheKeyArray.join(Separators.CACHE_KEY_SEPARATOR).toLowerCase(); + return accountKey.join(Separators.CACHE_KEY_SEPARATOR).toLowerCase(); } /** diff --git a/lib/msal-common/test/unifiedCache/entities/AccessTokenEntity.spec.ts b/lib/msal-common/test/unifiedCache/entities/AccessTokenEntity.spec.ts index 95aee7fa8d..f535a1eaa3 100644 --- a/lib/msal-common/test/unifiedCache/entities/AccessTokenEntity.spec.ts +++ b/lib/msal-common/test/unifiedCache/entities/AccessTokenEntity.spec.ts @@ -15,6 +15,4 @@ describe("AccessTokenEntity.ts Unit Tests", () => { "uid.utid-login.microsoftonline.com-accesstoken-mock_client_id-microsoft-scope1 scope2 scope3" ); }); - - }); diff --git a/lib/msal-common/test/unifiedCache/entities/AccountEntity.spec.ts b/lib/msal-common/test/unifiedCache/entities/AccountEntity.spec.ts index 87a5c97a09..8afc566db5 100644 --- a/lib/msal-common/test/unifiedCache/entities/AccountEntity.spec.ts +++ b/lib/msal-common/test/unifiedCache/entities/AccountEntity.spec.ts @@ -18,7 +18,7 @@ describe("AccountEntity.ts Unit Tests", () => { it("generate an AccountEntityKey", () => { let ac = new AccountEntity(); Object.assign(ac, mockAccountEntity); - expect(ac.generateAccountEntityKey()).to.eql( + expect(ac.generateAccountKey()).to.eql( "uid.utid-login.microsoftonline.com-microsoft" ); }); @@ -81,7 +81,7 @@ describe("AccountEntity.ts Unit Tests", () => { cryptoInterface ); - expect(acc.generateAccountEntityKey()).to.eql( + expect(acc.generateAccountKey()).to.eql( "uid.utid-login.microsoftonline.com-microsoft" ); }); diff --git a/lib/msal-common/test/unifiedCache/entities/cacheConstants.ts b/lib/msal-common/test/unifiedCache/entities/cacheConstants.ts index 5bf59cbb1a..741880cb00 100644 --- a/lib/msal-common/test/unifiedCache/entities/cacheConstants.ts +++ b/lib/msal-common/test/unifiedCache/entities/cacheConstants.ts @@ -139,7 +139,7 @@ export const MockCache = { rtF: mockCache.createMockRTWithFamilyId(), rtFKey: mockCache.createMockRTWithFamilyId().generateCredentialKey(), acc: mockCache.createMockAcc(), - accKey: mockCache.createMockAcc().generateAccountEntityKey(), + accKey: mockCache.createMockAcc().generateAccountKey(), amdt: mockCache.createMockAmdt(), amdtKey: mockCache.createMockAmdt().generateAppMetaDataEntityKey() } From 5863af0f8156e2a0103185980b2916a04d5165b8 Mon Sep 17 00:00:00 2001 From: sameerag Date: Thu, 14 May 2020 01:55:20 -0700 Subject: [PATCH 04/17] Cache lookup helpers --- .../interface/IAccountCredentialCache.ts | 76 +++++ .../lookup/AccountCredentialCache.ts | 301 ++++++++++++++++++ .../src/unifiedCache/lookup/CacheRecord.ts | 16 + .../src/unifiedCache/utils/CacheHelper.ts | 74 +++++ .../src/unifiedCache/utils/CacheTypes.ts | 6 + lib/msal-common/src/utils/Constants.ts | 15 +- .../lookup/AccountCredentialCache.spec.ts | 185 +++++++++++ 7 files changed, 672 insertions(+), 1 deletion(-) create mode 100644 lib/msal-common/src/unifiedCache/interface/IAccountCredentialCache.ts create mode 100644 lib/msal-common/src/unifiedCache/lookup/AccountCredentialCache.ts create mode 100644 lib/msal-common/src/unifiedCache/lookup/CacheRecord.ts create mode 100644 lib/msal-common/test/unifiedCache/lookup/AccountCredentialCache.spec.ts diff --git a/lib/msal-common/src/unifiedCache/interface/IAccountCredentialCache.ts b/lib/msal-common/src/unifiedCache/interface/IAccountCredentialCache.ts new file mode 100644 index 0000000000..951865f9d0 --- /dev/null +++ b/lib/msal-common/src/unifiedCache/interface/IAccountCredentialCache.ts @@ -0,0 +1,76 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +import { AccountEntity } from "../entities/AccountEntity"; +import { Credential } from "../entities/Credential"; +import { AccountCache, CredentialCache } from "../utils/CacheTypes"; + +export interface IAccountCredentialCache { + /** + * saves account into cache + * @param account + */ + saveAccount(account: AccountEntity): void; + + /** + * saves credential - accessToken, idToken or refreshToken into cache + * @param credential + */ + saveCredential(credential: Credential): void; + + /** + * Given account key retrieve an account + * @param key + */ + getAccount(key: string): AccountEntity; + + /** + * retrieve a credential - accessToken, idToken or refreshToken; given the cache key + * @param key + */ + getCredential(key: string): Credential; + + /** + * retrieve accounts matching all provided filters; if no filter is set, get all accounts + * @param homeAccountId + * @param environment + * @param realm + */ + getAccounts( + homeAccountId?: string, + environment?: string, + realm?: string + ): AccountCache; + + /** + * retrieve credentails matching all provided filters; if no filter is set, get all credentials + * @param homeAccountId + * @param environment + * @param credentialType + * @param clientId + * @param realm + * @param target + */ + getCredentials( + homeAccountId?: string, + environment?: string, + credentialType?: string, + clientId?: string, + realm?: string, + target?: string + ): CredentialCache; + + /** + * returns a boolean if the given account is removed + * @param account + */ + removeAccount(account: AccountEntity): boolean; + + /** + * returns a boolean if the given credential is removed + * @param credential + */ + removeCredential(credential: Credential): boolean; +} diff --git a/lib/msal-common/src/unifiedCache/lookup/AccountCredentialCache.ts b/lib/msal-common/src/unifiedCache/lookup/AccountCredentialCache.ts new file mode 100644 index 0000000000..b482ce79c3 --- /dev/null +++ b/lib/msal-common/src/unifiedCache/lookup/AccountCredentialCache.ts @@ -0,0 +1,301 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +import { AccountEntity } from "../entities/AccountEntity"; +import { IdTokenEntity } from "../entities/IdTokenEntity"; +import { AccessTokenEntity } from "../entities/AccessTokenEntity"; +import { RefreshTokenEntity } from "../entities/RefreshTokenEntity"; +import { Credential } from "../entities/Credential"; +import { UnifiedCacheManager } from "../UnifiedCacheManager"; +import { CredentialType, Separators, CacheKeyPosition } from "../../utils/Constants"; +import { AccountCache, CredentialCache, IdTokenCache, AccessTokenCache, RefreshTokenCache } from "../utils/CacheTypes"; +import { IAccountCredentialCache } from "../interface/IAccountCredentialCache"; +import { CacheHelper } from "../utils/CacheHelper"; + +export class AccountCredentialCache implements IAccountCredentialCache { + private cacheManager: UnifiedCacheManager; + + constructor(cacheMgr: UnifiedCacheManager) { + this.cacheManager = cacheMgr; + } + + /** + * saves account into cache + * @param account + */ + saveAccount(account: AccountEntity): void { + const cache = this.cacheManager.getCacheInMemory(); + const key = account.generateAccountKey(); + cache.accounts[key] = account; + this.cacheManager.setCacheInMemory(cache); + } + + /** + * saves credential - accessToken, idToken or refreshToken into cache + * @param credential + */ + saveCredential(credential: Credential): void { + const cache = this.cacheManager.getCacheInMemory(); + const key = credential.generateCredentialKey(); + + switch (credential.credentialType) { + case CredentialType.ID_TOKEN: + cache.idTokens[key] = credential as IdTokenEntity; + break; + case CredentialType.ACCESS_TOKEN: + cache.accessTokens[key] = credential as AccessTokenEntity; + break; + case CredentialType.REFRESH_TOKEN: + cache.refreshTokens[key] = credential as RefreshTokenEntity; + break; + default: + console.log("Cache entity type mismatch"); + } + + this.cacheManager.setCacheInMemory(cache); + } + + /** + * Given account key retrieve an account + * @param key + */ + getAccount(key: string): AccountEntity { + return this.cacheManager.getCacheInMemory().accounts[key] || null; + } + + /** + * retrieve a credential - accessToken, idToken or refreshToken; given the cache key + * @param key + */ + getCredential(key: string): Credential { + const cache = this.cacheManager.getCacheInMemory(); + switch (key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.CREDENTIAL_TYPE]) { + case "idtoken": + return cache.idTokens[key] || null; + case "accesstoken": + return cache.accessTokens[key] || null; + case "refreshtoken": + return cache.refreshTokens[key] || null; + default: + console.log("Cache entity type mismatch"); + return null; + } + } + + /** + * retrieve accounts matching all provided filters; if no filter is set, get all accounts + * @param homeAccountId + * @param environment + * @param realm + */ + getAccounts( + homeAccountId?: string, + environment?: string, + realm?: string + ): AccountCache { + const accounts: AccountCache = this.cacheManager.getCacheInMemory().accounts; + const matchingAccounts: AccountCache = {}; + + let matches: boolean = true; + + Object.keys(accounts).forEach((key) => { + + if (!!homeAccountId) { + matches = CacheHelper.matchHomeAccountId(key, homeAccountId); + } + + if (!!environment) { + matches = matches && CacheHelper.matchEnvironment(key, environment); + } + + if (!!realm) { + matches = matches && CacheHelper.matchTarget(key, realm); + } + + if (matches) { + matchingAccounts[key] = accounts[key]; + } + }); + + return matchingAccounts; + } + + /** + * retrieve credentails matching all provided filters; if no filter is set, get all credentials + * @param homeAccountId + * @param environment + * @param credentialType + * @param clientId + * @param realm + * @param target + */ + getCredentials( + homeAccountId?: string, + environment?: string, + credentialType?: string, + clientId?: string, + realm?: string, + target?: string + ): CredentialCache { + + const matchingCredentials: CredentialCache = { + idTokens: {}, + accessTokens: {}, + refreshTokens: {} + }; + + matchingCredentials.idTokens = this.getCredentialsInternal( + this.cacheManager.getCacheInMemory().idTokens, + homeAccountId, + environment, + credentialType, + clientId, + realm, + target + ) as IdTokenCache; + + matchingCredentials.accessTokens = this.getCredentialsInternal( + this.cacheManager.getCacheInMemory().accessTokens, + homeAccountId, + environment, + credentialType, + clientId, + realm, + target + ) as AccessTokenCache; + + matchingCredentials.refreshTokens = this.getCredentialsInternal( + this.cacheManager.getCacheInMemory().refreshTokens, + homeAccountId, + environment, + credentialType, + clientId, + realm, + target + ) as RefreshTokenCache; + + return matchingCredentials; + } + + /** + * Support function to help match credentials + * @param cacheCredentials + * @param homeAccountId + * @param environment + * @param credentialType + * @param clientId + * @param realm + * @param target + */ + private getCredentialsInternal( + cacheCredentials: object, + homeAccountId?: string, + environment?: string, + credentialType?: string, + clientId?: string, + realm?: string, + target?: string, + ): Object { + + const matchingCredentials = {}; + let matches: boolean; + + Object.keys(cacheCredentials).forEach((key) => { + if (!!homeAccountId) { + matches = CacheHelper.matchHomeAccountId(key, homeAccountId); + } + + if (!!environment) { + matches = matches && CacheHelper.matchEnvironment(key, environment); + } + + if (!!realm) { + matches = matches && CacheHelper.matchRealm(key, realm); + } + + if (!!credentialType) { + matches = matches && CacheHelper.matchCredentialType(key, credentialType); + } + + if (!!clientId) { + matches = matches && CacheHelper.matchClientId(key, clientId); + } + + if (!!target) { + matches = matches && CacheHelper.matchTarget(key, target); + } + + if (matches) { + matchingCredentials[key] = cacheCredentials[key]; + } + }); + + return matchingCredentials; + } + + /** + * returns a boolean if the given account is removed + * @param account + */ + removeAccount(account: AccountEntity): boolean { + const cache = this.cacheManager.getCacheInMemory(); + const accountKey = account.generateAccountKey(); + + delete cache.accounts[accountKey]; + return true; + } + + /** + * returns a boolean if the given account is removed + * @param account + */ + removeAccountContext(account: AccountEntity): boolean { + const cache = this.cacheManager.getCacheInMemory(); + + const accountId = account.generateAccountId(); + + // TODO: Check how this should be done, do we just remove the account or also the associated credentials always? + Object.keys(cache.accessTokens).forEach((key) => { + if (cache.accessTokens[key].generateAccountId() === accountId) + this.removeCredential(cache.accessTokens[key]); + }); + + Object.keys(cache.idTokens).forEach((key) => { + if (cache.idTokens[key].generateAccountId() === accountId) + this.removeCredential(cache.idTokens[key]); + }); + + Object.keys(cache.idTokens).forEach((key) => { + if (cache.idTokens[key].generateAccountId() === accountId) + this.removeCredential(cache.idTokens[key]); + }); + + this.removeAccount(account); + return true; + } + + /** + * returns a boolean if the given credential is removed + * @param credential + */ + removeCredential(credential: Credential): boolean { + const cache = this.cacheManager.getCacheInMemory(); + + switch (credential.credentialType) { + case CredentialType.ID_TOKEN: + delete cache.idTokens[credential.generateCredentialKey()]; + return true; + case CredentialType.ACCESS_TOKEN: + delete cache.accessTokens[credential.generateCredentialKey()]; + return true; + case CredentialType.REFRESH_TOKEN: + delete cache.refreshTokens[credential.generateCredentialKey()]; + return true; + default: + console.log("Cache entity type mismatch"); + return false; + } + } +} diff --git a/lib/msal-common/src/unifiedCache/lookup/CacheRecord.ts b/lib/msal-common/src/unifiedCache/lookup/CacheRecord.ts new file mode 100644 index 0000000000..ad5c5f4ff4 --- /dev/null +++ b/lib/msal-common/src/unifiedCache/lookup/CacheRecord.ts @@ -0,0 +1,16 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +import { AccountEntity } from "../entities/AccountEntity"; +import { IdTokenEntity } from "../entities/IdTokenEntity"; +import { AccessTokenEntity } from "../entities/AccessTokenEntity"; +import { RefreshTokenEntity } from "../entities/RefreshTokenEntity"; + +export type CacheRecord = { + account: AccountEntity, + idToken: IdTokenEntity, + accessToken: AccessTokenEntity, + refreshToken: RefreshTokenEntity +}; diff --git a/lib/msal-common/src/unifiedCache/utils/CacheHelper.ts b/lib/msal-common/src/unifiedCache/utils/CacheHelper.ts index 2cd6f06764..47671d746d 100644 --- a/lib/msal-common/src/unifiedCache/utils/CacheHelper.ts +++ b/lib/msal-common/src/unifiedCache/utils/CacheHelper.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. */ +import { Separators, CacheKeyPosition } from "../../utils/Constants"; + export class CacheHelper { /** * Helper to convert serialized data to object @@ -43,4 +45,76 @@ export class CacheHelper { }); return Object.assign({}, ...keyValues); } + + /** + * + * @param key + * @param homeAccountId + */ + static matchHomeAccountId(key: string, homeAccountId: string): boolean { + return homeAccountId === key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.HOME_ACCOUNT_ID]; + } + + /** + * + * @param key + * @param environment + */ + static matchEnvironment(key: string, environment: string): boolean { + return environment === key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.ENVIRONMENT]; + } + + /** + * + * @param key + * @param credentialType + * // TODO: Confirm equality for enum vs string here + */ + static matchCredentialType(key: string, credentialType: string): boolean { + return credentialType == key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.CREDENTIAL_TYPE]; + } + + /** + * + * @param key + * @param clientId + */ + static matchClientId(key: string, clientId: string): boolean { + return clientId === key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.CLIENT_ID]; + } + + /** + * + * @param key + * @param realm + */ + static matchRealm(key: string, realm: string): boolean { + return realm === key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.REALM]; + } + + /** + * + * @param key + * @param target + */ + static matchTarget(key: string, target: string): boolean { + return this.targetsIntersect(key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.TARGET], target); + } + + /** + * returns a boolean if the sets of scopes intersect (scopes are stored as "target" in cache) + * @param target + * @param credentialTarget + */ + static targetsIntersect(credentialTarget: string, target: string): boolean { + const targetSet = new Set(target.split(" ")); + const credentialTargetSet = new Set(credentialTarget.split(" ")); + + let isSubset = true; + targetSet.forEach((key) => { + isSubset = isSubset && credentialTargetSet.has(key); + }); + + return isSubset; + } } diff --git a/lib/msal-common/src/unifiedCache/utils/CacheTypes.ts b/lib/msal-common/src/unifiedCache/utils/CacheTypes.ts index ad6ee315fe..78abb03495 100644 --- a/lib/msal-common/src/unifiedCache/utils/CacheTypes.ts +++ b/lib/msal-common/src/unifiedCache/utils/CacheTypes.ts @@ -32,3 +32,9 @@ export type InMemoryCache = { refreshTokens: RefreshTokenCache; appMetadata: AppMetadataCache; }; + +export type CredentialCache = { + idTokens: IdTokenCache; + accessTokens: AccessTokenCache; + refreshTokens: RefreshTokenCache; +}; diff --git a/lib/msal-common/src/utils/Constants.ts b/lib/msal-common/src/utils/Constants.ts index e520e16571..1915c1b279 100644 --- a/lib/msal-common/src/utils/Constants.ts +++ b/lib/msal-common/src/utils/Constants.ts @@ -259,7 +259,7 @@ export enum CacheEntity { /** * Combine all cache types */ -export enum CacheTypes { +export enum CacheType { ACCESS_TOKEN, ID_TOKEN, REFRESH_TOKEN, @@ -267,6 +267,19 @@ export enum CacheTypes { APP_META_DATA }; +/** + * accountId: - + * credentialId: -- + */ +export enum CacheKeyPosition { + HOME_ACCOUNT_ID = 0, + ENVIRONMENT, + CREDENTIAL_TYPE, + CLIENT_ID, + REALM, + TARGET +}; + /** * More Cache related constants */ diff --git a/lib/msal-common/test/unifiedCache/lookup/AccountCredentialCache.spec.ts b/lib/msal-common/test/unifiedCache/lookup/AccountCredentialCache.spec.ts new file mode 100644 index 0000000000..60e70310a2 --- /dev/null +++ b/lib/msal-common/test/unifiedCache/lookup/AccountCredentialCache.spec.ts @@ -0,0 +1,185 @@ +import { expect } from "chai"; +import { InMemoryCache } from "../../../src/unifiedCache/utils/CacheTypes"; +import { ICacheStorage } from ".../../../src/cache/ICacheStorage"; +import { Deserializer } from "../../../src/unifiedCache/serialize/Deserializer"; +import { AccountEntity } from "../../../src/unifiedCache/entities/AccountEntity"; +import { AccessTokenEntity } from "../../../src/unifiedCache/entities/AccessTokenEntity"; +import { AccountCredentialCache } from "../../../src/unifiedCache/lookup/AccountCredentialCache"; +import { UnifiedCacheManager } from "../../../src/unifiedCache/UnifiedCacheManager"; + +const cacheJson = require("./../serialize/cache.json"); + +describe("AccountCredentialCache test cases", () => { + let store = {}; + let storageInterface: ICacheStorage; + const cache = JSON.stringify(cacheJson); + const inMemCache: InMemoryCache = Deserializer.deserializeAllCache(Deserializer.deserializeJSONBlob(cache)); + + beforeEach(() => { + storageInterface = { + getCache(): InMemoryCache { + return inMemCache; + }, + setCache(inMemCache): void { + this.inMemCache = inMemCache; + }, + setItem(key: string, value: string): void { + store[key] = value; + }, + getItem(key: string): string { + return store[key]; + }, + removeItem(key: string): void { + delete store[key]; + }, + containsKey(key: string): boolean { + return !!store[key]; + }, + getKeys(): string[] { + return Object.keys(store); + }, + clear(): void { + store = {}; + }, + } + }); + + it("save account", () => { + let ac = new AccountEntity(); + Object.assign( + ac, + { + homeAccountId: "someUid.someUtid", + environment: "login.microsoftonline.com", + realm: "microsoft", + localAccountId: "object1234", + username: "Jane Goodman", + authorityType: "MSSTS", + clientInfo: "eyJ1aWQiOiJzb21lVWlkIiwgInV0aWQiOiJzb21lVXRpZCJ9", + } + ); + + let unifiedCacheManager = new UnifiedCacheManager(storageInterface); + let accCredCache = new AccountCredentialCache(unifiedCacheManager); + + const accountKey = ac.generateAccountKey(); + accCredCache.saveAccount(ac); + expect(unifiedCacheManager.getCacheInMemory().accounts[accountKey].homeAccountId).to.eql("someUid.someUtid"); + }); + + it("save credential", () => { + let at = new AccessTokenEntity(); + Object.assign( + at, + { + homeAccountId: "someUid.someUtid", + environment: "login.microsoftonline.com", + credentialType: "AccessToken", + clientId: "mock_client_id", + secret: "an access token sample", + realm: "microsoft", + target: "scope6 scope7", + cachedAt: "1000", + expiresOn: "4600", + extendedExpiresOn: "4600", + } + ); + + let unifiedCacheManager = new UnifiedCacheManager(storageInterface); + let accCredCache = new AccountCredentialCache(unifiedCacheManager); + + const atKey = at.generateCredentialKey(); + accCredCache.saveCredential(at); + expect(unifiedCacheManager.getCacheInMemory().accessTokens[atKey].homeAccountId).to.eql("someUid.someUtid"); + }); + + it("getAccount", () => { + let unifiedCacheManager = new UnifiedCacheManager(storageInterface); + let accCredCache = new AccountCredentialCache(unifiedCacheManager); + + expect(accCredCache.getAccount("someuid.someutid-login.microsoftonline.com-microsoft").homeAccountId).to.eql("someUid.someUtid"); + }); + + it("getCredential", () => { + let unifiedCacheManager = new UnifiedCacheManager(storageInterface); + let accCredCache = new AccountCredentialCache(unifiedCacheManager); + + expect( + accCredCache.getCredential( + "someuid.someutid-login.microsoftonline.com-accesstoken-mock_client_id-microsoft-scope6 scope7" + ).homeAccountId + ).to.eql("someUid.someUtid"); + }); + + it("getAccounts", () => { + let unifiedCacheManager = new UnifiedCacheManager(storageInterface); + let accCredCache = new AccountCredentialCache(unifiedCacheManager); + + let accounts = accCredCache.getAccounts("uid.utid"); + expect(Object.keys(accounts).length).to.eql(1); + + accounts = accCredCache.getAccounts(null, "login.microsoftonline.com"); + expect(Object.keys(accounts).length).to.eql(2); + }); + + it("getCredentials", () => { + let unifiedCacheManager = new UnifiedCacheManager(storageInterface); + let accCredCache = new AccountCredentialCache(unifiedCacheManager); + + let credentials = accCredCache.getCredentials("uid.utid"); + expect(Object.keys(credentials.idTokens).length).to.eql(1); + expect(Object.keys(credentials.accessTokens).length).to.eql(2); + expect(Object.keys(credentials.refreshTokens).length).to.eql(2); + + credentials = accCredCache.getCredentials("someuid.someutid"); + expect(Object.keys(credentials.idTokens).length).to.eql(0); + expect(Object.keys(credentials.accessTokens).length).to.eql(1); + expect(Object.keys(credentials.refreshTokens).length).to.eql(0); + }); + + it("removeAccount", () => { + let unifiedCacheManager = new UnifiedCacheManager(storageInterface); + let accCredCache = new AccountCredentialCache(unifiedCacheManager); + + let ac = new AccountEntity(); + Object.assign( + ac, + { + homeAccountId: "someUid.someUtid", + environment: "login.microsoftonline.com", + realm: "microsoft", + localAccountId: "object1234", + username: "Jane Goodman", + authorityType: "MSSTS", + clientInfo: "eyJ1aWQiOiJzb21lVWlkIiwgInV0aWQiOiJzb21lVXRpZCJ9", + } + ); + + accCredCache.removeAccount(ac); + const accountKey = ac.generateAccountKey(); + expect(unifiedCacheManager.getCacheInMemory().accounts[accountKey]).to.eql(undefined); + }); + + it("removeCredential", () => { + let unifiedCacheManager = new UnifiedCacheManager(storageInterface); + let accCredCache = new AccountCredentialCache(unifiedCacheManager); + + let at = new AccessTokenEntity(); + Object.assign(at, { + homeAccountId: "someUid.someUtid", + environment: "login.microsoftonline.com", + credentialType: "AccessToken", + clientId: "mock_client_id", + secret: "an access token sample", + realm: "microsoft", + target: "scope6 scope7", + cachedAt: "1000", + expiresOn: "4600", + extendedExpiresOn: "4600", + }); + + accCredCache.removeCredential(at); + const atKey = at.generateCredentialKey(); + expect(unifiedCacheManager.getCacheInMemory().accessTokens[atKey]).to.eql(undefined); + }); +}); From c3d30ddb520e00a4a299b6aa75dc717fdb66c780 Mon Sep 17 00:00:00 2001 From: sameerag Date: Thu, 14 May 2020 02:03:00 -0700 Subject: [PATCH 05/17] Name Change for some APIs --- .../interface/IAccountCredentialCache.ts | 4 ++-- .../unifiedCache/lookup/AccountCredentialCache.ts | 12 ++++++------ .../lookup/AccountCredentialCache.spec.ts | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/msal-common/src/unifiedCache/interface/IAccountCredentialCache.ts b/lib/msal-common/src/unifiedCache/interface/IAccountCredentialCache.ts index 951865f9d0..65a2351566 100644 --- a/lib/msal-common/src/unifiedCache/interface/IAccountCredentialCache.ts +++ b/lib/msal-common/src/unifiedCache/interface/IAccountCredentialCache.ts @@ -38,7 +38,7 @@ export interface IAccountCredentialCache { * @param environment * @param realm */ - getAccounts( + getAccountsFilteredBy( homeAccountId?: string, environment?: string, realm?: string @@ -53,7 +53,7 @@ export interface IAccountCredentialCache { * @param realm * @param target */ - getCredentials( + getCredentialsFilteredBy( homeAccountId?: string, environment?: string, credentialType?: string, diff --git a/lib/msal-common/src/unifiedCache/lookup/AccountCredentialCache.ts b/lib/msal-common/src/unifiedCache/lookup/AccountCredentialCache.ts index b482ce79c3..3f99e1560c 100644 --- a/lib/msal-common/src/unifiedCache/lookup/AccountCredentialCache.ts +++ b/lib/msal-common/src/unifiedCache/lookup/AccountCredentialCache.ts @@ -90,7 +90,7 @@ export class AccountCredentialCache implements IAccountCredentialCache { * @param environment * @param realm */ - getAccounts( + getAccountsFilteredBy( homeAccountId?: string, environment?: string, realm?: string @@ -131,7 +131,7 @@ export class AccountCredentialCache implements IAccountCredentialCache { * @param realm * @param target */ - getCredentials( + getCredentialsFilteredBy( homeAccountId?: string, environment?: string, credentialType?: string, @@ -146,7 +146,7 @@ export class AccountCredentialCache implements IAccountCredentialCache { refreshTokens: {} }; - matchingCredentials.idTokens = this.getCredentialsInternal( + matchingCredentials.idTokens = this.getCredentialsFilteredByInternal( this.cacheManager.getCacheInMemory().idTokens, homeAccountId, environment, @@ -156,7 +156,7 @@ export class AccountCredentialCache implements IAccountCredentialCache { target ) as IdTokenCache; - matchingCredentials.accessTokens = this.getCredentialsInternal( + matchingCredentials.accessTokens = this.getCredentialsFilteredByInternal( this.cacheManager.getCacheInMemory().accessTokens, homeAccountId, environment, @@ -166,7 +166,7 @@ export class AccountCredentialCache implements IAccountCredentialCache { target ) as AccessTokenCache; - matchingCredentials.refreshTokens = this.getCredentialsInternal( + matchingCredentials.refreshTokens = this.getCredentialsFilteredByInternal( this.cacheManager.getCacheInMemory().refreshTokens, homeAccountId, environment, @@ -189,7 +189,7 @@ export class AccountCredentialCache implements IAccountCredentialCache { * @param realm * @param target */ - private getCredentialsInternal( + private getCredentialsFilteredByInternal( cacheCredentials: object, homeAccountId?: string, environment?: string, diff --git a/lib/msal-common/test/unifiedCache/lookup/AccountCredentialCache.spec.ts b/lib/msal-common/test/unifiedCache/lookup/AccountCredentialCache.spec.ts index 60e70310a2..1fa99b0615 100644 --- a/lib/msal-common/test/unifiedCache/lookup/AccountCredentialCache.spec.ts +++ b/lib/msal-common/test/unifiedCache/lookup/AccountCredentialCache.spec.ts @@ -115,10 +115,10 @@ describe("AccountCredentialCache test cases", () => { let unifiedCacheManager = new UnifiedCacheManager(storageInterface); let accCredCache = new AccountCredentialCache(unifiedCacheManager); - let accounts = accCredCache.getAccounts("uid.utid"); + let accounts = accCredCache.getAccountsFilteredBy("uid.utid"); expect(Object.keys(accounts).length).to.eql(1); - accounts = accCredCache.getAccounts(null, "login.microsoftonline.com"); + accounts = accCredCache.getAccountsFilteredBy(null, "login.microsoftonline.com"); expect(Object.keys(accounts).length).to.eql(2); }); @@ -126,12 +126,12 @@ describe("AccountCredentialCache test cases", () => { let unifiedCacheManager = new UnifiedCacheManager(storageInterface); let accCredCache = new AccountCredentialCache(unifiedCacheManager); - let credentials = accCredCache.getCredentials("uid.utid"); + let credentials = accCredCache.getCredentialsFilteredBy("uid.utid"); expect(Object.keys(credentials.idTokens).length).to.eql(1); expect(Object.keys(credentials.accessTokens).length).to.eql(2); expect(Object.keys(credentials.refreshTokens).length).to.eql(2); - credentials = accCredCache.getCredentials("someuid.someutid"); + credentials = accCredCache.getCredentialsFilteredBy("someuid.someutid"); expect(Object.keys(credentials.idTokens).length).to.eql(0); expect(Object.keys(credentials.accessTokens).length).to.eql(1); expect(Object.keys(credentials.refreshTokens).length).to.eql(0); From 6f9565b2ed56a40e4653a81ed86b509b75aab16e Mon Sep 17 00:00:00 2001 From: sameerag Date: Thu, 14 May 2020 02:46:46 -0700 Subject: [PATCH 06/17] Add helper functions in UnifiedCacheManager --- .../src/response/ResponseHandler.ts | 9 +- .../src/unifiedCache/UnifiedCacheManager.ts | 372 ++++++++++++++++-- ...untCredentialCache.ts => ICacheManager.ts} | 2 +- .../lookup/AccountCredentialCache.ts | 301 -------------- .../src/unifiedCache/lookup/CacheRecord.ts | 16 - .../unifiedCache/UnifiedCacheManager.spec.ts | 168 +++++++- .../lookup/AccountCredentialCache.spec.ts | 185 --------- 7 files changed, 498 insertions(+), 555 deletions(-) rename lib/msal-common/src/unifiedCache/interface/{IAccountCredentialCache.ts => ICacheManager.ts} (97%) delete mode 100644 lib/msal-common/src/unifiedCache/lookup/AccountCredentialCache.ts delete mode 100644 lib/msal-common/src/unifiedCache/lookup/CacheRecord.ts delete mode 100644 lib/msal-common/test/unifiedCache/lookup/AccountCredentialCache.spec.ts diff --git a/lib/msal-common/src/response/ResponseHandler.ts b/lib/msal-common/src/response/ResponseHandler.ts index f3ee4552f4..c184fc979d 100644 --- a/lib/msal-common/src/response/ResponseHandler.ts +++ b/lib/msal-common/src/response/ResponseHandler.ts @@ -174,9 +174,14 @@ export class ResponseHandler { addAccountToCache(serverTokenResponse: ServerAuthorizationTokenResponse, idToken: IdToken, authority: Authority): void { const environment = authority.canonicalAuthorityUrlComponents.HostNameAndPort; let accountEntity: AccountEntity; - const cachedAccount: AccountEntity = this.uCacheManager.getAccount(this.homeAccountIdentifier, environment, idToken.claims.tid); + accountEntity = this.generateAccountEntity( + serverTokenResponse, + idToken, + authority + ); + + const cachedAccount: AccountEntity = this.uCacheManager.getAccount(accountEntity.generateAccountKey()); if (!cachedAccount) { - accountEntity = this.generateAccountEntity(serverTokenResponse, idToken, authority); this.uCacheManager.addAccountEntity(accountEntity); } } diff --git a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts index dc34524db0..26fbb4b28d 100644 --- a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts +++ b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts @@ -4,7 +4,6 @@ */ import { InMemoryCache, JsonCache } from "./utils/CacheTypes"; -import { Separators } from "../utils/Constants"; import { AccessTokenEntity } from "./entities/AccessTokenEntity"; import { IdTokenEntity } from "./entities/IdTokenEntity"; import { RefreshTokenEntity } from "./entities/RefreshTokenEntity"; @@ -12,10 +11,23 @@ import { AccountEntity } from "./entities/AccountEntity"; import { ICacheStorage } from "../cache/ICacheStorage"; import { Deserializer } from "./serialize/Deserializer"; import { Serializer } from "./serialize/Serializer"; -import { AccountCache } from "./utils/CacheTypes"; - -export class UnifiedCacheManager { +import { Credential } from "./entities/Credential"; +import { + CredentialType, + Separators, + CacheKeyPosition, +} from "../utils/Constants"; +import { + AccountCache, + CredentialCache, + IdTokenCache, + AccessTokenCache, + RefreshTokenCache, +} from "./utils/CacheTypes"; +import { ICacheManager } from "./interface/ICacheManager"; +import { CacheHelper } from "./utils/CacheHelper"; +export class UnifiedCacheManager implements ICacheManager { // Storage interface private inMemoryCache: InMemoryCache; private cacheStorage: ICacheStorage; @@ -26,14 +38,15 @@ export class UnifiedCacheManager { } /** - * setter for in cache memory + * sets the inMemory cache + * @param cache */ setCacheInMemory(cache: InMemoryCache): void { this.inMemoryCache = cache; } /** - * get the cache in memory + * get the inMemory Cache */ getCacheInMemory(): InMemoryCache { return this.inMemoryCache; @@ -41,18 +54,55 @@ export class UnifiedCacheManager { /** * Initialize in memory cache from an exisiting cache vault + * @param cache */ generateInMemoryCache(cache: string): InMemoryCache { - return Deserializer.deserializeAllCache(Deserializer.deserializeJSONBlob(cache)); + return Deserializer.deserializeAllCache( + Deserializer.deserializeJSONBlob(cache) + ); } /** * retrieves the final JSON + * @param inMemoryCache */ generateJsonCache(inMemoryCache: InMemoryCache): JsonCache { return Serializer.serializeAllCache(inMemoryCache); } + /** + * append credential cache to in memory cach + * @param accessToken + * @param idToken + * @param refreshToken + */ + addCredentialCache( + accessToken: AccessTokenEntity, + idToken: IdTokenEntity, + refreshToken: RefreshTokenEntity + ): void { + this.inMemoryCache.accessTokens[ + accessToken.generateCredentialKey() + ] = accessToken; + this.inMemoryCache.idTokens[ + idToken.generateCredentialKey() + ] = idToken; + this.inMemoryCache.refreshTokens[ + refreshToken.generateCredentialKey() + ] = refreshToken; + } + + /** + * append account to the in memory cache + * @param account + */ + addAccountEntity(account: AccountEntity): void { + const accKey = account.generateAccountKey(); + if (!this.inMemoryCache.accounts[accKey]) { + this.inMemoryCache.accounts[accKey] = account; + } + } + /** * Returns all accounts in memory */ @@ -61,47 +111,307 @@ export class UnifiedCacheManager { } /** - * Returns if the account is in Cache + * saves account into cache + * @param account + */ + saveAccount(account: AccountEntity): void { + const cache = this.getCacheInMemory(); + const key = account.generateAccountKey(); + cache.accounts[key] = account; + this.setCacheInMemory(cache); + } + + /** + * saves credential - accessToken, idToken or refreshToken into cache + * @param credential + */ + saveCredential(credential: Credential): void { + const cache = this.getCacheInMemory(); + const key = credential.generateCredentialKey(); + + switch (credential.credentialType) { + case CredentialType.ID_TOKEN: + cache.idTokens[key] = credential as IdTokenEntity; + break; + case CredentialType.ACCESS_TOKEN: + cache.accessTokens[ + key + ] = credential as AccessTokenEntity; + break; + case CredentialType.REFRESH_TOKEN: + cache.refreshTokens[ + key + ] = credential as RefreshTokenEntity; + break; + default: + console.log("Cache entity type mismatch"); + } + + this.setCacheInMemory(cache); + } + + /** + * Given account key retrieve an account + * @param key + */ + getAccount(key: string): AccountEntity { + return this.getCacheInMemory().accounts[key] || null; + } + + /** + * retrieve a credential - accessToken, idToken or refreshToken; given the cache key + * @param key + */ + getCredential(key: string): Credential { + const cache = this.getCacheInMemory(); + switch ( + key.split(Separators.CACHE_KEY_SEPARATOR)[ + CacheKeyPosition.CREDENTIAL_TYPE + ] + ) { + case "idtoken": + return cache.idTokens[key] || null; + case "accesstoken": + return cache.accessTokens[key] || null; + case "refreshtoken": + return cache.refreshTokens[key] || null; + default: + console.log("Cache entity type mismatch"); + return null; + } + } + + /** + * retrieve accounts matching all provided filters; if no filter is set, get all accounts + * @param homeAccountId + * @param environment + * @param realm + */ + getAccountsFilteredBy( + homeAccountId?: string, + environment?: string, + realm?: string + ): AccountCache { + const accounts: AccountCache = this.getCacheInMemory().accounts; + const matchingAccounts: AccountCache = {}; + + let matches: boolean = true; + + Object.keys(accounts).forEach((key) => { + if (!!homeAccountId) { + matches = CacheHelper.matchHomeAccountId( + key, + homeAccountId + ); + } + + if (!!environment) { + matches = + matches && + CacheHelper.matchEnvironment(key, environment); + } + + if (!!realm) { + matches = matches && CacheHelper.matchTarget(key, realm); + } + + if (matches) { + matchingAccounts[key] = accounts[key]; + } + }); + + return matchingAccounts; + } + + /** + * retrieve credentails matching all provided filters; if no filter is set, get all credentials * @param homeAccountId * @param environment + * @param credentialType + * @param clientId * @param realm + * @param target */ - getAccount(homeAccountId: string, environment: string, realm: string): AccountEntity { - const accountCacheKey: Array = [ + getCredentialsFilteredBy( + homeAccountId?: string, + environment?: string, + credentialType?: string, + clientId?: string, + realm?: string, + target?: string + ): CredentialCache { + const matchingCredentials: CredentialCache = { + idTokens: {}, + accessTokens: {}, + refreshTokens: {}, + }; + + matchingCredentials.idTokens = this.getCredentialsFilteredByInternal( + this.getCacheInMemory().idTokens, homeAccountId, environment, - realm - ]; + credentialType, + clientId, + realm, + target + ) as IdTokenCache; - const accountKey = accountCacheKey.join(Separators.CACHE_KEY_SEPARATOR).toLowerCase(); + matchingCredentials.accessTokens = this.getCredentialsFilteredByInternal( + this.getCacheInMemory().accessTokens, + homeAccountId, + environment, + credentialType, + clientId, + realm, + target + ) as AccessTokenCache; + + matchingCredentials.refreshTokens = this.getCredentialsFilteredByInternal( + this.getCacheInMemory().refreshTokens, + homeAccountId, + environment, + credentialType, + clientId, + realm, + target + ) as RefreshTokenCache; - return this.inMemoryCache.accounts[accountKey] || null; + return matchingCredentials; } /** - * append credential cache to in memory cache - * @param idT: IdTokenEntity - * @param at: AccessTokenEntity - * @param rt: RefreshTokenEntity + * Support function to help match credentials + * @param cacheCredentials + * @param homeAccountId + * @param environment + * @param credentialType + * @param clientId + * @param realm + * @param target */ - addCredentialCache( - accessToken: AccessTokenEntity, - idToken: IdTokenEntity, - refreshToken: RefreshTokenEntity - ): void { - this.inMemoryCache.accessTokens[accessToken.generateCredentialKey()] = accessToken; - this.inMemoryCache.idTokens[idToken.generateCredentialKey()] = idToken; - this.inMemoryCache.refreshTokens[refreshToken.generateCredentialKey()] = refreshToken; + private getCredentialsFilteredByInternal( + cacheCredentials: object, + homeAccountId?: string, + environment?: string, + credentialType?: string, + clientId?: string, + realm?: string, + target?: string + ): Object { + const matchingCredentials = {}; + let matches: boolean; + + Object.keys(cacheCredentials).forEach((key) => { + if (!!homeAccountId) { + matches = CacheHelper.matchHomeAccountId( + key, + homeAccountId + ); + } + + if (!!environment) { + matches = + matches && + CacheHelper.matchEnvironment(key, environment); + } + + if (!!realm) { + matches = matches && CacheHelper.matchRealm(key, realm); + } + + if (!!credentialType) { + matches = + matches && + CacheHelper.matchCredentialType(key, credentialType); + } + + if (!!clientId) { + matches = + matches && CacheHelper.matchClientId(key, clientId); + } + + if (!!target) { + matches = + matches && CacheHelper.matchTarget(key, target); + } + + if (matches) { + matchingCredentials[key] = cacheCredentials[key]; + } + }); + + return matchingCredentials; } /** - * append account to the in memory cache + * returns a boolean if the given account is removed * @param account */ - addAccountEntity(account: AccountEntity): void { - const accKey = account.generateAccountKey(); - if (!this.inMemoryCache.accounts[accKey]) { - this.inMemoryCache.accounts[accKey] = account; + removeAccount(account: AccountEntity): boolean { + const cache = this.getCacheInMemory(); + const accountKey = account.generateAccountKey(); + + delete cache.accounts[accountKey]; + return true; + } + + /** + * returns a boolean if the given account is removed + * @param account + */ + removeAccountContext(account: AccountEntity): boolean { + const cache = this.getCacheInMemory(); + + const accountId = account.generateAccountId(); + + // TODO: Check how this should be done, do we just remove the account or also the associated credentials always? + Object.keys(cache.accessTokens).forEach((key) => { + if ( + cache.accessTokens[key].generateAccountId() === accountId + ) + this.removeCredential(cache.accessTokens[key]); + }); + + Object.keys(cache.idTokens).forEach((key) => { + if (cache.idTokens[key].generateAccountId() === accountId) + this.removeCredential(cache.idTokens[key]); + }); + + Object.keys(cache.idTokens).forEach((key) => { + if (cache.idTokens[key].generateAccountId() === accountId) + this.removeCredential(cache.idTokens[key]); + }); + + this.removeAccount(account); + return true; + } + + /** + * returns a boolean if the given credential is removed + * @param credential + */ + removeCredential(credential: Credential): boolean { + const cache = this.getCacheInMemory(); + + switch (credential.credentialType) { + case CredentialType.ID_TOKEN: + delete cache.idTokens[ + credential.generateCredentialKey() + ]; + return true; + case CredentialType.ACCESS_TOKEN: + delete cache.accessTokens[ + credential.generateCredentialKey() + ]; + return true; + case CredentialType.REFRESH_TOKEN: + delete cache.refreshTokens[ + credential.generateCredentialKey() + ]; + return true; + default: + console.log("Cache entity type mismatch"); + return false; } } } diff --git a/lib/msal-common/src/unifiedCache/interface/IAccountCredentialCache.ts b/lib/msal-common/src/unifiedCache/interface/ICacheManager.ts similarity index 97% rename from lib/msal-common/src/unifiedCache/interface/IAccountCredentialCache.ts rename to lib/msal-common/src/unifiedCache/interface/ICacheManager.ts index 65a2351566..736b9ba3c9 100644 --- a/lib/msal-common/src/unifiedCache/interface/IAccountCredentialCache.ts +++ b/lib/msal-common/src/unifiedCache/interface/ICacheManager.ts @@ -7,7 +7,7 @@ import { AccountEntity } from "../entities/AccountEntity"; import { Credential } from "../entities/Credential"; import { AccountCache, CredentialCache } from "../utils/CacheTypes"; -export interface IAccountCredentialCache { +export interface ICacheManager { /** * saves account into cache * @param account diff --git a/lib/msal-common/src/unifiedCache/lookup/AccountCredentialCache.ts b/lib/msal-common/src/unifiedCache/lookup/AccountCredentialCache.ts deleted file mode 100644 index 3f99e1560c..0000000000 --- a/lib/msal-common/src/unifiedCache/lookup/AccountCredentialCache.ts +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ - -import { AccountEntity } from "../entities/AccountEntity"; -import { IdTokenEntity } from "../entities/IdTokenEntity"; -import { AccessTokenEntity } from "../entities/AccessTokenEntity"; -import { RefreshTokenEntity } from "../entities/RefreshTokenEntity"; -import { Credential } from "../entities/Credential"; -import { UnifiedCacheManager } from "../UnifiedCacheManager"; -import { CredentialType, Separators, CacheKeyPosition } from "../../utils/Constants"; -import { AccountCache, CredentialCache, IdTokenCache, AccessTokenCache, RefreshTokenCache } from "../utils/CacheTypes"; -import { IAccountCredentialCache } from "../interface/IAccountCredentialCache"; -import { CacheHelper } from "../utils/CacheHelper"; - -export class AccountCredentialCache implements IAccountCredentialCache { - private cacheManager: UnifiedCacheManager; - - constructor(cacheMgr: UnifiedCacheManager) { - this.cacheManager = cacheMgr; - } - - /** - * saves account into cache - * @param account - */ - saveAccount(account: AccountEntity): void { - const cache = this.cacheManager.getCacheInMemory(); - const key = account.generateAccountKey(); - cache.accounts[key] = account; - this.cacheManager.setCacheInMemory(cache); - } - - /** - * saves credential - accessToken, idToken or refreshToken into cache - * @param credential - */ - saveCredential(credential: Credential): void { - const cache = this.cacheManager.getCacheInMemory(); - const key = credential.generateCredentialKey(); - - switch (credential.credentialType) { - case CredentialType.ID_TOKEN: - cache.idTokens[key] = credential as IdTokenEntity; - break; - case CredentialType.ACCESS_TOKEN: - cache.accessTokens[key] = credential as AccessTokenEntity; - break; - case CredentialType.REFRESH_TOKEN: - cache.refreshTokens[key] = credential as RefreshTokenEntity; - break; - default: - console.log("Cache entity type mismatch"); - } - - this.cacheManager.setCacheInMemory(cache); - } - - /** - * Given account key retrieve an account - * @param key - */ - getAccount(key: string): AccountEntity { - return this.cacheManager.getCacheInMemory().accounts[key] || null; - } - - /** - * retrieve a credential - accessToken, idToken or refreshToken; given the cache key - * @param key - */ - getCredential(key: string): Credential { - const cache = this.cacheManager.getCacheInMemory(); - switch (key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.CREDENTIAL_TYPE]) { - case "idtoken": - return cache.idTokens[key] || null; - case "accesstoken": - return cache.accessTokens[key] || null; - case "refreshtoken": - return cache.refreshTokens[key] || null; - default: - console.log("Cache entity type mismatch"); - return null; - } - } - - /** - * retrieve accounts matching all provided filters; if no filter is set, get all accounts - * @param homeAccountId - * @param environment - * @param realm - */ - getAccountsFilteredBy( - homeAccountId?: string, - environment?: string, - realm?: string - ): AccountCache { - const accounts: AccountCache = this.cacheManager.getCacheInMemory().accounts; - const matchingAccounts: AccountCache = {}; - - let matches: boolean = true; - - Object.keys(accounts).forEach((key) => { - - if (!!homeAccountId) { - matches = CacheHelper.matchHomeAccountId(key, homeAccountId); - } - - if (!!environment) { - matches = matches && CacheHelper.matchEnvironment(key, environment); - } - - if (!!realm) { - matches = matches && CacheHelper.matchTarget(key, realm); - } - - if (matches) { - matchingAccounts[key] = accounts[key]; - } - }); - - return matchingAccounts; - } - - /** - * retrieve credentails matching all provided filters; if no filter is set, get all credentials - * @param homeAccountId - * @param environment - * @param credentialType - * @param clientId - * @param realm - * @param target - */ - getCredentialsFilteredBy( - homeAccountId?: string, - environment?: string, - credentialType?: string, - clientId?: string, - realm?: string, - target?: string - ): CredentialCache { - - const matchingCredentials: CredentialCache = { - idTokens: {}, - accessTokens: {}, - refreshTokens: {} - }; - - matchingCredentials.idTokens = this.getCredentialsFilteredByInternal( - this.cacheManager.getCacheInMemory().idTokens, - homeAccountId, - environment, - credentialType, - clientId, - realm, - target - ) as IdTokenCache; - - matchingCredentials.accessTokens = this.getCredentialsFilteredByInternal( - this.cacheManager.getCacheInMemory().accessTokens, - homeAccountId, - environment, - credentialType, - clientId, - realm, - target - ) as AccessTokenCache; - - matchingCredentials.refreshTokens = this.getCredentialsFilteredByInternal( - this.cacheManager.getCacheInMemory().refreshTokens, - homeAccountId, - environment, - credentialType, - clientId, - realm, - target - ) as RefreshTokenCache; - - return matchingCredentials; - } - - /** - * Support function to help match credentials - * @param cacheCredentials - * @param homeAccountId - * @param environment - * @param credentialType - * @param clientId - * @param realm - * @param target - */ - private getCredentialsFilteredByInternal( - cacheCredentials: object, - homeAccountId?: string, - environment?: string, - credentialType?: string, - clientId?: string, - realm?: string, - target?: string, - ): Object { - - const matchingCredentials = {}; - let matches: boolean; - - Object.keys(cacheCredentials).forEach((key) => { - if (!!homeAccountId) { - matches = CacheHelper.matchHomeAccountId(key, homeAccountId); - } - - if (!!environment) { - matches = matches && CacheHelper.matchEnvironment(key, environment); - } - - if (!!realm) { - matches = matches && CacheHelper.matchRealm(key, realm); - } - - if (!!credentialType) { - matches = matches && CacheHelper.matchCredentialType(key, credentialType); - } - - if (!!clientId) { - matches = matches && CacheHelper.matchClientId(key, clientId); - } - - if (!!target) { - matches = matches && CacheHelper.matchTarget(key, target); - } - - if (matches) { - matchingCredentials[key] = cacheCredentials[key]; - } - }); - - return matchingCredentials; - } - - /** - * returns a boolean if the given account is removed - * @param account - */ - removeAccount(account: AccountEntity): boolean { - const cache = this.cacheManager.getCacheInMemory(); - const accountKey = account.generateAccountKey(); - - delete cache.accounts[accountKey]; - return true; - } - - /** - * returns a boolean if the given account is removed - * @param account - */ - removeAccountContext(account: AccountEntity): boolean { - const cache = this.cacheManager.getCacheInMemory(); - - const accountId = account.generateAccountId(); - - // TODO: Check how this should be done, do we just remove the account or also the associated credentials always? - Object.keys(cache.accessTokens).forEach((key) => { - if (cache.accessTokens[key].generateAccountId() === accountId) - this.removeCredential(cache.accessTokens[key]); - }); - - Object.keys(cache.idTokens).forEach((key) => { - if (cache.idTokens[key].generateAccountId() === accountId) - this.removeCredential(cache.idTokens[key]); - }); - - Object.keys(cache.idTokens).forEach((key) => { - if (cache.idTokens[key].generateAccountId() === accountId) - this.removeCredential(cache.idTokens[key]); - }); - - this.removeAccount(account); - return true; - } - - /** - * returns a boolean if the given credential is removed - * @param credential - */ - removeCredential(credential: Credential): boolean { - const cache = this.cacheManager.getCacheInMemory(); - - switch (credential.credentialType) { - case CredentialType.ID_TOKEN: - delete cache.idTokens[credential.generateCredentialKey()]; - return true; - case CredentialType.ACCESS_TOKEN: - delete cache.accessTokens[credential.generateCredentialKey()]; - return true; - case CredentialType.REFRESH_TOKEN: - delete cache.refreshTokens[credential.generateCredentialKey()]; - return true; - default: - console.log("Cache entity type mismatch"); - return false; - } - } -} diff --git a/lib/msal-common/src/unifiedCache/lookup/CacheRecord.ts b/lib/msal-common/src/unifiedCache/lookup/CacheRecord.ts deleted file mode 100644 index ad5c5f4ff4..0000000000 --- a/lib/msal-common/src/unifiedCache/lookup/CacheRecord.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ - -import { AccountEntity } from "../entities/AccountEntity"; -import { IdTokenEntity } from "../entities/IdTokenEntity"; -import { AccessTokenEntity } from "../entities/AccessTokenEntity"; -import { RefreshTokenEntity } from "../entities/RefreshTokenEntity"; - -export type CacheRecord = { - account: AccountEntity, - idToken: IdTokenEntity, - accessToken: AccessTokenEntity, - refreshToken: RefreshTokenEntity -}; diff --git a/lib/msal-common/test/unifiedCache/UnifiedCacheManager.spec.ts b/lib/msal-common/test/unifiedCache/UnifiedCacheManager.spec.ts index ec1064f249..a16f18e3b4 100644 --- a/lib/msal-common/test/unifiedCache/UnifiedCacheManager.spec.ts +++ b/lib/msal-common/test/unifiedCache/UnifiedCacheManager.spec.ts @@ -1,26 +1,29 @@ import { expect } from "chai"; import { UnifiedCacheManager } from "../../src/unifiedCache/UnifiedCacheManager"; import { mockCache } from "./entities/cacheConstants"; -import { InMemoryCache, JsonCache } from "../../src/unifiedCache/utils/CacheTypes"; +import { InMemoryCache} from "../../src/unifiedCache/utils/CacheTypes"; import { ICacheStorage } from "../../src/cache/ICacheStorage"; import { Deserializer } from "../../src/unifiedCache/serialize/Deserializer"; +import { AccountEntity } from "../../src/unifiedCache/entities/AccountEntity"; +import { AccessTokenEntity } from "../../src/unifiedCache/entities/AccessTokenEntity"; const cacheJson = require("./serialize/cache.json"); describe("UnifiedCacheManager test cases", () => { - let store = {}; let storageInterface: ICacheStorage; const cache = JSON.stringify(cacheJson); - const inMemCache: InMemoryCache = Deserializer.deserializeAllCache(Deserializer.deserializeJSONBlob(cache)); + const inMemCache: InMemoryCache = Deserializer.deserializeAllCache( + Deserializer.deserializeJSONBlob(cache) + ); beforeEach(() => { storageInterface = { getCache(): InMemoryCache { return inMemCache; }, - setCache(): void { - // do nothing + setCache(inMemCache): void { + this.inMemCache = inMemCache; }, setItem(key: string, value: string): void { store[key] = value; @@ -40,11 +43,10 @@ describe("UnifiedCacheManager test cases", () => { clear(): void { store = {}; }, - } + }; }); it("initCache", () => { - let unifiedCacheManager = new UnifiedCacheManager(storageInterface); // create mock AccessToken @@ -53,26 +55,154 @@ describe("UnifiedCacheManager test cases", () => { const atTwo = mockCache.createMockATTwo(); const atTwoKey = atTwo.generateCredentialKey(); - expect(Object.keys(unifiedCacheManager.getCacheInMemory().accessTokens).length).to.equal(2); - expect(unifiedCacheManager.getCacheInMemory().accessTokens[atOneKey]).to.eql(atOne); - expect(unifiedCacheManager.getCacheInMemory().accessTokens[atTwoKey]).to.eql(atTwo); + expect( + Object.keys(unifiedCacheManager.getCacheInMemory().accessTokens) + .length + ).to.equal(2); + expect( + unifiedCacheManager.getCacheInMemory().accessTokens[atOneKey] + ).to.eql(atOne); + expect( + unifiedCacheManager.getCacheInMemory().accessTokens[atTwoKey] + ).to.eql(atTwo); + }); + + it("save account", () => { + let ac = new AccountEntity(); + Object.assign(ac, { + homeAccountId: "someUid.someUtid", + environment: "login.microsoftonline.com", + realm: "microsoft", + localAccountId: "object1234", + username: "Jane Goodman", + authorityType: "MSSTS", + clientInfo: "eyJ1aWQiOiJzb21lVWlkIiwgInV0aWQiOiJzb21lVXRpZCJ9", + }); + + let unifiedCacheManager = new UnifiedCacheManager(storageInterface); + + const accountKey = ac.generateAccountKey(); + unifiedCacheManager.saveAccount(ac); + expect( + unifiedCacheManager.getCacheInMemory().accounts[accountKey] + .homeAccountId + ).to.eql("someUid.someUtid"); + }); + + it("save credential", () => { + let at = new AccessTokenEntity(); + Object.assign(at, { + homeAccountId: "someUid.someUtid", + environment: "login.microsoftonline.com", + credentialType: "AccessToken", + clientId: "mock_client_id", + secret: "an access token sample", + realm: "microsoft", + target: "scope6 scope7", + cachedAt: "1000", + expiresOn: "4600", + extendedExpiresOn: "4600", + }); + + let unifiedCacheManager = new UnifiedCacheManager(storageInterface); + + const atKey = at.generateCredentialKey(); + unifiedCacheManager.saveCredential(at); + expect( + unifiedCacheManager.getCacheInMemory().accessTokens[atKey] + .homeAccountId + ).to.eql("someUid.someUtid"); }); it("getAccount", () => { + let unifiedCacheManager = new UnifiedCacheManager(storageInterface); + + expect( + unifiedCacheManager.getAccount( + "someuid.someutid-login.microsoftonline.com-microsoft" + ).homeAccountId + ).to.eql("someUid.someUtid"); + }); + + it("getCredential", () => { + let unifiedCacheManager = new UnifiedCacheManager(storageInterface); + + expect( + unifiedCacheManager.getCredential( + "someuid.someutid-login.microsoftonline.com-accesstoken-mock_client_id-microsoft-scope6 scope7" + ).homeAccountId + ).to.eql("someUid.someUtid"); + }); + + it("getAccounts", () => { + let unifiedCacheManager = new UnifiedCacheManager(storageInterface); + + let accounts = unifiedCacheManager.getAccountsFilteredBy("uid.utid"); + expect(Object.keys(accounts).length).to.eql(1); + + accounts = unifiedCacheManager.getAccountsFilteredBy( + null, + "login.microsoftonline.com" + ); + expect(Object.keys(accounts).length).to.eql(2); + }); + it("getCredentials", () => { let unifiedCacheManager = new UnifiedCacheManager(storageInterface); - // create mock Account - const acc = mockCache.createMockAcc(); - const homeAccountId = "uid.utid"; - const environment = "login.microsoftonline.com"; - const realm = "microsoft"; + let credentials = unifiedCacheManager.getCredentialsFilteredBy("uid.utid"); + expect(Object.keys(credentials.idTokens).length).to.eql(1); + expect(Object.keys(credentials.accessTokens).length).to.eql(2); + expect(Object.keys(credentials.refreshTokens).length).to.eql(2); - const genAcc = unifiedCacheManager.getAccount(homeAccountId, environment, realm); - expect(acc).to.eql(genAcc); + credentials = unifiedCacheManager.getCredentialsFilteredBy("someuid.someutid"); + expect(Object.keys(credentials.idTokens).length).to.eql(0); + expect(Object.keys(credentials.accessTokens).length).to.eql(1); + expect(Object.keys(credentials.refreshTokens).length).to.eql(0); + }); + + it("removeAccount", () => { + let unifiedCacheManager = new UnifiedCacheManager(storageInterface); + + let ac = new AccountEntity(); + Object.assign(ac, { + homeAccountId: "someUid.someUtid", + environment: "login.microsoftonline.com", + realm: "microsoft", + localAccountId: "object1234", + username: "Jane Goodman", + authorityType: "MSSTS", + clientInfo: "eyJ1aWQiOiJzb21lVWlkIiwgInV0aWQiOiJzb21lVXRpZCJ9", + }); - const randomAcc = unifiedCacheManager.getAccount("", "", ""); - expect(randomAcc).to.be.null; + unifiedCacheManager.removeAccount(ac); + const accountKey = ac.generateAccountKey(); + expect( + unifiedCacheManager.getCacheInMemory().accounts[accountKey] + ).to.eql(undefined); }); + it("removeCredential", () => { + let unifiedCacheManager = new UnifiedCacheManager(storageInterface); + + let at = new AccessTokenEntity(); + Object.assign(at, { + homeAccountId: "someUid.someUtid", + environment: "login.microsoftonline.com", + credentialType: "AccessToken", + clientId: "mock_client_id", + secret: "an access token sample", + realm: "microsoft", + target: "scope6 scope7", + cachedAt: "1000", + expiresOn: "4600", + extendedExpiresOn: "4600", + }); + + unifiedCacheManager.removeCredential(at); + const atKey = at.generateCredentialKey(); + expect( + unifiedCacheManager.getCacheInMemory().accessTokens[atKey] + ).to.eql(undefined); + }); }); diff --git a/lib/msal-common/test/unifiedCache/lookup/AccountCredentialCache.spec.ts b/lib/msal-common/test/unifiedCache/lookup/AccountCredentialCache.spec.ts deleted file mode 100644 index 1fa99b0615..0000000000 --- a/lib/msal-common/test/unifiedCache/lookup/AccountCredentialCache.spec.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { expect } from "chai"; -import { InMemoryCache } from "../../../src/unifiedCache/utils/CacheTypes"; -import { ICacheStorage } from ".../../../src/cache/ICacheStorage"; -import { Deserializer } from "../../../src/unifiedCache/serialize/Deserializer"; -import { AccountEntity } from "../../../src/unifiedCache/entities/AccountEntity"; -import { AccessTokenEntity } from "../../../src/unifiedCache/entities/AccessTokenEntity"; -import { AccountCredentialCache } from "../../../src/unifiedCache/lookup/AccountCredentialCache"; -import { UnifiedCacheManager } from "../../../src/unifiedCache/UnifiedCacheManager"; - -const cacheJson = require("./../serialize/cache.json"); - -describe("AccountCredentialCache test cases", () => { - let store = {}; - let storageInterface: ICacheStorage; - const cache = JSON.stringify(cacheJson); - const inMemCache: InMemoryCache = Deserializer.deserializeAllCache(Deserializer.deserializeJSONBlob(cache)); - - beforeEach(() => { - storageInterface = { - getCache(): InMemoryCache { - return inMemCache; - }, - setCache(inMemCache): void { - this.inMemCache = inMemCache; - }, - setItem(key: string, value: string): void { - store[key] = value; - }, - getItem(key: string): string { - return store[key]; - }, - removeItem(key: string): void { - delete store[key]; - }, - containsKey(key: string): boolean { - return !!store[key]; - }, - getKeys(): string[] { - return Object.keys(store); - }, - clear(): void { - store = {}; - }, - } - }); - - it("save account", () => { - let ac = new AccountEntity(); - Object.assign( - ac, - { - homeAccountId: "someUid.someUtid", - environment: "login.microsoftonline.com", - realm: "microsoft", - localAccountId: "object1234", - username: "Jane Goodman", - authorityType: "MSSTS", - clientInfo: "eyJ1aWQiOiJzb21lVWlkIiwgInV0aWQiOiJzb21lVXRpZCJ9", - } - ); - - let unifiedCacheManager = new UnifiedCacheManager(storageInterface); - let accCredCache = new AccountCredentialCache(unifiedCacheManager); - - const accountKey = ac.generateAccountKey(); - accCredCache.saveAccount(ac); - expect(unifiedCacheManager.getCacheInMemory().accounts[accountKey].homeAccountId).to.eql("someUid.someUtid"); - }); - - it("save credential", () => { - let at = new AccessTokenEntity(); - Object.assign( - at, - { - homeAccountId: "someUid.someUtid", - environment: "login.microsoftonline.com", - credentialType: "AccessToken", - clientId: "mock_client_id", - secret: "an access token sample", - realm: "microsoft", - target: "scope6 scope7", - cachedAt: "1000", - expiresOn: "4600", - extendedExpiresOn: "4600", - } - ); - - let unifiedCacheManager = new UnifiedCacheManager(storageInterface); - let accCredCache = new AccountCredentialCache(unifiedCacheManager); - - const atKey = at.generateCredentialKey(); - accCredCache.saveCredential(at); - expect(unifiedCacheManager.getCacheInMemory().accessTokens[atKey].homeAccountId).to.eql("someUid.someUtid"); - }); - - it("getAccount", () => { - let unifiedCacheManager = new UnifiedCacheManager(storageInterface); - let accCredCache = new AccountCredentialCache(unifiedCacheManager); - - expect(accCredCache.getAccount("someuid.someutid-login.microsoftonline.com-microsoft").homeAccountId).to.eql("someUid.someUtid"); - }); - - it("getCredential", () => { - let unifiedCacheManager = new UnifiedCacheManager(storageInterface); - let accCredCache = new AccountCredentialCache(unifiedCacheManager); - - expect( - accCredCache.getCredential( - "someuid.someutid-login.microsoftonline.com-accesstoken-mock_client_id-microsoft-scope6 scope7" - ).homeAccountId - ).to.eql("someUid.someUtid"); - }); - - it("getAccounts", () => { - let unifiedCacheManager = new UnifiedCacheManager(storageInterface); - let accCredCache = new AccountCredentialCache(unifiedCacheManager); - - let accounts = accCredCache.getAccountsFilteredBy("uid.utid"); - expect(Object.keys(accounts).length).to.eql(1); - - accounts = accCredCache.getAccountsFilteredBy(null, "login.microsoftonline.com"); - expect(Object.keys(accounts).length).to.eql(2); - }); - - it("getCredentials", () => { - let unifiedCacheManager = new UnifiedCacheManager(storageInterface); - let accCredCache = new AccountCredentialCache(unifiedCacheManager); - - let credentials = accCredCache.getCredentialsFilteredBy("uid.utid"); - expect(Object.keys(credentials.idTokens).length).to.eql(1); - expect(Object.keys(credentials.accessTokens).length).to.eql(2); - expect(Object.keys(credentials.refreshTokens).length).to.eql(2); - - credentials = accCredCache.getCredentialsFilteredBy("someuid.someutid"); - expect(Object.keys(credentials.idTokens).length).to.eql(0); - expect(Object.keys(credentials.accessTokens).length).to.eql(1); - expect(Object.keys(credentials.refreshTokens).length).to.eql(0); - }); - - it("removeAccount", () => { - let unifiedCacheManager = new UnifiedCacheManager(storageInterface); - let accCredCache = new AccountCredentialCache(unifiedCacheManager); - - let ac = new AccountEntity(); - Object.assign( - ac, - { - homeAccountId: "someUid.someUtid", - environment: "login.microsoftonline.com", - realm: "microsoft", - localAccountId: "object1234", - username: "Jane Goodman", - authorityType: "MSSTS", - clientInfo: "eyJ1aWQiOiJzb21lVWlkIiwgInV0aWQiOiJzb21lVXRpZCJ9", - } - ); - - accCredCache.removeAccount(ac); - const accountKey = ac.generateAccountKey(); - expect(unifiedCacheManager.getCacheInMemory().accounts[accountKey]).to.eql(undefined); - }); - - it("removeCredential", () => { - let unifiedCacheManager = new UnifiedCacheManager(storageInterface); - let accCredCache = new AccountCredentialCache(unifiedCacheManager); - - let at = new AccessTokenEntity(); - Object.assign(at, { - homeAccountId: "someUid.someUtid", - environment: "login.microsoftonline.com", - credentialType: "AccessToken", - clientId: "mock_client_id", - secret: "an access token sample", - realm: "microsoft", - target: "scope6 scope7", - cachedAt: "1000", - expiresOn: "4600", - extendedExpiresOn: "4600", - }); - - accCredCache.removeCredential(at); - const atKey = at.generateCredentialKey(); - expect(unifiedCacheManager.getCacheInMemory().accessTokens[atKey]).to.eql(undefined); - }); -}); From 4ddae07a2b82adfe7c2e7ba7bbe62a91bb306384 Mon Sep 17 00:00:00 2001 From: sameerag Date: Thu, 14 May 2020 02:58:33 -0700 Subject: [PATCH 07/17] Fix linter issues --- lib/msal-common/src/response/ResponseHandler.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/msal-common/src/response/ResponseHandler.ts b/lib/msal-common/src/response/ResponseHandler.ts index c184fc979d..c434f86634 100644 --- a/lib/msal-common/src/response/ResponseHandler.ts +++ b/lib/msal-common/src/response/ResponseHandler.ts @@ -172,9 +172,7 @@ export class ResponseHandler { * @param authority */ addAccountToCache(serverTokenResponse: ServerAuthorizationTokenResponse, idToken: IdToken, authority: Authority): void { - const environment = authority.canonicalAuthorityUrlComponents.HostNameAndPort; - let accountEntity: AccountEntity; - accountEntity = this.generateAccountEntity( + const accountEntity: AccountEntity = this.generateAccountEntity( serverTokenResponse, idToken, authority From 2b0afe18f71a58872a3b6fb532562fea4b06a91c Mon Sep 17 00:00:00 2001 From: sameerag Date: Thu, 14 May 2020 02:59:04 -0700 Subject: [PATCH 08/17] fix linter issues --- lib/msal-common/src/response/ResponseHandler.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/msal-common/src/response/ResponseHandler.ts b/lib/msal-common/src/response/ResponseHandler.ts index c184fc979d..ed89de4d28 100644 --- a/lib/msal-common/src/response/ResponseHandler.ts +++ b/lib/msal-common/src/response/ResponseHandler.ts @@ -172,9 +172,7 @@ export class ResponseHandler { * @param authority */ addAccountToCache(serverTokenResponse: ServerAuthorizationTokenResponse, idToken: IdToken, authority: Authority): void { - const environment = authority.canonicalAuthorityUrlComponents.HostNameAndPort; - let accountEntity: AccountEntity; - accountEntity = this.generateAccountEntity( + let accountEntity: AccountEntity = this.generateAccountEntity( serverTokenResponse, idToken, authority From 7ca83abbe11d67f2254a16927c35853c98705731 Mon Sep 17 00:00:00 2001 From: sameerag Date: Thu, 14 May 2020 03:13:03 -0700 Subject: [PATCH 09/17] fix linter issues --- lib/msal-common/src/response/ResponseHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msal-common/src/response/ResponseHandler.ts b/lib/msal-common/src/response/ResponseHandler.ts index ed89de4d28..c434f86634 100644 --- a/lib/msal-common/src/response/ResponseHandler.ts +++ b/lib/msal-common/src/response/ResponseHandler.ts @@ -172,7 +172,7 @@ export class ResponseHandler { * @param authority */ addAccountToCache(serverTokenResponse: ServerAuthorizationTokenResponse, idToken: IdToken, authority: Authority): void { - let accountEntity: AccountEntity = this.generateAccountEntity( + const accountEntity: AccountEntity = this.generateAccountEntity( serverTokenResponse, idToken, authority From 87ce0326c86e71beb54f333a350ed6c4c8bfefc5 Mon Sep 17 00:00:00 2001 From: sameerag Date: Fri, 15 May 2020 02:11:35 -0700 Subject: [PATCH 10/17] Changing the filter interface for users to be able to pass optional params and avoid null params --- .../src/unifiedCache/UnifiedCacheManager.ts | 93 +++++++++++++------ .../unifiedCache/interface/ICacheManager.ts | 13 +-- .../src/unifiedCache/utils/CacheHelper.ts | 14 ++- .../src/unifiedCache/utils/CacheTypes.ts | 21 +++++ .../unifiedCache/UnifiedCacheManager.spec.ts | 24 +++-- 5 files changed, 118 insertions(+), 47 deletions(-) diff --git a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts index 26fbb4b28d..7ac7feb34f 100644 --- a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts +++ b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -import { InMemoryCache, JsonCache } from "./utils/CacheTypes"; +import { InMemoryCache, JsonCache, AccountFilter, CredentialFilter } from "./utils/CacheTypes"; import { AccessTokenEntity } from "./entities/AccessTokenEntity"; import { IdTokenEntity } from "./entities/IdTokenEntity"; import { RefreshTokenEntity } from "./entities/RefreshTokenEntity"; @@ -183,11 +183,29 @@ export class UnifiedCacheManager implements ICacheManager { /** * retrieve accounts matching all provided filters; if no filter is set, get all accounts + * not checking for casing as keys are all generated in lower case, remember to convert to lower case if object properties are compared * @param homeAccountId * @param environment * @param realm */ getAccountsFilteredBy( + accountFilter: AccountFilter + ): AccountCache { + return this.getAccountsFilteredByInternal( + accountFilter.homeAccountId, + accountFilter.environment, + accountFilter.realm + ); + } + + /** + * retrieve accounts matching all provided filters; if no filter is set, get all accounts + * not checking for casing as keys are all generated in lower case, remember to convert to lower case if object properties are compared + * @param homeAccountId + * @param environment + * @param realm + */ + getAccountsFilteredByInternal( homeAccountId?: string, environment?: string, realm?: string @@ -199,20 +217,15 @@ export class UnifiedCacheManager implements ICacheManager { Object.keys(accounts).forEach((key) => { if (!!homeAccountId) { - matches = CacheHelper.matchHomeAccountId( - key, - homeAccountId - ); + matches = CacheHelper.matchHomeAccountId(key, homeAccountId); } if (!!environment) { - matches = - matches && - CacheHelper.matchEnvironment(key, environment); + matches = matches && CacheHelper.matchEnvironment(key, environment); } if (!!realm) { - matches = matches && CacheHelper.matchTarget(key, realm); + matches = matches && CacheHelper.matchRealm(key, realm); } if (matches) { @@ -233,6 +246,33 @@ export class UnifiedCacheManager implements ICacheManager { * @param target */ getCredentialsFilteredBy( + filter: CredentialFilter + ): CredentialCache { + const matchingCredentials: CredentialCache = { + idTokens: {}, + accessTokens: {}, + refreshTokens: {}, + }; + + return this.getCredentialsFilteredByInternal( + filter.homeAccountId, + filter.environment, + filter.clientId, + filter.realm, + filter.target + ); + } + + /** + * retrieve credentails matching all provided filters; if no filter is set, get all credentials + * @param homeAccountId + * @param environment + * @param credentialType + * @param clientId + * @param realm + * @param target + */ + getCredentialsFilteredByInternal( homeAccountId?: string, environment?: string, credentialType?: string, @@ -246,7 +286,7 @@ export class UnifiedCacheManager implements ICacheManager { refreshTokens: {}, }; - matchingCredentials.idTokens = this.getCredentialsFilteredByInternal( + matchingCredentials.idTokens = this.getCredentialsFilteredByCredentialType( this.getCacheInMemory().idTokens, homeAccountId, environment, @@ -256,7 +296,7 @@ export class UnifiedCacheManager implements ICacheManager { target ) as IdTokenCache; - matchingCredentials.accessTokens = this.getCredentialsFilteredByInternal( + matchingCredentials.accessTokens = this.getCredentialsFilteredByCredentialType( this.getCacheInMemory().accessTokens, homeAccountId, environment, @@ -266,7 +306,7 @@ export class UnifiedCacheManager implements ICacheManager { target ) as AccessTokenCache; - matchingCredentials.refreshTokens = this.getCredentialsFilteredByInternal( + matchingCredentials.refreshTokens = this.getCredentialsFilteredByCredentialType( this.getCacheInMemory().refreshTokens, homeAccountId, environment, @@ -289,7 +329,7 @@ export class UnifiedCacheManager implements ICacheManager { * @param realm * @param target */ - private getCredentialsFilteredByInternal( + private getCredentialsFilteredByCredentialType( cacheCredentials: object, homeAccountId?: string, environment?: string, @@ -299,7 +339,7 @@ export class UnifiedCacheManager implements ICacheManager { target?: string ): Object { const matchingCredentials = {}; - let matches: boolean; + let matches: boolean = true; Object.keys(cacheCredentials).forEach((key) => { if (!!homeAccountId) { @@ -310,9 +350,7 @@ export class UnifiedCacheManager implements ICacheManager { } if (!!environment) { - matches = - matches && - CacheHelper.matchEnvironment(key, environment); + matches = matches && CacheHelper.matchEnvironment(key, environment); } if (!!realm) { @@ -320,19 +358,16 @@ export class UnifiedCacheManager implements ICacheManager { } if (!!credentialType) { - matches = - matches && - CacheHelper.matchCredentialType(key, credentialType); + matches = matches && CacheHelper.matchCredentialType(key, credentialType); } if (!!clientId) { - matches = - matches && CacheHelper.matchClientId(key, clientId); + matches = matches && CacheHelper.matchClientId(key, clientId); } - if (!!target) { - matches = - matches && CacheHelper.matchTarget(key, target); + // idTokens do not have "target", target specific refreshTokens do exist for some types of authentication + if (!!target && CacheHelper.getCredentialType(key) != CredentialType.ID_TOKEN.toString().toLowerCase()) { + matches = matches && CacheHelper.matchTarget(key, target); } if (matches) { @@ -351,8 +386,12 @@ export class UnifiedCacheManager implements ICacheManager { const cache = this.getCacheInMemory(); const accountKey = account.generateAccountKey(); - delete cache.accounts[accountKey]; - return true; + if (!!cache.accounts[accountKey]) { + delete cache.accounts[accountKey]; + return true; + } + + return false; } /** diff --git a/lib/msal-common/src/unifiedCache/interface/ICacheManager.ts b/lib/msal-common/src/unifiedCache/interface/ICacheManager.ts index 736b9ba3c9..69b29f95d7 100644 --- a/lib/msal-common/src/unifiedCache/interface/ICacheManager.ts +++ b/lib/msal-common/src/unifiedCache/interface/ICacheManager.ts @@ -5,7 +5,7 @@ import { AccountEntity } from "../entities/AccountEntity"; import { Credential } from "../entities/Credential"; -import { AccountCache, CredentialCache } from "../utils/CacheTypes"; +import { AccountCache, CredentialCache, AccountFilter, CredentialFilter } from "../utils/CacheTypes"; export interface ICacheManager { /** @@ -39,9 +39,7 @@ export interface ICacheManager { * @param realm */ getAccountsFilteredBy( - homeAccountId?: string, - environment?: string, - realm?: string + filter: AccountFilter ): AccountCache; /** @@ -54,12 +52,7 @@ export interface ICacheManager { * @param target */ getCredentialsFilteredBy( - homeAccountId?: string, - environment?: string, - credentialType?: string, - clientId?: string, - realm?: string, - target?: string + filter: CredentialFilter ): CredentialCache; /** diff --git a/lib/msal-common/src/unifiedCache/utils/CacheHelper.ts b/lib/msal-common/src/unifiedCache/utils/CacheHelper.ts index 47671d746d..8a6c1f70fd 100644 --- a/lib/msal-common/src/unifiedCache/utils/CacheHelper.ts +++ b/lib/msal-common/src/unifiedCache/utils/CacheHelper.ts @@ -71,7 +71,7 @@ export class CacheHelper { * // TODO: Confirm equality for enum vs string here */ static matchCredentialType(key: string, credentialType: string): boolean { - return credentialType == key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.CREDENTIAL_TYPE]; + return credentialType.toLowerCase() === key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.CREDENTIAL_TYPE].toString().toLowerCase(); } /** @@ -98,7 +98,7 @@ export class CacheHelper { * @param target */ static matchTarget(key: string, target: string): boolean { - return this.targetsIntersect(key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.TARGET], target); + return CacheHelper.targetsIntersect(key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.TARGET], target); } /** @@ -117,4 +117,14 @@ export class CacheHelper { return isSubset; } + + /** + * helper function to return `CredentialType` + * @param key + */ + static getCredentialType(key: string): string { + return key.split(Separators.CACHE_KEY_SEPARATOR)[ + CacheKeyPosition.CREDENTIAL_TYPE + ]; + } } diff --git a/lib/msal-common/src/unifiedCache/utils/CacheTypes.ts b/lib/msal-common/src/unifiedCache/utils/CacheTypes.ts index 78abb03495..7773a86367 100644 --- a/lib/msal-common/src/unifiedCache/utils/CacheTypes.ts +++ b/lib/msal-common/src/unifiedCache/utils/CacheTypes.ts @@ -38,3 +38,24 @@ export type CredentialCache = { accessTokens: AccessTokenCache; refreshTokens: RefreshTokenCache; }; + +/** + * Account: -- + */ +export type AccountFilter = { + homeAccountId?: string; + environment?: string; + realm?: string; +} + +/** + * Credential: ----- + */ +export type CredentialFilter = { + homeAccountId?: string; + environment?: string; + credentialType?: string; + clientId?: string; + realm?: string; + target?: string; +}; diff --git a/lib/msal-common/test/unifiedCache/UnifiedCacheManager.spec.ts b/lib/msal-common/test/unifiedCache/UnifiedCacheManager.spec.ts index a16f18e3b4..fe82128ecf 100644 --- a/lib/msal-common/test/unifiedCache/UnifiedCacheManager.spec.ts +++ b/lib/msal-common/test/unifiedCache/UnifiedCacheManager.spec.ts @@ -1,7 +1,7 @@ import { expect } from "chai"; import { UnifiedCacheManager } from "../../src/unifiedCache/UnifiedCacheManager"; import { mockCache } from "./entities/cacheConstants"; -import { InMemoryCache} from "../../src/unifiedCache/utils/CacheTypes"; +import { InMemoryCache, CredentialFilter, AccountFilter} from "../../src/unifiedCache/utils/CacheTypes"; import { ICacheStorage } from "../../src/cache/ICacheStorage"; import { Deserializer } from "../../src/unifiedCache/serialize/Deserializer"; import { AccountEntity } from "../../src/unifiedCache/entities/AccountEntity"; @@ -137,28 +137,36 @@ describe("UnifiedCacheManager test cases", () => { it("getAccounts", () => { let unifiedCacheManager = new UnifiedCacheManager(storageInterface); - let accounts = unifiedCacheManager.getAccountsFilteredBy("uid.utid"); + const filterOne: AccountFilter = { homeAccountId: "uid.utid" }; + let accounts = unifiedCacheManager.getAccountsFilteredBy(filterOne); expect(Object.keys(accounts).length).to.eql(1); - accounts = unifiedCacheManager.getAccountsFilteredBy( - null, - "login.microsoftonline.com" - ); + const filterTwo: AccountFilter = { environment: "login.microsoftonline.com" }; + accounts = unifiedCacheManager.getAccountsFilteredBy(filterTwo); expect(Object.keys(accounts).length).to.eql(2); }); it("getCredentials", () => { let unifiedCacheManager = new UnifiedCacheManager(storageInterface); - let credentials = unifiedCacheManager.getCredentialsFilteredBy("uid.utid"); + // filter by homeAccountId + const filterOne: CredentialFilter = { homeAccountId: "uid.utid" }; + let credentials = unifiedCacheManager.getCredentialsFilteredBy(filterOne); expect(Object.keys(credentials.idTokens).length).to.eql(1); expect(Object.keys(credentials.accessTokens).length).to.eql(2); expect(Object.keys(credentials.refreshTokens).length).to.eql(2); - credentials = unifiedCacheManager.getCredentialsFilteredBy("someuid.someutid"); + // filter by homeAccountId + const filterTwo: CredentialFilter = { homeAccountId: "someuid.someutid" }; + credentials = unifiedCacheManager.getCredentialsFilteredBy(filterTwo); expect(Object.keys(credentials.idTokens).length).to.eql(0); expect(Object.keys(credentials.accessTokens).length).to.eql(1); expect(Object.keys(credentials.refreshTokens).length).to.eql(0); + + // filter by target + const filterThree = { target: "scope1 scope2 scope3" }; + credentials = unifiedCacheManager.getCredentialsFilteredBy(filterThree); + console.log(credentials.accessTokens); }); it("removeAccount", () => { From d109982ebd568c3d3bd1d41e3476edee5e074997 Mon Sep 17 00:00:00 2001 From: sameerag Date: Fri, 15 May 2020 13:37:03 -0700 Subject: [PATCH 11/17] fixing compile issues --- lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts | 7 +------ lib/msal-common/src/unifiedCache/utils/CacheTypes.ts | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts index 7ac7feb34f..f2fee14b4c 100644 --- a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts +++ b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts @@ -189,7 +189,7 @@ export class UnifiedCacheManager implements ICacheManager { * @param realm */ getAccountsFilteredBy( - accountFilter: AccountFilter + accountFilter: AccountFilter ): AccountCache { return this.getAccountsFilteredByInternal( accountFilter.homeAccountId, @@ -248,11 +248,6 @@ export class UnifiedCacheManager implements ICacheManager { getCredentialsFilteredBy( filter: CredentialFilter ): CredentialCache { - const matchingCredentials: CredentialCache = { - idTokens: {}, - accessTokens: {}, - refreshTokens: {}, - }; return this.getCredentialsFilteredByInternal( filter.homeAccountId, diff --git a/lib/msal-common/src/unifiedCache/utils/CacheTypes.ts b/lib/msal-common/src/unifiedCache/utils/CacheTypes.ts index 7773a86367..8786e6d758 100644 --- a/lib/msal-common/src/unifiedCache/utils/CacheTypes.ts +++ b/lib/msal-common/src/unifiedCache/utils/CacheTypes.ts @@ -46,7 +46,7 @@ export type AccountFilter = { homeAccountId?: string; environment?: string; realm?: string; -} +}; /** * Credential: ----- From 7369e5137d8adc327a0b3727885b6cc453220421 Mon Sep 17 00:00:00 2001 From: sameerag Date: Tue, 19 May 2020 14:03:26 -0700 Subject: [PATCH 12/17] Response Changes --- .../src/response/ResponseHandler.ts | 126 +++++++----------- .../src/unifiedCache/UnifiedCacheManager.ts | 43 ++---- .../entities/AccessTokenEntity.ts | 29 ++-- .../src/unifiedCache/entities/CacheRecord.ts | 16 +++ .../unifiedCache/entities/IdTokenEntity.ts | 10 +- .../entities/RefreshTokenEntity.ts | 9 +- 6 files changed, 96 insertions(+), 137 deletions(-) create mode 100644 lib/msal-common/src/unifiedCache/entities/CacheRecord.ts diff --git a/lib/msal-common/src/response/ResponseHandler.ts b/lib/msal-common/src/response/ResponseHandler.ts index c434f86634..7004c76241 100644 --- a/lib/msal-common/src/response/ResponseHandler.ts +++ b/lib/msal-common/src/response/ResponseHandler.ts @@ -22,6 +22,8 @@ import { IdTokenEntity } from "../unifiedCache/entities/IdTokenEntity"; import { AccessTokenEntity } from "../unifiedCache/entities/AccessTokenEntity"; import { RefreshTokenEntity } from "../unifiedCache/entities/RefreshTokenEntity"; import { InteractionRequiredAuthError } from "../error/InteractionRequiredAuthError"; +import { Separators } from '../utils/Constants'; +import { CacheRecord } from '../unifiedCache/entities/CacheRecord'; /** * Class that handles response parsing. @@ -104,84 +106,34 @@ export class ResponseHandler { * @param state */ generateAuthenticationResult(serverTokenResponse: ServerAuthorizationTokenResponse, authority: Authority): AuthenticationResult { - // Retrieve current account if in Cache - // TODO: add this once the req for cache look up for tokens is confirmed - const authenticationResult = this.processTokenResponse(serverTokenResponse, authority); - - const environment = authority.canonicalAuthorityUrlComponents.HostNameAndPort; - this.addCredentialsToCache(authenticationResult, environment, serverTokenResponse.refresh_token); - - return authenticationResult; - } - - /** - * Returns a new AuthenticationResult with the data from original result filled with the relevant data. - * @param authenticationResult - * @param idTokenString(raw idToken in the server response) - */ - processTokenResponse(serverTokenResponse: ServerAuthorizationTokenResponse, authority: Authority): AuthenticationResult { - const authenticationResult: AuthenticationResult = { - uniqueId: "", - tenantId: "", - tokenType: "", - idToken: null, - idTokenClaims: null, - accessToken: "", - scopes: [], - expiresOn: null, - familyId: null - }; - - // IdToken + // create an idToken object (not entity) const idTokenObj = new IdToken(serverTokenResponse.id_token, this.cryptoObj); - // if account is not in cache, append it to the cache - this.addAccountToCache(serverTokenResponse, idTokenObj, authority); + // save the response tokens + const cacheRecord = this.generateCacheRecord(serverTokenResponse, idTokenObj, authority); + this.uCacheManager.saveCacheRecord(cacheRecord); - // TODO: Check how this changes for auth code response - const expiresSeconds = Number(idTokenObj.claims.exp); - if (expiresSeconds && !authenticationResult.expiresOn) { - authenticationResult.expiresOn = new Date(expiresSeconds * 1000); - } // Expiration calculation const expiresInSeconds = TimeUtils.nowSeconds() + serverTokenResponse.expires_in; const extendedExpiresInSeconds = expiresInSeconds + serverTokenResponse.ext_expires_in; - // Set consented scopes in response - const responseScopes = ScopeSet.fromString(serverTokenResponse.scope, this.clientId, true); - return { - ...authenticationResult, + const responseScopes = ScopeSet.fromString(serverTokenResponse.scope, this.clientId, true); + let authenticationResult = { uniqueId: idTokenObj.claims.oid || idTokenObj.claims.sub, tenantId: idTokenObj.claims.tid, + scopes: responseScopes.asArray(), + tokenType: "Bearer", idToken: idTokenObj.rawIdToken, idTokenClaims: idTokenObj.claims, accessToken: serverTokenResponse.access_token, expiresOn: new Date(expiresInSeconds), extExpiresOn: new Date(extendedExpiresInSeconds), - scopes: responseScopes.asArray(), - familyId: serverTokenResponse.foci, + familyId: serverTokenResponse.foci || null, }; - } - /** - * if Account is not in the cache, generateAccount and append it to the cache - * @param serverTokenResponse - * @param idToken - * @param authority - */ - addAccountToCache(serverTokenResponse: ServerAuthorizationTokenResponse, idToken: IdToken, authority: Authority): void { - const accountEntity: AccountEntity = this.generateAccountEntity( - serverTokenResponse, - idToken, - authority - ); - - const cachedAccount: AccountEntity = this.uCacheManager.getAccount(accountEntity.generateAccountKey()); - if (!cachedAccount) { - this.uCacheManager.addAccountEntity(accountEntity); - } + return authenticationResult; } /** @@ -208,35 +160,53 @@ export class ResponseHandler { } /** - * Appends the minted tokens to the in-memory cache - * @param authenticationResult + * Generates CacheRecord + * @param serverTokenResponse + * @param idTokenObj * @param authority */ - addCredentialsToCache( - authenticationResult: AuthenticationResult, - authority: string, - refreshToken: string - ): void { - const idTokenEntity = IdTokenEntity.createIdTokenEntity( + generateCacheRecord(serverTokenResponse: ServerAuthorizationTokenResponse, idTokenObj: IdToken, authority: Authority): CacheRecord { + + let cacheRecord = new CacheRecord(); + + // Account + cacheRecord.account = this.generateAccountEntity( + serverTokenResponse, + idTokenObj, + authority + ); + + // IdToken + cacheRecord.idToken = IdTokenEntity.createIdTokenEntity( this.homeAccountIdentifier, - authenticationResult, + authority.canonicalAuthorityUrlComponents.HostNameAndPort, + serverTokenResponse.id_token, this.clientId, - authority + idTokenObj.claims.tid ); - const accessTokenEntity = AccessTokenEntity.createAccessTokenEntity( + + // AccessToken + const responseScopes = ScopeSet.fromString(serverTokenResponse.scope, this.clientId, true); + cacheRecord.accessToken = AccessTokenEntity.createAccessTokenEntity( this.homeAccountIdentifier, - authenticationResult, + authority.canonicalAuthorityUrlComponents.HostNameAndPort, + serverTokenResponse.access_token, this.clientId, - authority + idTokenObj.claims.tid, + responseScopes.asArray().join(" "), + serverTokenResponse.expires_in, + serverTokenResponse.ext_expires_in ); - const refreshTokenEntity = RefreshTokenEntity.createRefreshTokenEntity( + + // refreshToken + cacheRecord.refreshToken = RefreshTokenEntity.createRefreshTokenEntity( this.homeAccountIdentifier, - authenticationResult, - refreshToken, + authority.canonicalAuthorityUrlComponents.HostNameAndPort, + serverTokenResponse.refresh_token, this.clientId, - authority + serverTokenResponse.foci ); - this.uCacheManager.addCredentialCache(accessTokenEntity, idTokenEntity, refreshTokenEntity); + return cacheRecord; } } diff --git a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts index 7ac7feb34f..608455d36c 100644 --- a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts +++ b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts @@ -26,6 +26,7 @@ import { } from "./utils/CacheTypes"; import { ICacheManager } from "./interface/ICacheManager"; import { CacheHelper } from "./utils/CacheHelper"; +import { CacheRecord } from './entities/CacheRecord'; export class UnifiedCacheManager implements ICacheManager { // Storage interface @@ -71,43 +72,21 @@ export class UnifiedCacheManager implements ICacheManager { } /** - * append credential cache to in memory cach - * @param accessToken - * @param idToken - * @param refreshToken - */ - addCredentialCache( - accessToken: AccessTokenEntity, - idToken: IdTokenEntity, - refreshToken: RefreshTokenEntity - ): void { - this.inMemoryCache.accessTokens[ - accessToken.generateCredentialKey() - ] = accessToken; - this.inMemoryCache.idTokens[ - idToken.generateCredentialKey() - ] = idToken; - this.inMemoryCache.refreshTokens[ - refreshToken.generateCredentialKey() - ] = refreshToken; - } - - /** - * append account to the in memory cache - * @param account + * Returns all accounts in memory */ - addAccountEntity(account: AccountEntity): void { - const accKey = account.generateAccountKey(); - if (!this.inMemoryCache.accounts[accKey]) { - this.inMemoryCache.accounts[accKey] = account; - } + getAllAccounts(): AccountCache { + return this.inMemoryCache.accounts; } /** - * Returns all accounts in memory + * saves a cache record + * @param cacheRecord */ - getAllAccounts(): AccountCache { - return this.inMemoryCache.accounts; + saveCacheRecord(cacheRecord: CacheRecord): void { + this.saveAccount(cacheRecord.account); + this.saveCredential(cacheRecord.idToken); + this.saveCredential(cacheRecord.accessToken); + this.saveCredential(cacheRecord.refreshToken); } /** diff --git a/lib/msal-common/src/unifiedCache/entities/AccessTokenEntity.ts b/lib/msal-common/src/unifiedCache/entities/AccessTokenEntity.ts index 72268536d3..f3fc461c84 100644 --- a/lib/msal-common/src/unifiedCache/entities/AccessTokenEntity.ts +++ b/lib/msal-common/src/unifiedCache/entities/AccessTokenEntity.ts @@ -5,7 +5,6 @@ import { Credential } from "./Credential"; import { CredentialType } from "../../utils/Constants"; -import { AuthenticationResult } from "../../response/AuthenticationResult"; /** * ACCESS_TOKEN Credential Type @@ -22,22 +21,22 @@ export class AccessTokenEntity extends Credential { /** * Create AccessTokenEntity - * @param homeAccountId - * @param authenticationResult - * @param clientId - * @param authority - */ + */ static createAccessTokenEntity( homeAccountId: string, - authenticationResult: AuthenticationResult, + environment: string, + accessToken: string, clientId: string, - environment: string + tenantId: string, + scopes: string, + expiresOn: number, + extExpiresOn: number ): AccessTokenEntity { const atEntity: AccessTokenEntity = new AccessTokenEntity(); atEntity.homeAccountId = homeAccountId; atEntity.credentialType = CredentialType.ACCESS_TOKEN; - atEntity.secret = authenticationResult.accessToken; + atEntity.secret = accessToken; const date = new Date(); const currentTime = date.getMilliseconds() / 1000; @@ -46,17 +45,13 @@ export class AccessTokenEntity extends Credential { // TODO: Crosscheck the exact conversion UTC // Token expiry time. // This value should be  calculated based on the current UTC time measured locally and the value  expires_in Represented as a string in JSON. - atEntity.expiresOn = authenticationResult.expiresOn - .getMilliseconds() - .toString(); - atEntity.extendedExpiresOn = authenticationResult.extExpiresOn - .getMilliseconds() - .toString(); + atEntity.expiresOn = expiresOn.toString(); + atEntity.extendedExpiresOn = extExpiresOn.toString(); atEntity.environment = environment; atEntity.clientId = clientId; - atEntity.realm = authenticationResult.tenantId; - atEntity.target = authenticationResult.scopes.join(" "); + atEntity.realm = tenantId; + atEntity.target = scopes; return atEntity; } diff --git a/lib/msal-common/src/unifiedCache/entities/CacheRecord.ts b/lib/msal-common/src/unifiedCache/entities/CacheRecord.ts new file mode 100644 index 0000000000..acf5493a59 --- /dev/null +++ b/lib/msal-common/src/unifiedCache/entities/CacheRecord.ts @@ -0,0 +1,16 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +import { AccountEntity } from "./AccountEntity"; +import { IdTokenEntity } from "./IdTokenEntity"; +import { AccessTokenEntity } from "./AccessTokenEntity"; +import { RefreshTokenEntity } from "./RefreshTokenEntity"; + +export class CacheRecord { + account: AccountEntity; + idToken: IdTokenEntity; + accessToken: AccessTokenEntity; + refreshToken: RefreshTokenEntity; +} diff --git a/lib/msal-common/src/unifiedCache/entities/IdTokenEntity.ts b/lib/msal-common/src/unifiedCache/entities/IdTokenEntity.ts index e1addac5cd..d7765e77be 100644 --- a/lib/msal-common/src/unifiedCache/entities/IdTokenEntity.ts +++ b/lib/msal-common/src/unifiedCache/entities/IdTokenEntity.ts @@ -5,7 +5,6 @@ import { Credential } from "./Credential"; import { CredentialType } from "../../utils/Constants"; -import { AuthenticationResult } from "../../response/AuthenticationResult"; /** * ID_TOKEN Cache @@ -22,9 +21,10 @@ export class IdTokenEntity extends Credential { */ static createIdTokenEntity( homeAccountId: string, - authenticationResult: AuthenticationResult, + environment: string, + idToken: string, clientId: string, - environment: string + tenantId: string ): IdTokenEntity { const idTokenEntity = new IdTokenEntity(); @@ -32,8 +32,8 @@ export class IdTokenEntity extends Credential { idTokenEntity.homeAccountId = homeAccountId; idTokenEntity.environment = environment; idTokenEntity.clientId = clientId; - idTokenEntity.secret = authenticationResult.idToken; - idTokenEntity.realm = authenticationResult.tenantId; + idTokenEntity.secret = idToken; + idTokenEntity.realm = tenantId; return idTokenEntity; } diff --git a/lib/msal-common/src/unifiedCache/entities/RefreshTokenEntity.ts b/lib/msal-common/src/unifiedCache/entities/RefreshTokenEntity.ts index 2397e59473..ac7ae2e08c 100644 --- a/lib/msal-common/src/unifiedCache/entities/RefreshTokenEntity.ts +++ b/lib/msal-common/src/unifiedCache/entities/RefreshTokenEntity.ts @@ -5,7 +5,6 @@ import { Credential } from "./Credential"; import { CredentialType } from "../../utils/Constants"; -import { AuthenticationResult } from "../../response/AuthenticationResult"; /** * REFRESH_TOKEN Cache @@ -22,10 +21,10 @@ export class RefreshTokenEntity extends Credential { */ static createRefreshTokenEntity( homeAccountId: string, - authenticationResult: AuthenticationResult, + environment: string, refreshToken: string, clientId: string, - environment: string + familyId?: string ): RefreshTokenEntity { const rtEntity = new RefreshTokenEntity(); @@ -35,8 +34,8 @@ export class RefreshTokenEntity extends Credential { rtEntity.homeAccountId = homeAccountId; rtEntity.secret = refreshToken; - if (authenticationResult.familyId) - rtEntity.familyId = authenticationResult.familyId; + if (familyId) + rtEntity.familyId = familyId; return rtEntity; } From 9649a916595cb6dc2be1b9208192a50dd7228b09 Mon Sep 17 00:00:00 2001 From: sameerag Date: Wed, 20 May 2020 01:01:21 -0700 Subject: [PATCH 13/17] fixing linter issues --- .../src/response/AuthenticationResult.ts | 13 +++++-------- lib/msal-common/src/response/ResponseHandler.ts | 15 ++++++--------- .../src/unifiedCache/UnifiedCacheManager.ts | 10 ++-------- .../src/unifiedCache/utils/CacheTypes.ts | 2 +- 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/lib/msal-common/src/response/AuthenticationResult.ts b/lib/msal-common/src/response/AuthenticationResult.ts index bb5e4acc01..8fcde3ca08 100644 --- a/lib/msal-common/src/response/AuthenticationResult.ts +++ b/lib/msal-common/src/response/AuthenticationResult.ts @@ -8,18 +8,15 @@ import { StringDict } from "../utils/MsalTypes"; /** * Result returned from the authority's token endpoint. */ -// TODO: Also consider making an external type and use this as internal export class AuthenticationResult { - // TODO this is temp class, it will be updated. - uniqueId: string; // TODO: Check applicability - tenantId: string; // TODO: Check applicability + uniqueId: string; + tenantId: string; scopes: Array; - tokenType: string; // TODO: get rid of this if we can idToken: string; idTokenClaims: StringDict; accessToken: string; expiresOn: Date; - extExpiresOn?: Date; // TODO: Check what this maps to in other libraries - userRequestState?: string; // TODO: remove, just check how state is handled in other libraries - familyId?: string; // TODO: Check wider audience + extExpiresOn?: Date; + state?: string; + familyId?: string; } diff --git a/lib/msal-common/src/response/ResponseHandler.ts b/lib/msal-common/src/response/ResponseHandler.ts index 7004c76241..1cebdfe34c 100644 --- a/lib/msal-common/src/response/ResponseHandler.ts +++ b/lib/msal-common/src/response/ResponseHandler.ts @@ -22,8 +22,7 @@ import { IdTokenEntity } from "../unifiedCache/entities/IdTokenEntity"; import { AccessTokenEntity } from "../unifiedCache/entities/AccessTokenEntity"; import { RefreshTokenEntity } from "../unifiedCache/entities/RefreshTokenEntity"; import { InteractionRequiredAuthError } from "../error/InteractionRequiredAuthError"; -import { Separators } from '../utils/Constants'; -import { CacheRecord } from '../unifiedCache/entities/CacheRecord'; +import { CacheRecord } from "../unifiedCache/entities/CacheRecord"; /** * Class that handles response parsing. @@ -114,17 +113,15 @@ export class ResponseHandler { const cacheRecord = this.generateCacheRecord(serverTokenResponse, idTokenObj, authority); this.uCacheManager.saveCacheRecord(cacheRecord); - // Expiration calculation const expiresInSeconds = TimeUtils.nowSeconds() + serverTokenResponse.expires_in; const extendedExpiresInSeconds = expiresInSeconds + serverTokenResponse.ext_expires_in; const responseScopes = ScopeSet.fromString(serverTokenResponse.scope, this.clientId, true); - let authenticationResult = { + const authenticationResult: AuthenticationResult = { uniqueId: idTokenObj.claims.oid || idTokenObj.claims.sub, tenantId: idTokenObj.claims.tid, scopes: responseScopes.asArray(), - tokenType: "Bearer", idToken: idTokenObj.rawIdToken, idTokenClaims: idTokenObj.claims, accessToken: serverTokenResponse.access_token, @@ -167,13 +164,13 @@ export class ResponseHandler { */ generateCacheRecord(serverTokenResponse: ServerAuthorizationTokenResponse, idTokenObj: IdToken, authority: Authority): CacheRecord { - let cacheRecord = new CacheRecord(); + const cacheRecord = new CacheRecord(); // Account cacheRecord.account = this.generateAccountEntity( - serverTokenResponse, - idTokenObj, - authority + serverTokenResponse, + idTokenObj, + authority ); // IdToken diff --git a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts index 608455d36c..ab055081be 100644 --- a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts +++ b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts @@ -26,7 +26,7 @@ import { } from "./utils/CacheTypes"; import { ICacheManager } from "./interface/ICacheManager"; import { CacheHelper } from "./utils/CacheHelper"; -import { CacheRecord } from './entities/CacheRecord'; +import { CacheRecord } from "./entities/CacheRecord"; export class UnifiedCacheManager implements ICacheManager { // Storage interface @@ -168,7 +168,7 @@ export class UnifiedCacheManager implements ICacheManager { * @param realm */ getAccountsFilteredBy( - accountFilter: AccountFilter + accountFilter: AccountFilter ): AccountCache { return this.getAccountsFilteredByInternal( accountFilter.homeAccountId, @@ -227,12 +227,6 @@ export class UnifiedCacheManager implements ICacheManager { getCredentialsFilteredBy( filter: CredentialFilter ): CredentialCache { - const matchingCredentials: CredentialCache = { - idTokens: {}, - accessTokens: {}, - refreshTokens: {}, - }; - return this.getCredentialsFilteredByInternal( filter.homeAccountId, filter.environment, diff --git a/lib/msal-common/src/unifiedCache/utils/CacheTypes.ts b/lib/msal-common/src/unifiedCache/utils/CacheTypes.ts index 7773a86367..8786e6d758 100644 --- a/lib/msal-common/src/unifiedCache/utils/CacheTypes.ts +++ b/lib/msal-common/src/unifiedCache/utils/CacheTypes.ts @@ -46,7 +46,7 @@ export type AccountFilter = { homeAccountId?: string; environment?: string; realm?: string; -} +}; /** * Credential: ----- From e19fdad176ef5bc1959b79ecd609c56cf698bd24 Mon Sep 17 00:00:00 2001 From: sameerag Date: Wed, 20 May 2020 01:05:09 -0700 Subject: [PATCH 14/17] fix linter issues --- lib/msal-browser/src/cache/BrowserStorage.ts | 2 +- lib/msal-node/src/cache/CacheContext.ts | 31 +++++++++++++------ lib/msal-node/src/cache/CacheManager.ts | 1 - lib/msal-node/src/cache/Storage.ts | 9 ++---- lib/msal-node/src/client/ClientApplication.ts | 19 +++++++----- lib/msal-node/src/config/Configuration.ts | 10 ++++-- lib/msal-node/src/utils/Constants.ts | 2 +- .../client/PublicClientApplication.spec.ts | 17 +++------- .../test/config/ClientConfiguration.spec.ts | 14 +++++---- lib/msal-node/test/utils/TestConstants.ts | 18 ++++++----- 10 files changed, 67 insertions(+), 56 deletions(-) diff --git a/lib/msal-browser/src/cache/BrowserStorage.ts b/lib/msal-browser/src/cache/BrowserStorage.ts index c4be0b6a9a..cc59f6796f 100644 --- a/lib/msal-browser/src/cache/BrowserStorage.ts +++ b/lib/msal-browser/src/cache/BrowserStorage.ts @@ -268,7 +268,7 @@ export class BrowserStorage implements ICacheStorage { /** * Dummy implementation until browser cache is migrated */ - setCache() { + setCache(): void { // sets nothing } } diff --git a/lib/msal-node/src/cache/CacheContext.ts b/lib/msal-node/src/cache/CacheContext.ts index dd7464962e..cd872368e4 100644 --- a/lib/msal-node/src/cache/CacheContext.ts +++ b/lib/msal-node/src/cache/CacheContext.ts @@ -7,7 +7,7 @@ import { JsonCache, Deserializer, Serializer, - StringUtils + StringUtils, } from '@azure/msal-common'; import { CacheManager } from '../cache/CacheManager'; import { Storage } from '../cache/Storage'; @@ -16,18 +16,17 @@ import { Storage } from '../cache/Storage'; * class managing sync between the persistent cache blob in the disk and the in memory cache of the node */ export class CacheContext { - private cachePath: string; private defaultSerializedCache: JsonCache = { Account: {}, IdToken: {}, AccessToken: {}, RefreshToken: {}, - AppMetadata: {} + AppMetadata: {}, }; constructor() { - this.cachePath = ""; + this.cachePath = ''; } /** @@ -54,8 +53,12 @@ export class CacheContext { */ async syncCache(storage: Storage): Promise { if (!StringUtils.isEmpty(this.cachePath)) { - const currentCache = Serializer.serializeAllCache(storage.getCache()); - const tCache = Deserializer.deserializeJSONBlob(await CacheManager.readFromFile(this.cachePath)); + const currentCache = Serializer.serializeAllCache( + storage.getCache() + ); + const tCache = Deserializer.deserializeJSONBlob( + await CacheManager.readFromFile(this.cachePath) + ); return this.mergeCache(tCache, currentCache); } @@ -71,10 +74,18 @@ export class CacheContext { return { Account: { ...persistentCache.Account, ...currentCache.Account }, IdToken: { ...persistentCache.IdToken, ...currentCache.IdToken }, - AccessToken: { ...persistentCache.AccessToken, ...currentCache.AccessToken }, - RefreshToken: { ...persistentCache.RefreshToken, ...currentCache.RefreshToken }, - AppMetadata: { ...persistentCache.AppMetadata, ...currentCache.AppMetadata} + AccessToken: { + ...persistentCache.AccessToken, + ...currentCache.AccessToken, + }, + RefreshToken: { + ...persistentCache.RefreshToken, + ...currentCache.RefreshToken, + }, + AppMetadata: { + ...persistentCache.AppMetadata, + ...currentCache.AppMetadata, + }, }; } - } diff --git a/lib/msal-node/src/cache/CacheManager.ts b/lib/msal-node/src/cache/CacheManager.ts index 53d56ed778..d3098f95de 100644 --- a/lib/msal-node/src/cache/CacheManager.ts +++ b/lib/msal-node/src/cache/CacheManager.ts @@ -6,7 +6,6 @@ import { promises as fs } from 'fs'; export class CacheManager { - /** * Read contents of the cache blob to in memoryCache * @param cachePath diff --git a/lib/msal-node/src/cache/Storage.ts b/lib/msal-node/src/cache/Storage.ts index 830e522e94..a7de13d6a8 100644 --- a/lib/msal-node/src/cache/Storage.ts +++ b/lib/msal-node/src/cache/Storage.ts @@ -2,10 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ -import { - ICacheStorage, - InMemoryCache -} from '@azure/msal-common'; +import { ICacheStorage, InMemoryCache } from '@azure/msal-common'; import { CacheOptions } from '../config/Configuration'; /** @@ -13,12 +10,12 @@ import { CacheOptions } from '../config/Configuration'; */ export class Storage implements ICacheStorage { // Cache configuration, either set by user or default values. - private cacheConfig: CacheOptions;; + private cacheConfig: CacheOptions; private inMemoryCache: InMemoryCache; constructor(cacheConfig: CacheOptions) { this.cacheConfig = cacheConfig; - if (this.cacheConfig.cacheLocation! === "fileCache") + if (this.cacheConfig.cacheLocation! === 'fileCache') this.inMemoryCache = this.cacheConfig.cacheInMemory!; } diff --git a/lib/msal-node/src/client/ClientApplication.ts b/lib/msal-node/src/client/ClientApplication.ts index 1b31a81feb..8c7da73b13 100644 --- a/lib/msal-node/src/client/ClientApplication.ts +++ b/lib/msal-node/src/client/ClientApplication.ts @@ -19,10 +19,9 @@ import { Storage } from '../cache/Storage'; import { version } from '../../package.json'; import { Constants } from './../utils/Constants'; import { CacheContext } from '../cache/CacheContext'; -import { CacheManager } from '../cache/CacheManager' +import { CacheManager } from '../cache/CacheManager'; export abstract class ClientApplication { - protected config: Configuration; protected cacheContext: CacheContext; protected storage: Storage; @@ -99,8 +98,12 @@ export abstract class ClientApplication { * handle the caching and refreshing of tokens automatically. * @param request */ - async acquireTokenByRefreshToken(request: RefreshTokenRequest): Promise{ - const refreshTokenClient = new RefreshTokenClient(this.buildOauthClientConfiguration()); + async acquireTokenByRefreshToken( + request: RefreshTokenRequest + ): Promise { + const refreshTokenClient = new RefreshTokenClient( + this.buildOauthClientConfiguration() + ); return refreshTokenClient.acquireToken(request); } @@ -120,8 +123,8 @@ export abstract class ClientApplication { libraryInfo: { sku: Constants.MSAL_SKU, version: version, - cpu: process.arch || "", - os: process.platform || "" + cpu: process.arch || '', + os: process.platform || '', }, }; } @@ -143,7 +146,9 @@ export abstract class ClientApplication { async writeCacheToDisk(cachePath: string): Promise { await this.readCacheFromDisk(cachePath); const inMemCache = this.storage.getCache(); - const cacheBlob = Serializer.serializeJSONBlob(Serializer.serializeAllCache(inMemCache)); + const cacheBlob = Serializer.serializeJSONBlob( + Serializer.serializeAllCache(inMemCache) + ); CacheManager.writeToFile(cachePath, cacheBlob); } } diff --git a/lib/msal-node/src/config/Configuration.ts b/lib/msal-node/src/config/Configuration.ts index 4892deb60d..3f2b6125c8 100644 --- a/lib/msal-node/src/config/Configuration.ts +++ b/lib/msal-node/src/config/Configuration.ts @@ -11,7 +11,7 @@ import { } from '@azure/msal-common'; import { NetworkUtils } from '../utils/NetworkUtils'; import { CACHE } from '../utils/Constants'; -import debug from "debug"; +import debug from 'debug'; export type NodeAuthOptions = AuthOptions; @@ -72,8 +72,12 @@ const DEFAULT_CACHE_OPTIONS: CacheOptions = { }; const DEFAULT_LOGGER_OPTIONS: LoggerOptions = { - loggerCallback: (level: LogLevel, message: string, containsPii: boolean) => { - debug(`msal:${LogLevel[level]}${containsPii ? "-Pii": ""}`)(message); + loggerCallback: ( + level: LogLevel, + message: string, + containsPii: boolean + ) => { + debug(`msal:${LogLevel[level]}${containsPii ? '-Pii' : ''}`)(message); }, piiLoggingEnabled: false, logLevel: LogLevel.Info, diff --git a/lib/msal-node/src/utils/Constants.ts b/lib/msal-node/src/utils/Constants.ts index 541a310b19..3f04c00b96 100644 --- a/lib/msal-node/src/utils/Constants.ts +++ b/lib/msal-node/src/utils/Constants.ts @@ -43,5 +43,5 @@ export const CACHE = { * Constants for headers */ export const Constants = { - MSAL_SKU: "msal.js.node", + MSAL_SKU: 'msal.js.node', }; diff --git a/lib/msal-node/test/client/PublicClientApplication.spec.ts b/lib/msal-node/test/client/PublicClientApplication.spec.ts index 1cd2f3d022..db510406f6 100644 --- a/lib/msal-node/test/client/PublicClientApplication.spec.ts +++ b/lib/msal-node/test/client/PublicClientApplication.spec.ts @@ -1,6 +1,6 @@ import { PublicClientApplication } from './../../src/client/PublicClientApplication'; import { AuthorizationCodeRequest, Configuration } from './../../src/index'; -import { TEST_CONSTANTS } from "../utils/TestConstants"; +import { TEST_CONSTANTS } from '../utils/TestConstants'; import { AuthorizationCodeClient, AuthorizationCodeUrlRequest, @@ -10,19 +10,16 @@ import { RefreshTokenRequest, // ClientConfiguration, // ClientConfigurationError, -} -from "@azure/msal-common"; +} from '@azure/msal-common'; -jest.mock("@azure/msal-common"); +jest.mock('@azure/msal-common'); describe('PublicClientApplication', () => { - let appConfig: Configuration = { auth: { clientId: TEST_CONSTANTS.CLIENT_ID, authority: TEST_CONSTANTS.AUTHORITY, }, - }; // const expectedOauthClientConfig: ClientConfiguration = { @@ -35,13 +32,11 @@ describe('PublicClientApplication', () => { }); test('exports a class', () => { - const authApp = new PublicClientApplication(appConfig); expect(authApp).toBeInstanceOf(PublicClientApplication); }); test('acquireTokenByDeviceCode', async () => { - const request: DeviceCodeRequest = { deviceCodeCallback: response => { console.log(response); @@ -56,7 +51,6 @@ describe('PublicClientApplication', () => { }); test('acquireTokenByAuthorizationCode', async () => { - const request: AuthorizationCodeRequest = { scopes: TEST_CONSTANTS.DEFAULT_GRAPH_SCOPE, redirectUri: TEST_CONSTANTS.REDIRECT_URI, @@ -67,14 +61,12 @@ describe('PublicClientApplication', () => { await authApp.acquireTokenByCode(request); expect(AuthorizationCodeClient).toHaveBeenCalledTimes(1); // expect(AuthorizationCodeClient).toHaveBeenCalledWith(expect.objectContaining(expectedOauthClientConfig)); - }); test('acquireTokenByRefreshToken', async () => { - const request: RefreshTokenRequest = { scopes: TEST_CONSTANTS.DEFAULT_GRAPH_SCOPE, - refreshToken: TEST_CONSTANTS.REFRESH_TOKEN + refreshToken: TEST_CONSTANTS.REFRESH_TOKEN, }; const authApp = new PublicClientApplication(appConfig); @@ -84,7 +76,6 @@ describe('PublicClientApplication', () => { }); test('create AuthorizationCode URL', async () => { - const request: AuthorizationCodeUrlRequest = { scopes: TEST_CONSTANTS.DEFAULT_GRAPH_SCOPE, redirectUri: TEST_CONSTANTS.REDIRECT_URI, diff --git a/lib/msal-node/test/config/ClientConfiguration.spec.ts b/lib/msal-node/test/config/ClientConfiguration.spec.ts index a89f72ffd8..6ebff95504 100644 --- a/lib/msal-node/test/config/ClientConfiguration.spec.ts +++ b/lib/msal-node/test/config/ClientConfiguration.spec.ts @@ -1,12 +1,14 @@ -import {buildAppConfiguration, Configuration} from "../../src/config/Configuration"; -import {CACHE} from "../../src/utils/Constants"; -import {HttpClient} from "../../src/network/HttpClient"; -import {TEST_CONSTANTS} from "../utils/TestConstants"; -import {LogLevel, NetworkRequestOptions} from "@azure/msal-common"; +import { + buildAppConfiguration, + Configuration, +} from '../../src/config/Configuration'; +import { CACHE } from '../../src/utils/Constants'; +import { HttpClient } from '../../src/network/HttpClient'; +import { TEST_CONSTANTS } from '../utils/TestConstants'; +import { LogLevel, NetworkRequestOptions } from '@azure/msal-common'; describe('ClientConfiguration tests', () => { test('builds configuration and assigns default functions', () => { - const config: Configuration = buildAppConfiguration({}); // network options diff --git a/lib/msal-node/test/utils/TestConstants.ts b/lib/msal-node/test/utils/TestConstants.ts index 83f86afaa9..6867b469a2 100644 --- a/lib/msal-node/test/utils/TestConstants.ts +++ b/lib/msal-node/test/utils/TestConstants.ts @@ -3,14 +3,16 @@ */ export const TEST_CONSTANTS = { - CLIENT_ID: "b41a6fbb-c728-4e03-aa59-d25b0fd383b6", - AUTHORITY: "https://login.microsoftonline.com/TenantId", - REDIRECT_URI: "http://localhost:8080", - DEFAULT_GRAPH_SCOPE: ["user.read"], - AUTHORIZATION_CODE: "0.ASgAqPq4kJXMDkamGO53C-4XWVm3ypmrKgtCkdhePY1PBjsoAJg.AQABAAIAAAAm-06blBE1TpVMil8KPQ41DOje1jDj1oK3KxTXGKg89VjLYJi71gx_npOoxVfC7X49MqOX7IltTJOilUId-IAHndHXlfWzoSGq3GUmwAOLMisftceBRtq3YBsvHX7giiuSZXJgpgu03uf3V2h5Z3GJNpnSXT1f7iVFuRvGh1-jqjWxKs2un8AS5rhti1ym1zxkeicKT43va5jQeHVUlTQo69llnwQJ3iKmKLDVq_Q25Au4EQjYaeEx6TP5IZSqPPm7x0bynmjE8cqR5r4ySP4wH8fjnxlLySrUEZObk2VgREB1AdH6-xKIa04EnJEj9dUgTwiFvQumkuHHetFOgH7ep_9diFOdAOQLUK8C9N4Prlj0JiOcgn6l0xYd5Q9691Ylw8UfifLwq_B7f30mMLN64_XgoBY9K9CR1L4EC1kPPwIhVv3m6xmbhXZ3efx-A-bbV2SYcO4D4ZlnQztHzie_GUlredtsdEMAOE3-jaMJs7i2yYMuIEEtRcHIjV_WscVooCDdKmVncHOObWhNUSdULAejBr3pFs0v3QO_xZ269eLu5Z0qHzCZ_EPg2aL-ERz-rpgdclQ_H_KnEtMsC4F1RgAnDjVmSRKJZZdnNLfKSX_Wd40t_nuo4kjN2cSt8QzzeL533zIZ4CxthOsC4HH2RcUZDIgHdLDLT2ukg-Osc6J9URpZP-IUpdjXg_uwbkHEjrXDMBMo2pmCqaWbMJKo5Lr7CrystifnDITXzZmmOah8HV83Xyb6EP8Gno6JRuaG80j8BKDWyb1Yof4rnLI1kZ59n_t2d0LnRBXz50PdWCWX6vtkg-kAV-bGJQr45XDSKBSv0Q_fVsdLMk24NacUZcF5ujUtqv__Bv-wATzCHWlbUDGHC8nHEi84PcYAjSsgAA", - REFRESH_TOKEN: "thisIsARefreshT0ken", - AUTH_CODE_URL: "https://login.microsoftonline.com/TenantId/oauth2.0/v2.0/authorize?client_id=b41a6fbb-c728-4e03-aa59-d25b0fd383b6&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%2F8080%2F&response_mode=query&scope=user.read%20openid%20profile%20offline_access", - CACHE_LOCATION: "Test", + CLIENT_ID: 'b41a6fbb-c728-4e03-aa59-d25b0fd383b6', + AUTHORITY: 'https://login.microsoftonline.com/TenantId', + REDIRECT_URI: 'http://localhost:8080', + DEFAULT_GRAPH_SCOPE: ['user.read'], + AUTHORIZATION_CODE: + '0.ASgAqPq4kJXMDkamGO53C-4XWVm3ypmrKgtCkdhePY1PBjsoAJg.AQABAAIAAAAm-06blBE1TpVMil8KPQ41DOje1jDj1oK3KxTXGKg89VjLYJi71gx_npOoxVfC7X49MqOX7IltTJOilUId-IAHndHXlfWzoSGq3GUmwAOLMisftceBRtq3YBsvHX7giiuSZXJgpgu03uf3V2h5Z3GJNpnSXT1f7iVFuRvGh1-jqjWxKs2un8AS5rhti1ym1zxkeicKT43va5jQeHVUlTQo69llnwQJ3iKmKLDVq_Q25Au4EQjYaeEx6TP5IZSqPPm7x0bynmjE8cqR5r4ySP4wH8fjnxlLySrUEZObk2VgREB1AdH6-xKIa04EnJEj9dUgTwiFvQumkuHHetFOgH7ep_9diFOdAOQLUK8C9N4Prlj0JiOcgn6l0xYd5Q9691Ylw8UfifLwq_B7f30mMLN64_XgoBY9K9CR1L4EC1kPPwIhVv3m6xmbhXZ3efx-A-bbV2SYcO4D4ZlnQztHzie_GUlredtsdEMAOE3-jaMJs7i2yYMuIEEtRcHIjV_WscVooCDdKmVncHOObWhNUSdULAejBr3pFs0v3QO_xZ269eLu5Z0qHzCZ_EPg2aL-ERz-rpgdclQ_H_KnEtMsC4F1RgAnDjVmSRKJZZdnNLfKSX_Wd40t_nuo4kjN2cSt8QzzeL533zIZ4CxthOsC4HH2RcUZDIgHdLDLT2ukg-Osc6J9URpZP-IUpdjXg_uwbkHEjrXDMBMo2pmCqaWbMJKo5Lr7CrystifnDITXzZmmOah8HV83Xyb6EP8Gno6JRuaG80j8BKDWyb1Yof4rnLI1kZ59n_t2d0LnRBXz50PdWCWX6vtkg-kAV-bGJQr45XDSKBSv0Q_fVsdLMk24NacUZcF5ujUtqv__Bv-wATzCHWlbUDGHC8nHEi84PcYAjSsgAA', + REFRESH_TOKEN: 'thisIsARefreshT0ken', + AUTH_CODE_URL: + 'https://login.microsoftonline.com/TenantId/oauth2.0/v2.0/authorize?client_id=b41a6fbb-c728-4e03-aa59-d25b0fd383b6&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%2F8080%2F&response_mode=query&scope=user.read%20openid%20profile%20offline_access', + CACHE_LOCATION: 'Test', }; export const AUTHENTICATION_RESULT = { From 036ac45c965d8a99125103736185ae5bc932bf75 Mon Sep 17 00:00:00 2001 From: sameerag Date: Tue, 2 Jun 2020 23:07:27 -0700 Subject: [PATCH 15/17] fixing linter errors --- lib/msal-browser/src/cache/BrowserStorage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msal-browser/src/cache/BrowserStorage.ts b/lib/msal-browser/src/cache/BrowserStorage.ts index c4be0b6a9a..cc59f6796f 100644 --- a/lib/msal-browser/src/cache/BrowserStorage.ts +++ b/lib/msal-browser/src/cache/BrowserStorage.ts @@ -268,7 +268,7 @@ export class BrowserStorage implements ICacheStorage { /** * Dummy implementation until browser cache is migrated */ - setCache() { + setCache(): void { // sets nothing } } From 2da03d9234f13473957c34c26078f609b71890ce Mon Sep 17 00:00:00 2001 From: sameerag Date: Wed, 3 Jun 2020 14:58:22 -0700 Subject: [PATCH 16/17] Pushing changes for cacheType and some other oprtimizations! Not final. --- lib/msal-common/src/cache/ICacheStorage.ts | 2 +- lib/msal-common/src/index.ts | 1 + .../src/unifiedCache/UnifiedCacheManager.ts | 132 +++++++++++++----- .../unifiedCache/entities/AccountEntity.ts | 22 ++- .../src/unifiedCache/entities/Credential.ts | 20 ++- .../unifiedCache/interface/ICacheManager.ts | 2 +- .../serialize/EntitySerializer.ts | 69 +++++++++ .../src/unifiedCache/serialize/Serializer.ts | 34 +---- .../src/unifiedCache/utils/CacheHelper.ts | 16 +-- lib/msal-common/src/utils/Constants.ts | 35 ++--- lib/msal-node/src/cache/Storage.ts | 28 ++-- 11 files changed, 255 insertions(+), 106 deletions(-) create mode 100644 lib/msal-common/src/unifiedCache/serialize/EntitySerializer.ts diff --git a/lib/msal-common/src/cache/ICacheStorage.ts b/lib/msal-common/src/cache/ICacheStorage.ts index 0c2f5289d0..93378f6bd3 100644 --- a/lib/msal-common/src/cache/ICacheStorage.ts +++ b/lib/msal-common/src/cache/ICacheStorage.ts @@ -39,7 +39,7 @@ export interface ICacheStorage { * Function which removes item from cache. * @param key */ - removeItem(key: string): void; + removeItem(key: string): boolean; /** * Function which returns boolean whether cache contains a specific key. diff --git a/lib/msal-common/src/index.ts b/lib/msal-common/src/index.ts index b2579d9d1b..7fa16e179a 100644 --- a/lib/msal-common/src/index.ts +++ b/lib/msal-common/src/index.ts @@ -20,6 +20,7 @@ export { UnifiedCacheManager } from "./unifiedCache/UnifiedCacheManager"; export { JsonCache, InMemoryCache } from "./unifiedCache/utils/CacheTypes"; export { Serializer } from "./unifiedCache/serialize/Serializer"; export { Deserializer } from "./unifiedCache/serialize/Deserializer"; +export { CacheHelper } from "./unifiedCache/utils/CacheHelper"; // Network Interface export { INetworkModule, NetworkRequestOptions } from "./network/INetworkModule"; export { NetworkResponse } from "./network/NetworkManager"; diff --git a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts index f2fee14b4c..d14b4f7cd5 100644 --- a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts +++ b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts @@ -15,7 +15,7 @@ import { Credential } from "./entities/Credential"; import { CredentialType, Separators, - CacheKeyPosition, + CredentialKeyPosition, } from "../utils/Constants"; import { AccountCache, @@ -71,7 +71,7 @@ export class UnifiedCacheManager implements ICacheManager { } /** - * append credential cache to in memory cach + * append credential cache to in memory cache * @param accessToken * @param idToken * @param refreshToken @@ -127,21 +127,16 @@ export class UnifiedCacheManager implements ICacheManager { */ saveCredential(credential: Credential): void { const cache = this.getCacheInMemory(); - const key = credential.generateCredentialKey(); switch (credential.credentialType) { case CredentialType.ID_TOKEN: - cache.idTokens[key] = credential as IdTokenEntity; + this.saveIdToken(credential); break; case CredentialType.ACCESS_TOKEN: - cache.accessTokens[ - key - ] = credential as AccessTokenEntity; + this.saveAccessToken(credential); break; case CredentialType.REFRESH_TOKEN: - cache.refreshTokens[ - key - ] = credential as RefreshTokenEntity; + this.saveRefreshToken(credential); break; default: console.log("Cache entity type mismatch"); @@ -150,6 +145,49 @@ export class UnifiedCacheManager implements ICacheManager { this.setCacheInMemory(cache); } + /** + * save IdTokenEntity to Cache + * @param idToken + */ + saveIdToken(idToken: Credential) { + const cache = this.getCacheInMemory(); + const key = idToken.generateCredentialKey(); + if (cache.idTokens[key]) { + this.removeIdToken(key); + } + + cache.idTokens[key] = idToken as IdTokenEntity; + } + + /** + * save AccessTokenEntity to Cache + * @param accessToken + */ + saveAccessToken(accessToken: Credential) { + const cache = this.getCacheInMemory(); + const key = accessToken.generateCredentialKey(); + if (cache.accessTokens[key]) { + this.removeAccessToken(key); + } + + cache.accessTokens[key] = accessToken as AccessTokenEntity; + + } + + /** + * save RefreshTokenEntity to Cache + * @param refreshToken + */ + saveRefreshToken(refreshToken: Credential) { + const cache = this.getCacheInMemory(); + const key = refreshToken.generateCredentialKey(); + if (cache.refreshTokens[key]) { + this.removeRefreshToken(key); + } + + cache.refreshTokens[key] = refreshToken as RefreshTokenEntity; + } + /** * Given account key retrieve an account * @param key @@ -166,7 +204,7 @@ export class UnifiedCacheManager implements ICacheManager { const cache = this.getCacheInMemory(); switch ( key.split(Separators.CACHE_KEY_SEPARATOR)[ - CacheKeyPosition.CREDENTIAL_TYPE + CredentialKeyPosition.CREDENTIAL_TYPE ] ) { case "idtoken": @@ -395,25 +433,21 @@ export class UnifiedCacheManager implements ICacheManager { */ removeAccountContext(account: AccountEntity): boolean { const cache = this.getCacheInMemory(); - const accountId = account.generateAccountId(); - // TODO: Check how this should be done, do we just remove the account or also the associated credentials always? - Object.keys(cache.accessTokens).forEach((key) => { - if ( - cache.accessTokens[key].generateAccountId() === accountId - ) - this.removeCredential(cache.accessTokens[key]); - }); - Object.keys(cache.idTokens).forEach((key) => { if (cache.idTokens[key].generateAccountId() === accountId) - this.removeCredential(cache.idTokens[key]); + this.removeIdToken(key); }); - Object.keys(cache.idTokens).forEach((key) => { - if (cache.idTokens[key].generateAccountId() === accountId) - this.removeCredential(cache.idTokens[key]); + Object.keys(cache.accessTokens).forEach((key) => { + if (cache.accessTokens[key].generateAccountId() === accountId) + this.removeAccessToken(key); + }); + + Object.keys(cache.refreshTokens).forEach((key) => { + if (cache.refreshTokens[key].generateAccountId() === accountId) + this.removeRefreshToken(key); }); this.removeAccount(account); @@ -425,27 +459,51 @@ export class UnifiedCacheManager implements ICacheManager { * @param credential */ removeCredential(credential: Credential): boolean { - const cache = this.getCacheInMemory(); + const key = credential.generateCredentialKey(); switch (credential.credentialType) { case CredentialType.ID_TOKEN: - delete cache.idTokens[ - credential.generateCredentialKey() - ]; - return true; + return this.removeIdToken(key); case CredentialType.ACCESS_TOKEN: - delete cache.accessTokens[ - credential.generateCredentialKey() - ]; - return true; + return this.removeAccessToken(key); case CredentialType.REFRESH_TOKEN: - delete cache.refreshTokens[ - credential.generateCredentialKey() - ]; - return true; + return this.removeRefreshToken(key); default: console.log("Cache entity type mismatch"); return false; } } + + /** + * Remove an IdToken from the memory + * @param key + */ + removeIdToken(key: string): boolean { + const cache = this.getCacheInMemory(); + delete cache.idTokens[key]; + this.setCacheInMemory(cache); + return true; + } + + /** + * Remove an AccessToken from the memory + * @param key + */ + removeAccessToken(key: string): boolean { + const cache = this.getCacheInMemory(); + delete cache.accessTokens[key]; + this.setCacheInMemory(cache); + return true; + } + + /** + * Remove a RefreshToken from the memory + * @param key + */ + removeRefreshToken(key: string): boolean { + const cache = this.getCacheInMemory(); + delete cache.refreshTokens[key]; + this.setCacheInMemory(cache); + return true; + } } diff --git a/lib/msal-common/src/unifiedCache/entities/AccountEntity.ts b/lib/msal-common/src/unifiedCache/entities/AccountEntity.ts index c2682fdf69..ba5d49c49a 100644 --- a/lib/msal-common/src/unifiedCache/entities/AccountEntity.ts +++ b/lib/msal-common/src/unifiedCache/entities/AccountEntity.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -import { Separators, CacheAccountType } from "../../utils/Constants"; +import { Separators, CacheAccountType, CacheType } from "../../utils/Constants"; import { Authority } from "../../authority/Authority"; import { IdToken } from "../../account/IdToken"; import { ICrypto } from "../../crypto/ICrypto"; @@ -52,6 +52,26 @@ export class AccountEntity { return accountKey.join(Separators.CACHE_KEY_SEPARATOR).toLowerCase(); } + /** + * returns the type of the cache (in this case account) + */ + generateType(): number { + switch (this.authorityType) { + case CacheAccountType.ADFS_ACCOUNT_TYPE: + return CacheType.ADFS; + case CacheAccountType.MSAV1_ACCOUNT_TYPE: + return CacheType.MSA; + case CacheAccountType.MSSTS_ACCOUNT_TYPE: + return CacheType.MSSTS; + case CacheAccountType.GENERIC_ACCOUNT_TYPE: + return CacheType.GENERIC; + default: { + console.log("Unexpected account type"); + return null; + } + } + } + /** * Build Account cache from IdToken, clientInfo and authority/policy * @param clientInfo diff --git a/lib/msal-common/src/unifiedCache/entities/Credential.ts b/lib/msal-common/src/unifiedCache/entities/Credential.ts index e384585713..4e6592b7b9 100644 --- a/lib/msal-common/src/unifiedCache/entities/Credential.ts +++ b/lib/msal-common/src/unifiedCache/entities/Credential.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -import { Separators, CredentialType } from "../../utils/Constants"; +import { Separators, CredentialType, CacheType } from "../../utils/Constants"; /** * Base type for credentials to be stored in the cache: eg: ACCESS_TOKEN, ID_TOKEN etc @@ -61,4 +61,22 @@ export class Credential { return credentialKey.join(Separators.CACHE_KEY_SEPARATOR).toLowerCase(); } + + /** + * returns the type of the cache (in this case credential) + */ + generateType(): number { + switch (this.credentialType) { + case CredentialType.ID_TOKEN: + return CacheType.ID_TOKEN; + case CredentialType.ACCESS_TOKEN: + return CacheType.ACCESS_TOKEN; + case CredentialType.REFRESH_TOKEN: + return CacheType.REFRESH_TOKEN; + default: { + console.log("Unexpected credential type"); + return null; + } + } + } } diff --git a/lib/msal-common/src/unifiedCache/interface/ICacheManager.ts b/lib/msal-common/src/unifiedCache/interface/ICacheManager.ts index 69b29f95d7..f67ecab230 100644 --- a/lib/msal-common/src/unifiedCache/interface/ICacheManager.ts +++ b/lib/msal-common/src/unifiedCache/interface/ICacheManager.ts @@ -43,7 +43,7 @@ export interface ICacheManager { ): AccountCache; /** - * retrieve credentails matching all provided filters; if no filter is set, get all credentials + * retrieve credentials matching all provided filters; if no filter is set, get all credentials * @param homeAccountId * @param environment * @param credentialType diff --git a/lib/msal-common/src/unifiedCache/serialize/EntitySerializer.ts b/lib/msal-common/src/unifiedCache/serialize/EntitySerializer.ts new file mode 100644 index 0000000000..def1ac3226 --- /dev/null +++ b/lib/msal-common/src/unifiedCache/serialize/EntitySerializer.ts @@ -0,0 +1,69 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ +import { AccountCacheMaps, AccessTokenCacheMaps, IdTokenCacheMaps, RefreshTokenCacheMaps, AppMetadataCacheMaps } from "../serialize/JsonKeys"; +import { AccountCache, AccessTokenCache, IdTokenCache, RefreshTokenCache, AppMetadataCache } from "../utils/CacheTypes"; +import { CacheHelper } from "../utils/CacheHelper"; + +export class EntitySerializer { + /** + * Convert AccountEntity to string + * @param accCache + * @param key + */ + static mapAccountKeys(accCache: AccountCache, key: string): object { + return CacheHelper.renameKeys( + accCache[key], + AccountCacheMaps.toCacheMap + ); + } + + /** + * Convert IdTokenEntity to string + * @param idTCache + * @param key + */ + static mapIdTokenKeys(idTCache: IdTokenCache, key: string): object { + return CacheHelper.renameKeys( + idTCache[key], + IdTokenCacheMaps.toCacheMap + ); + } + + /** + * Convert AccessTokenEntity to string + * @param atCache + * @param key + */ + static mapAccessTokenKeys(atCache: AccessTokenCache, key: string): object { + return CacheHelper.renameKeys( + atCache[key], + AccessTokenCacheMaps.toCacheMap + ); + } + + /** + * Convert RefreshTokenEntity to string + * @param rtCache + * @param key + */ + static mapRefreshTokenKeys(rtCache: RefreshTokenCache, key: string): object { + return CacheHelper.renameKeys( + rtCache[key], + RefreshTokenCacheMaps.toCacheMap + ); + } + + /** + * Convert AppMetaDataEntity to string + * @param amdtCache + * @param key + */ + static mapAppMetadataKeys(amdtCache: AppMetadataCache, key: string): object { + return CacheHelper.renameKeys( + amdtCache[key], + AppMetadataCacheMaps.toCacheMap + ); + } +} diff --git a/lib/msal-common/src/unifiedCache/serialize/Serializer.ts b/lib/msal-common/src/unifiedCache/serialize/Serializer.ts index a4b5215402..d9792ff565 100644 --- a/lib/msal-common/src/unifiedCache/serialize/Serializer.ts +++ b/lib/msal-common/src/unifiedCache/serialize/Serializer.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. */ -import { CacheHelper } from "../utils/CacheHelper"; -import { AccountCacheMaps, AccessTokenCacheMaps, IdTokenCacheMaps, RefreshTokenCacheMaps, AppMetadataCacheMaps } from "./JsonKeys"; +import { EntitySerializer } from "./EntitySerializer"; import { AccountCache, AccessTokenCache, IdTokenCache, RefreshTokenCache, AppMetadataCache, JsonCache, InMemoryCache } from "../utils/CacheTypes"; import { StringDict } from "../../utils/MsalTypes"; @@ -25,11 +24,7 @@ export class Serializer { static serializeAccounts(accCache: AccountCache): StringDict { const accounts = {}; Object.keys(accCache).map(function (key) { - const mappedAcc = CacheHelper.renameKeys( - accCache[key], - AccountCacheMaps.toCacheMap - ); - accounts[key] = mappedAcc; + accounts[key] = EntitySerializer.mapAccountKeys(accCache, key); }); return accounts; @@ -42,11 +37,7 @@ export class Serializer { static serializeIdTokens(idTCache: IdTokenCache): StringDict{ const idTokens = {}; Object.keys(idTCache).map(function (key) { - const mappedIdT = CacheHelper.renameKeys( - idTCache[key], - IdTokenCacheMaps.toCacheMap - ); - idTokens[key] = mappedIdT; + idTokens[key] = EntitySerializer.mapIdTokenKeys(idTCache, key); }); return idTokens; @@ -57,14 +48,9 @@ export class Serializer { * @param atCache */ static serializeAccessTokens(atCache: AccessTokenCache): StringDict { - // access tokens const accessTokens = {}; Object.keys(atCache).map(function (key) { - const mappedAT = CacheHelper.renameKeys( - atCache[key], - AccessTokenCacheMaps.toCacheMap - ); - accessTokens[key] = mappedAT; + accessTokens[key] = EntitySerializer.mapAccessTokenKeys(atCache, key); }); return accessTokens; @@ -77,11 +63,7 @@ export class Serializer { static serializeRefreshTokens(rtCache: RefreshTokenCache): StringDict{ const refreshTokens = {}; Object.keys(rtCache).map(function (key) { - const mappedRT = CacheHelper.renameKeys( - rtCache[key], - RefreshTokenCacheMaps.toCacheMap - ); - refreshTokens[key] = mappedRT; + refreshTokens[key] = EntitySerializer.mapRefreshTokenKeys(rtCache, key); }); return refreshTokens; @@ -94,11 +76,7 @@ export class Serializer { static serializeAppMetadata(amdtCache: AppMetadataCache): StringDict { const appMetadata = {}; Object.keys(amdtCache).map(function (key) { - const mappedAmdt = CacheHelper.renameKeys( - amdtCache[key], - AppMetadataCacheMaps.toCacheMap - ); - appMetadata[key] = mappedAmdt; + appMetadata[key] = EntitySerializer.mapAppMetadataKeys(amdtCache, key); }); return appMetadata; diff --git a/lib/msal-common/src/unifiedCache/utils/CacheHelper.ts b/lib/msal-common/src/unifiedCache/utils/CacheHelper.ts index 8a6c1f70fd..1eb1557998 100644 --- a/lib/msal-common/src/unifiedCache/utils/CacheHelper.ts +++ b/lib/msal-common/src/unifiedCache/utils/CacheHelper.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -import { Separators, CacheKeyPosition } from "../../utils/Constants"; +import { Separators, CredentialKeyPosition } from "../../utils/Constants"; export class CacheHelper { /** @@ -52,7 +52,7 @@ export class CacheHelper { * @param homeAccountId */ static matchHomeAccountId(key: string, homeAccountId: string): boolean { - return homeAccountId === key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.HOME_ACCOUNT_ID]; + return homeAccountId === key.split(Separators.CACHE_KEY_SEPARATOR)[CredentialKeyPosition.HOME_ACCOUNT_ID]; } /** @@ -61,7 +61,7 @@ export class CacheHelper { * @param environment */ static matchEnvironment(key: string, environment: string): boolean { - return environment === key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.ENVIRONMENT]; + return environment === key.split(Separators.CACHE_KEY_SEPARATOR)[CredentialKeyPosition.ENVIRONMENT]; } /** @@ -71,7 +71,7 @@ export class CacheHelper { * // TODO: Confirm equality for enum vs string here */ static matchCredentialType(key: string, credentialType: string): boolean { - return credentialType.toLowerCase() === key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.CREDENTIAL_TYPE].toString().toLowerCase(); + return credentialType.toLowerCase() === key.split(Separators.CACHE_KEY_SEPARATOR)[CredentialKeyPosition.CREDENTIAL_TYPE].toString().toLowerCase(); } /** @@ -80,7 +80,7 @@ export class CacheHelper { * @param clientId */ static matchClientId(key: string, clientId: string): boolean { - return clientId === key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.CLIENT_ID]; + return clientId === key.split(Separators.CACHE_KEY_SEPARATOR)[CredentialKeyPosition.CLIENT_ID]; } /** @@ -89,7 +89,7 @@ export class CacheHelper { * @param realm */ static matchRealm(key: string, realm: string): boolean { - return realm === key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.REALM]; + return realm === key.split(Separators.CACHE_KEY_SEPARATOR)[CredentialKeyPosition.REALM]; } /** @@ -98,7 +98,7 @@ export class CacheHelper { * @param target */ static matchTarget(key: string, target: string): boolean { - return CacheHelper.targetsIntersect(key.split(Separators.CACHE_KEY_SEPARATOR)[CacheKeyPosition.TARGET], target); + return CacheHelper.targetsIntersect(key.split(Separators.CACHE_KEY_SEPARATOR)[CredentialKeyPosition.TARGET], target); } /** @@ -124,7 +124,7 @@ export class CacheHelper { */ static getCredentialType(key: string): string { return key.split(Separators.CACHE_KEY_SEPARATOR)[ - CacheKeyPosition.CREDENTIAL_TYPE + CredentialKeyPosition.CREDENTIAL_TYPE ]; } } diff --git a/lib/msal-common/src/utils/Constants.ts b/lib/msal-common/src/utils/Constants.ts index 340c96fc82..981b0f1621 100644 --- a/lib/msal-common/src/utils/Constants.ts +++ b/lib/msal-common/src/utils/Constants.ts @@ -239,7 +239,7 @@ export enum CacheAccountType { MSSTS_ACCOUNT_TYPE = "MSSTS", ADFS_ACCOUNT_TYPE = "ADFS", MSAV1_ACCOUNT_TYPE = "MSA", - OTHER_ACCOUNT_TYPE = "Other" + GENERIC_ACCOUNT_TYPE = "Generic" // NTLM, Kerberos, FBA, Basic etc } /** @@ -259,36 +259,31 @@ export enum CredentialType { REFRESH_TOKEN = "RefreshToken" } -/** - * cache Type - */ -export enum CacheEntity { - ACCOUNT = "Account", - APP_META_DATA = "AppMetaData" -} - /** * Combine all cache types */ export enum CacheType { - ACCESS_TOKEN, - ID_TOKEN, - REFRESH_TOKEN, - ACCOUNT, - APP_META_DATA + ADFS = 1001, + MSA = 1002, + MSSTS = 1003, + GENERIC = 1004, + ACCESS_TOKEN = 2001, + REFRESH_TOKEN = 2002, + ID_TOKEN = 2003, + APP_META_DATA = 3001 }; /** * accountId: - * credentialId: -- */ -export enum CacheKeyPosition { +export enum CredentialKeyPosition { HOME_ACCOUNT_ID = 0, - ENVIRONMENT, - CREDENTIAL_TYPE, - CLIENT_ID, - REALM, - TARGET + ENVIRONMENT = 1, + CREDENTIAL_TYPE = 2, + CLIENT_ID = 3, + REALM = 4, + TARGET = 5 }; /** diff --git a/lib/msal-node/src/cache/Storage.ts b/lib/msal-node/src/cache/Storage.ts index a7de13d6a8..d0d3eac967 100644 --- a/lib/msal-node/src/cache/Storage.ts +++ b/lib/msal-node/src/cache/Storage.ts @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ -import { ICacheStorage, InMemoryCache } from '@azure/msal-common'; +import { ICacheStorage, InMemoryCache, CredentialType, CacheHelper} from '@azure/msal-common'; import { CacheOptions } from '../config/Configuration'; /** @@ -38,12 +38,9 @@ export class Storage implements ICacheStorage { * Sets the cache item with the key and value given. * @param key * @param value - * TODO: implement after the lookup implementation */ setItem(key: string, value: string): void { - if (key && value) { - return; - } + } /** @@ -58,12 +55,25 @@ export class Storage implements ICacheStorage { /** * Removes the cache item with the given key. - * Will also clear the cookie item if storeAuthStateInCookie is set to true. * @param key - * TODO: implement after the lookup implementation */ - removeItem(key: string): void { - if (!key) return; + removeItem(key: string): boolean { + const cache = this.getCache(); + + switch (CacheHelper.getCredentialType(key)) { + case CredentialType.ID_TOKEN: + delete cache.idTokens[key]; + return true; + case CredentialType.ACCESS_TOKEN: + delete cache.accessTokens[key]; + return true; + case CredentialType.REFRESH_TOKEN: + delete cache.refreshTokens[key]; + return true; + default: + console.log("Cache entity type mismatch"); + return false; + } } /** From 3da2e578b417cb814a038faba115d23aeb590896 Mon Sep 17 00:00:00 2001 From: sameerag Date: Thu, 4 Jun 2020 02:13:25 -0700 Subject: [PATCH 17/17] Cache save/delete/lookup moved to platform specific implementation --- lib/msal-browser/src/cache/BrowserStorage.ts | 32 ++- lib/msal-common/src/cache/ICacheStorage.ts | 24 ++- .../src/config/ClientConfiguration.ts | 12 ++ lib/msal-common/src/index.ts | 2 +- .../src/unifiedCache/UnifiedCacheManager.ts | 177 +++-------------- .../src/unifiedCache/utils/CacheHelper.ts | 77 +++++++- lib/msal-common/src/utils/Constants.ts | 15 +- .../unifiedCache/UnifiedCacheManager.spec.ts | 148 +++++++++++++- lib/msal-node/src/cache/Storage.ts | 186 ++++++++++++++++-- 9 files changed, 490 insertions(+), 183 deletions(-) diff --git a/lib/msal-browser/src/cache/BrowserStorage.ts b/lib/msal-browser/src/cache/BrowserStorage.ts index cc59f6796f..1d215c3de8 100644 --- a/lib/msal-browser/src/cache/BrowserStorage.ts +++ b/lib/msal-browser/src/cache/BrowserStorage.ts @@ -152,12 +152,13 @@ export class BrowserStorage implements ICacheStorage { * Will also clear the cookie item if storeAuthStateInCookie is set to true. * @param key */ - removeItem(key: string): void { + removeItem(key: string): boolean { const msalKey = this.generateCacheKey(key); this.windowStorage.removeItem(msalKey); if (this.cacheConfig.storeAuthStateInCookie) { this.clearItemCookie(msalKey); } + return true; } /** @@ -271,4 +272,33 @@ export class BrowserStorage implements ICacheStorage { setCache(): void { // sets nothing } + + /** + * Dummy implementation for interface compat - will change after BrowserCacheMigration + * @param key + * @param value + * @param type + */ + setItemInMemory(key: string, value: object, type?: string): void { + if (key && value && type) + return; + } + + /** + * Dummy implementation for interface compat - will change after BrowserCacheMigration + * @param key + * @param type + */ + getItemFromMemory(key: string, type?: string): object { + return key && type ? {} : {}; + }; + + /** + * Dummy implementation for interface compat - will change after BrowserCacheMigration + * @param key + * @param type + */ + removeItemFromMemory(key: string, type?: string): boolean { + return key && type ? true : false; + }; } diff --git a/lib/msal-common/src/cache/ICacheStorage.ts b/lib/msal-common/src/cache/ICacheStorage.ts index 93378f6bd3..7a9c53fbe5 100644 --- a/lib/msal-common/src/cache/ICacheStorage.ts +++ b/lib/msal-common/src/cache/ICacheStorage.ts @@ -29,6 +29,14 @@ export interface ICacheStorage { */ setItem(key: string, value: string): void; + /** + * Function to set item in Memory + * @param key + * @param value + * @param type + */ + setItemInMemory(key: string, value: object, type?: string): void; + /** * Function which retrieves item from cache. * @param key @@ -36,11 +44,25 @@ export interface ICacheStorage { getItem(key: string): string; /** - * Function which removes item from cache. + * Function to get an item from memory + * @param key + * @param type + */ + getItemFromMemory(key: string, type?: string): object; + + /** + * Function to remove an item from cache given its key. * @param key */ removeItem(key: string): boolean; + /** + * Function to remove an item from memory given its key + * @param key + * @param type + */ + removeItemFromMemory(key: string, type?: string): boolean; + /** * Function which returns boolean whether cache contains a specific key. * @param key diff --git a/lib/msal-common/src/config/ClientConfiguration.ts b/lib/msal-common/src/config/ClientConfiguration.ts index 0e94e6439d..a4c61b3b81 100644 --- a/lib/msal-common/src/config/ClientConfiguration.ts +++ b/lib/msal-common/src/config/ClientConfiguration.ts @@ -125,6 +125,10 @@ const DEFAULT_STORAGE_IMPLEMENTATION: ICacheStorage = { const notImplErr = "Storage interface - getItem() has not been implemented for the cacheStorage interface."; throw AuthError.createUnexpectedError(notImplErr); }, + getItemFromMemory: (): object => { + const notImplErr = "Storage interface - getItemFromMemory() has not been implemented for the cacheStorage interface."; + throw AuthError.createUnexpectedError(notImplErr); + }, getKeys: (): string[] => { const notImplErr = "Storage interface - getKeys() has not been implemented for the cacheStorage interface."; throw AuthError.createUnexpectedError(notImplErr); @@ -133,10 +137,18 @@ const DEFAULT_STORAGE_IMPLEMENTATION: ICacheStorage = { const notImplErr = "Storage interface - removeItem() has not been implemented for the cacheStorage interface."; throw AuthError.createUnexpectedError(notImplErr); }, + removeItemFromMemory: () => { + const notImplErr = "Storage interface - removeItemFromMemory() has not been implemented for the cacheStorage interface."; + throw AuthError.createUnexpectedError(notImplErr); + }, setItem: () => { const notImplErr = "Storage interface - setItem() has not been implemented for the cacheStorage interface."; throw AuthError.createUnexpectedError(notImplErr); }, + setItemInMemory: () => { + const notImplErr = "Storage interface - setItemInMemory() has not been implemented for the cacheStorage interface."; + throw AuthError.createUnexpectedError(notImplErr); + }, getCache: (): InMemoryCache => { const notImplErr = "Storage interface - getCache() has not been implemented for the cacheStorage interface."; throw AuthError.createUnexpectedError(notImplErr); diff --git a/lib/msal-common/src/index.ts b/lib/msal-common/src/index.ts index 7fa16e179a..1b0cd40770 100644 --- a/lib/msal-common/src/index.ts +++ b/lib/msal-common/src/index.ts @@ -50,7 +50,7 @@ export { ClientAuthError, ClientAuthErrorMessage } from "./error/ClientAuthError export { ClientConfigurationError, ClientConfigurationErrorMessage } from "./error/ClientConfigurationError"; // Constants and Utils export { - Constants, PromptValue, TemporaryCacheKeys, PersistentCacheKeys, Prompt, ResponseMode + Constants, PromptValue, TemporaryCacheKeys, PersistentCacheKeys, Prompt, ResponseMode, CacheSchemaType, CredentialType } from "./utils/Constants"; export { StringUtils } from "./utils/StringUtils"; export { StringDict } from "./utils/MsalTypes"; diff --git a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts index 803c9e0d96..00eb40a1e0 100644 --- a/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts +++ b/lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts @@ -4,9 +4,6 @@ */ import { InMemoryCache, JsonCache, AccountFilter, CredentialFilter } from "./utils/CacheTypes"; -import { AccessTokenEntity } from "./entities/AccessTokenEntity"; -import { IdTokenEntity } from "./entities/IdTokenEntity"; -import { RefreshTokenEntity } from "./entities/RefreshTokenEntity"; import { AccountEntity } from "./entities/AccountEntity"; import { ICacheStorage } from "../cache/ICacheStorage"; import { Deserializer } from "./serialize/Deserializer"; @@ -14,8 +11,7 @@ import { Serializer } from "./serialize/Serializer"; import { Credential } from "./entities/Credential"; import { CredentialType, - Separators, - CredentialKeyPosition, + CacheSchemaType } from "../utils/Constants"; import { AccountCache, @@ -30,12 +26,10 @@ import { CacheRecord } from "./entities/CacheRecord"; export class UnifiedCacheManager implements ICacheManager { // Storage interface - private inMemoryCache: InMemoryCache; private cacheStorage: ICacheStorage; constructor(cacheImpl: ICacheStorage) { this.cacheStorage = cacheImpl; - this.inMemoryCache = this.cacheStorage.getCache(); } /** @@ -43,14 +37,14 @@ export class UnifiedCacheManager implements ICacheManager { * @param cache */ setCacheInMemory(cache: InMemoryCache): void { - this.inMemoryCache = cache; + this.cacheStorage.setCache(cache); } /** * get the inMemory Cache */ getCacheInMemory(): InMemoryCache { - return this.inMemoryCache; + return this.cacheStorage.getCache(); } /** @@ -75,7 +69,7 @@ export class UnifiedCacheManager implements ICacheManager { * Returns all accounts in memory */ getAllAccounts(): AccountCache { - return this.inMemoryCache.accounts; + return this.getCacheInMemory().accounts; } /** @@ -94,10 +88,8 @@ export class UnifiedCacheManager implements ICacheManager { * @param account */ saveAccount(account: AccountEntity): void { - const cache = this.getCacheInMemory(); const key = account.generateAccountKey(); - cache.accounts[key] = account; - this.setCacheInMemory(cache); + this.cacheStorage.setItemInMemory(key, account, CacheSchemaType.ACCOUNT); } /** @@ -105,66 +97,9 @@ export class UnifiedCacheManager implements ICacheManager { * @param credential */ saveCredential(credential: Credential): void { - const cache = this.getCacheInMemory(); - - switch (credential.credentialType) { - case CredentialType.ID_TOKEN: - this.saveIdToken(credential); - break; - case CredentialType.ACCESS_TOKEN: - this.saveAccessToken(credential); - break; - case CredentialType.REFRESH_TOKEN: - this.saveRefreshToken(credential); - break; - default: - console.log("Cache entity type mismatch"); - } - - this.setCacheInMemory(cache); - } - - /** - * save IdTokenEntity to Cache - * @param idToken - */ - saveIdToken(idToken: Credential) { - const cache = this.getCacheInMemory(); - const key = idToken.generateCredentialKey(); - if (cache.idTokens[key]) { - this.removeIdToken(key); - } - - cache.idTokens[key] = idToken as IdTokenEntity; - } - - /** - * save AccessTokenEntity to Cache - * @param accessToken - */ - saveAccessToken(accessToken: Credential) { - const cache = this.getCacheInMemory(); - const key = accessToken.generateCredentialKey(); - if (cache.accessTokens[key]) { - this.removeAccessToken(key); - } - - cache.accessTokens[key] = accessToken as AccessTokenEntity; - - } - - /** - * save RefreshTokenEntity to Cache - * @param refreshToken - */ - saveRefreshToken(refreshToken: Credential) { - const cache = this.getCacheInMemory(); - const key = refreshToken.generateCredentialKey(); - if (cache.refreshTokens[key]) { - this.removeRefreshToken(key); - } - - cache.refreshTokens[key] = refreshToken as RefreshTokenEntity; + console.log("in UCacheManager saving credential"); + const key = credential.generateCredentialKey(); + this.cacheStorage.setItemInMemory(key, credential, CacheSchemaType.CREDENTIAL); } /** @@ -172,7 +107,7 @@ export class UnifiedCacheManager implements ICacheManager { * @param key */ getAccount(key: string): AccountEntity { - return this.getCacheInMemory().accounts[key] || null; + return this.cacheStorage.getItemFromMemory(key, CacheSchemaType.ACCOUNT) as AccountEntity; } /** @@ -180,22 +115,7 @@ export class UnifiedCacheManager implements ICacheManager { * @param key */ getCredential(key: string): Credential { - const cache = this.getCacheInMemory(); - switch ( - key.split(Separators.CACHE_KEY_SEPARATOR)[ - CredentialKeyPosition.CREDENTIAL_TYPE - ] - ) { - case "idtoken": - return cache.idTokens[key] || null; - case "accesstoken": - return cache.accessTokens[key] || null; - case "refreshtoken": - return cache.refreshTokens[key] || null; - default: - console.log("Cache entity type mismatch"); - return null; - } + return this.cacheStorage.getItemFromMemory(key, CacheSchemaType.CREDENTIAL) as Credential; } /** @@ -377,7 +297,7 @@ export class UnifiedCacheManager implements ICacheManager { } // idTokens do not have "target", target specific refreshTokens do exist for some types of authentication - if (!!target && CacheHelper.getCredentialType(key) != CredentialType.ID_TOKEN.toString().toLowerCase()) { + if (!!target && CacheHelper.getCredentialType(key) != CredentialType.ID_TOKEN) { matches = matches && CacheHelper.matchTarget(key, target); } @@ -394,15 +314,8 @@ export class UnifiedCacheManager implements ICacheManager { * @param account */ removeAccount(account: AccountEntity): boolean { - const cache = this.getCacheInMemory(); - const accountKey = account.generateAccountKey(); - - if (!!cache.accounts[accountKey]) { - delete cache.accounts[accountKey]; - return true; - } - - return false; + const key = account.generateAccountKey(); + return this.cacheStorage.removeItemFromMemory(key, CacheSchemaType.ACCOUNT); } /** @@ -414,22 +327,24 @@ export class UnifiedCacheManager implements ICacheManager { const accountId = account.generateAccountId(); Object.keys(cache.idTokens).forEach((key) => { - if (cache.idTokens[key].generateAccountId() === accountId) - this.removeIdToken(key); + if (cache.idTokens[key].generateAccountId() === accountId) { + this.cacheStorage.removeItemFromMemory(key, CacheSchemaType.CREDENTIAL); + } }); Object.keys(cache.accessTokens).forEach((key) => { - if (cache.accessTokens[key].generateAccountId() === accountId) - this.removeAccessToken(key); + if (cache.accessTokens[key].generateAccountId() === accountId) { + this.cacheStorage.removeItemFromMemory(key, CacheSchemaType.CREDENTIAL); + } }); Object.keys(cache.refreshTokens).forEach((key) => { - if (cache.refreshTokens[key].generateAccountId() === accountId) - this.removeRefreshToken(key); + if (cache.refreshTokens[key].generateAccountId() === accountId) { + this.cacheStorage.removeItemFromMemory(key, CacheSchemaType.CREDENTIAL); + } }); - this.removeAccount(account); - return true; + return this.removeAccount(account); } /** @@ -438,50 +353,6 @@ export class UnifiedCacheManager implements ICacheManager { */ removeCredential(credential: Credential): boolean { const key = credential.generateCredentialKey(); - - switch (credential.credentialType) { - case CredentialType.ID_TOKEN: - return this.removeIdToken(key); - case CredentialType.ACCESS_TOKEN: - return this.removeAccessToken(key); - case CredentialType.REFRESH_TOKEN: - return this.removeRefreshToken(key); - default: - console.log("Cache entity type mismatch"); - return false; - } - } - - /** - * Remove an IdToken from the memory - * @param key - */ - removeIdToken(key: string): boolean { - const cache = this.getCacheInMemory(); - delete cache.idTokens[key]; - this.setCacheInMemory(cache); - return true; - } - - /** - * Remove an AccessToken from the memory - * @param key - */ - removeAccessToken(key: string): boolean { - const cache = this.getCacheInMemory(); - delete cache.accessTokens[key]; - this.setCacheInMemory(cache); - return true; - } - - /** - * Remove a RefreshToken from the memory - * @param key - */ - removeRefreshToken(key: string): boolean { - const cache = this.getCacheInMemory(); - delete cache.refreshTokens[key]; - this.setCacheInMemory(cache); - return true; + return this.cacheStorage.removeItemFromMemory(key, CacheSchemaType.CREDENTIAL); } } diff --git a/lib/msal-common/src/unifiedCache/utils/CacheHelper.ts b/lib/msal-common/src/unifiedCache/utils/CacheHelper.ts index 1eb1557998..2ee8b581fa 100644 --- a/lib/msal-common/src/unifiedCache/utils/CacheHelper.ts +++ b/lib/msal-common/src/unifiedCache/utils/CacheHelper.ts @@ -3,7 +3,12 @@ * Licensed under the MIT License. */ -import { Separators, CredentialKeyPosition } from "../../utils/Constants"; +import { + Separators, + CredentialKeyPosition, + CacheType, + CacheSchemaType, +} from "../../utils/Constants"; export class CacheHelper { /** @@ -52,7 +57,12 @@ export class CacheHelper { * @param homeAccountId */ static matchHomeAccountId(key: string, homeAccountId: string): boolean { - return homeAccountId === key.split(Separators.CACHE_KEY_SEPARATOR)[CredentialKeyPosition.HOME_ACCOUNT_ID]; + return ( + homeAccountId === + key.split(Separators.CACHE_KEY_SEPARATOR)[ + CredentialKeyPosition.HOME_ACCOUNT_ID + ] + ); } /** @@ -61,7 +71,12 @@ export class CacheHelper { * @param environment */ static matchEnvironment(key: string, environment: string): boolean { - return environment === key.split(Separators.CACHE_KEY_SEPARATOR)[CredentialKeyPosition.ENVIRONMENT]; + return ( + environment === + key.split(Separators.CACHE_KEY_SEPARATOR)[ + CredentialKeyPosition.ENVIRONMENT + ] + ); } /** @@ -71,7 +86,13 @@ export class CacheHelper { * // TODO: Confirm equality for enum vs string here */ static matchCredentialType(key: string, credentialType: string): boolean { - return credentialType.toLowerCase() === key.split(Separators.CACHE_KEY_SEPARATOR)[CredentialKeyPosition.CREDENTIAL_TYPE].toString().toLowerCase(); + return ( + credentialType.toLowerCase() === + key + .split(Separators.CACHE_KEY_SEPARATOR) + [CredentialKeyPosition.CREDENTIAL_TYPE].toString() + .toLowerCase() + ); } /** @@ -80,7 +101,12 @@ export class CacheHelper { * @param clientId */ static matchClientId(key: string, clientId: string): boolean { - return clientId === key.split(Separators.CACHE_KEY_SEPARATOR)[CredentialKeyPosition.CLIENT_ID]; + return ( + clientId === + key.split(Separators.CACHE_KEY_SEPARATOR)[ + CredentialKeyPosition.CLIENT_ID + ] + ); } /** @@ -89,7 +115,12 @@ export class CacheHelper { * @param realm */ static matchRealm(key: string, realm: string): boolean { - return realm === key.split(Separators.CACHE_KEY_SEPARATOR)[CredentialKeyPosition.REALM]; + return ( + realm === + key.split(Separators.CACHE_KEY_SEPARATOR)[ + CredentialKeyPosition.REALM + ] + ); } /** @@ -98,7 +129,12 @@ export class CacheHelper { * @param target */ static matchTarget(key: string, target: string): boolean { - return CacheHelper.targetsIntersect(key.split(Separators.CACHE_KEY_SEPARATOR)[CredentialKeyPosition.TARGET], target); + return CacheHelper.targetsIntersect( + key.split(Separators.CACHE_KEY_SEPARATOR)[ + CredentialKeyPosition.TARGET + ], + target + ); } /** @@ -127,4 +163,31 @@ export class CacheHelper { CredentialKeyPosition.CREDENTIAL_TYPE ]; } + + /** + * helper function to return `CacheSchemaType` + * @param key + */ + static getCacheType(type: number): string { + switch (type) { + case CacheType.ADFS: + case CacheType.MSA: + case CacheType.MSSTS: + case CacheType.GENERIC: + return CacheSchemaType.ACCOUNT; + + case CacheType.ACCESS_TOKEN: + case CacheType.REFRESH_TOKEN: + case CacheType.ID_TOKEN: + return CacheSchemaType.CREDENTIAL; + + case CacheType.APP_META_DATA: + return CacheSchemaType.APP_META_DATA; + + default: { + console.log("Invalid cache type"); + return null; + } + } + } } diff --git a/lib/msal-common/src/utils/Constants.ts b/lib/msal-common/src/utils/Constants.ts index 981b0f1621..5c414e600e 100644 --- a/lib/msal-common/src/utils/Constants.ts +++ b/lib/msal-common/src/utils/Constants.ts @@ -254,9 +254,18 @@ export enum Separators { * Credentail Type stored in the cache */ export enum CredentialType { - ID_TOKEN = "IdToken", - ACCESS_TOKEN = "AccessToken", - REFRESH_TOKEN = "RefreshToken" + ID_TOKEN = "idtoken", + ACCESS_TOKEN = "accesstoken", + REFRESH_TOKEN = "refreshtoken", +} + +/** + * Credentail Type stored in the cache + */ +export enum CacheSchemaType { + ACCOUNT = "Account", + CREDENTIAL = "Credential", + APP_META_DATA = "AppMetadata" } /** diff --git a/lib/msal-common/test/unifiedCache/UnifiedCacheManager.spec.ts b/lib/msal-common/test/unifiedCache/UnifiedCacheManager.spec.ts index fe82128ecf..bc57b13d4a 100644 --- a/lib/msal-common/test/unifiedCache/UnifiedCacheManager.spec.ts +++ b/lib/msal-common/test/unifiedCache/UnifiedCacheManager.spec.ts @@ -6,6 +6,10 @@ import { ICacheStorage } from "../../src/cache/ICacheStorage"; import { Deserializer } from "../../src/unifiedCache/serialize/Deserializer"; import { AccountEntity } from "../../src/unifiedCache/entities/AccountEntity"; import { AccessTokenEntity } from "../../src/unifiedCache/entities/AccessTokenEntity"; +import { CacheSchemaType, CacheHelper, CredentialType } from "../../src"; +import { IdTokenEntity } from "../../src/unifiedCache/entities/IdTokenEntity"; +import { RefreshTokenEntity } from "../../src/unifiedCache/entities/RefreshTokenEntity"; +import { AppMetadataEntity } from "../../src/unifiedCache/entities/AppMetadataEntity"; const cacheJson = require("./serialize/cache.json"); @@ -28,11 +32,152 @@ describe("UnifiedCacheManager test cases", () => { setItem(key: string, value: string): void { store[key] = value; }, + setItemInMemory(key: string, value: object, type?: string): void { + // read inMemoryCache + const cache = this.getCache(); + + // save the cacheItem + switch (type) { + case CacheSchemaType.ACCOUNT: { + cache.accounts[key] = value as AccountEntity; + break; + } + case CacheSchemaType.CREDENTIAL: { + const credentialType = CacheHelper.getCredentialType(key); + console.log(credentialType); + switch (credentialType) { + case CredentialType.ID_TOKEN: { + cache.idTokens[key] = value as IdTokenEntity; + break; + } + case CredentialType.ACCESS_TOKEN: { + cache.accessTokens[key] = value as AccessTokenEntity; + console.log(value); + break; + } + case CredentialType.REFRESH_TOKEN: { + cache.refreshTokens[key] = value as RefreshTokenEntity; + break; + } + } + break; + } + case CacheSchemaType.APP_META_DATA: { + cache.appMetadata[key] = value as AppMetadataEntity; + break; + } + default: { + console.log("Invalid Cache Type"); + return; + } + } + + // update inMemoryCache + this.setCache(cache); + }, getItem(key: string): string { return store[key]; }, - removeItem(key: string): void { + getItemFromMemory(key: string, type?: string): object { + // read inMemoryCache + const cache = this.getCache(); + + // save the cacheItem + switch (type) { + case CacheSchemaType.ACCOUNT: { + return cache.accounts[key] as AccountEntity || null; + } + case CacheSchemaType.CREDENTIAL: { + const credentialType = CacheHelper.getCredentialType(key); + let credential = null; + switch (credentialType) { + case CredentialType.ID_TOKEN: { + credential = cache.idTokens[key] as IdTokenEntity || null; + break; + } + case CredentialType.ACCESS_TOKEN: { + credential = cache.accessTokens[key] as AccessTokenEntity || null; + break; + } + case CredentialType.REFRESH_TOKEN: { + credential = cache.refreshTokens[key] as RefreshTokenEntity || null; + break; + } + } + return credential!; + } + case CacheSchemaType.APP_META_DATA: { + return cache.appMetadata[key] as AppMetadataEntity || null; + } + default: { + console.log("Invalid Cache Type"); + return {}; + } + } + }, + removeItem(key: string): boolean { delete store[key]; + return true; + }, + removeItemFromMemory(key: string, type?: string): boolean { + // read inMemoryCache + const cache = this.getCache(); + let result: boolean = false; + + // save the cacheItem + switch (type) { + case CacheSchemaType.ACCOUNT: { + if (!!cache.accounts[key]) { + delete cache.accounts[key]; + result = true; + } + break; + } + case CacheSchemaType.CREDENTIAL: { + const credentialType = CacheHelper.getCredentialType(key); + switch (credentialType) { + case CredentialType.ID_TOKEN: { + if (!!cache.idTokens[key]) { + delete cache.idTokens[key]; + result = true; + } + break; + } + case CredentialType.ACCESS_TOKEN: { + if (!!cache.accessTokens[key]) { + delete cache.accessTokens[key]; + result = true; + } + break; + } + case CredentialType.REFRESH_TOKEN: { + if (!!cache.refreshTokens[key]) { + delete cache.refreshTokens[key]; + result = true; + } + break; + } + } + break; + } + case CacheSchemaType.APP_META_DATA: { + if (!!cache.appMetadata[key]) { + delete cache.appMetadata[key]; + result = true; + } + break; + } + default: { + console.log("Invalid Cache Type"); + break; + } + } + + // write to the cache after removal + if (result) { + this.setCache(cache); + } + return result; }, containsKey(key: string): boolean { return !!store[key]; @@ -105,7 +250,6 @@ describe("UnifiedCacheManager test cases", () => { }); let unifiedCacheManager = new UnifiedCacheManager(storageInterface); - const atKey = at.generateCredentialKey(); unifiedCacheManager.saveCredential(at); expect( diff --git a/lib/msal-node/src/cache/Storage.ts b/lib/msal-node/src/cache/Storage.ts index d0d3eac967..845b428deb 100644 --- a/lib/msal-node/src/cache/Storage.ts +++ b/lib/msal-node/src/cache/Storage.ts @@ -2,8 +2,19 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ -import { ICacheStorage, InMemoryCache, CredentialType, CacheHelper} from '@azure/msal-common'; +import { + ICacheStorage, + InMemoryCache, + CredentialType, + CacheSchemaType, + CacheHelper, +} from '@azure/msal-common'; import { CacheOptions } from '../config/Configuration'; +import { AccountEntity } from '@azure/msal-common/dist/src/unifiedCache/entities/AccountEntity'; +import { AccessTokenEntity } from '@azure/msal-common/dist/src/unifiedCache/entities/AccessTokenEntity'; +import { RefreshTokenEntity } from '@azure/msal-common/dist/src/unifiedCache/entities/RefreshTokenEntity'; +import { IdTokenEntity } from '@azure/msal-common/dist/src/unifiedCache/entities/IdTokenEntity'; +import { AppMetadataEntity } from '@azure/msal-common/dist/src/unifiedCache/entities/AppMetadataEntity'; /** * This class implements Storage for node, reading cache from user specified storage location or an extension library @@ -38,42 +49,187 @@ export class Storage implements ICacheStorage { * Sets the cache item with the key and value given. * @param key * @param value + * TODO: Implement after granular persistence of cache is supported */ setItem(key: string, value: string): void { + if (key && value) { + return; + } + } + + /** + * Set item in Memory + * @param key + * @param value + * @param type + */ + setItemInMemory(key: string, value: object, type?: string): void { + // read inMemoryCache + const cache = this.getCache(); + + // save the cacheItem + switch (type) { + case CacheSchemaType.ACCOUNT: { + cache.accounts[key] = value as AccountEntity; + break; + } + case CacheSchemaType.CREDENTIAL: { + const credentialType = CacheHelper.getCredentialType(key); + switch (credentialType) { + case CredentialType.ID_TOKEN: { + cache.idTokens[key] = value as IdTokenEntity; + break; + } + case CredentialType.ACCESS_TOKEN: { + cache.accessTokens[key] = value as AccessTokenEntity; + break; + } + case CredentialType.REFRESH_TOKEN: { + cache.refreshTokens[key] = value as RefreshTokenEntity; + break; + } + } + break; + } + case CacheSchemaType.APP_META_DATA: { + cache.appMetadata[key] = value as AppMetadataEntity; + break; + } + default: { + console.log('Invalid Cache Type'); + return; + } + } + // update inMemoryCache + this.setCache(cache); } /** * Gets cache item with given key. * Will retrieve frm cookies if storeAuthStateInCookie is set to true. * @param key - * TODO: implement after the lookup implementation + * TODO: Implement after granular persistence of cache is supported */ getItem(key: string): string { return key ? 'random' : 'truly random'; } + getItemFromMemory(key: string, type?: string): object { + // read inMemoryCache + const cache = this.getCache(); + + // save the cacheItem + switch (type) { + case CacheSchemaType.ACCOUNT: { + return (cache.accounts[key] as AccountEntity) || null; + } + case CacheSchemaType.CREDENTIAL: { + const credentialType = CacheHelper.getCredentialType(key); + let credential = null; + switch (credentialType) { + case CredentialType.ID_TOKEN: { + credential = + (cache.idTokens[key] as IdTokenEntity) || null; + break; + } + case CredentialType.ACCESS_TOKEN: { + credential = + (cache.accessTokens[key] as AccessTokenEntity) || + null; + break; + } + case CredentialType.REFRESH_TOKEN: { + credential = + (cache.refreshTokens[key] as RefreshTokenEntity) || + null; + break; + } + } + return credential!; + } + case CacheSchemaType.APP_META_DATA: { + return (cache.appMetadata[key] as AppMetadataEntity) || null; + } + default: { + console.log('Invalid Cache Type'); + return {}; + } + } + } + /** * Removes the cache item with the given key. * @param key */ removeItem(key: string): boolean { + return key ? true : false; + } + + /** + * Removes the cache item from memory with the given key. + * @param key + * @param type + */ + removeItemFromMemory(key: string, type?: string): boolean { + // read inMemoryCache const cache = this.getCache(); + let result: boolean = false; + + // save the cacheItem + switch (type) { + case CacheSchemaType.ACCOUNT: { + if (!!cache.accounts[key]) { + delete cache.accounts[key]; + result = true; + } + break; + } + case CacheSchemaType.CREDENTIAL: { + const credentialType = CacheHelper.getCredentialType(key); + switch (credentialType) { + case CredentialType.ID_TOKEN: { + if (!!cache.idTokens[key]) { + delete cache.idTokens[key]; + result = true; + } + break; + } + case CredentialType.ACCESS_TOKEN: { + if (!!cache.accessTokens[key]) { + delete cache.accessTokens[key]; + result = true; + } + break; + } + case CredentialType.REFRESH_TOKEN: { + if (!!cache.refreshTokens[key]) { + delete cache.refreshTokens[key]; + result = true; + } + break; + } + } + break; + } + case CacheSchemaType.APP_META_DATA: { + if (!!cache.appMetadata[key]) { + delete cache.appMetadata[key]; + result = true; + } + break; + } + default: { + console.log('Invalid Cache Type'); + break; + } + } - switch (CacheHelper.getCredentialType(key)) { - case CredentialType.ID_TOKEN: - delete cache.idTokens[key]; - return true; - case CredentialType.ACCESS_TOKEN: - delete cache.accessTokens[key]; - return true; - case CredentialType.REFRESH_TOKEN: - delete cache.refreshTokens[key]; - return true; - default: - console.log("Cache entity type mismatch"); - return false; + // write to the cache after removal + if (result) { + this.setCache(cache); } + return result; } /**