Skip to content

[msal-node] Cache Lookup - 1: Logical keys for cache entities #1624

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 34 commits into from
Jun 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
446258d
Edit tests to match the new key generation
sameerag May 11, 2020
8b86a3d
update test case desc
sameerag May 11, 2020
82e11c8
Add accountKey changes
sameerag May 11, 2020
4ff2f5f
Merge branch 'msal-node-cache-fileops-async' into msal-node-cache-keys
sameerag May 11, 2020
6948e41
Merge branch 'msal-node-cache-fileops-async' into msal-node-cache-keys
sameerag May 11, 2020
ddee5c4
Merge branch 'msal-node-cache-fileops-async' into msal-node-cache-keys
sameerag May 11, 2020
a456a67
Merge branch 'msal-node-cache-fileops-async' into msal-node-cache-keys
sameerag May 11, 2020
398802f
Merge branch 'msal-node-cache-fileops-async' into msal-node-cache-keys
sameerag May 11, 2020
5863af0
Cache lookup helpers
sameerag May 14, 2020
c3d30dd
Name Change for some APIs
sameerag May 14, 2020
6f9565b
Add helper functions in UnifiedCacheManager
sameerag May 14, 2020
4ddae07
Fix linter issues
sameerag May 14, 2020
2b0afe1
fix linter issues
sameerag May 14, 2020
953e638
Merge branch 'msal-node-cache-lookup-interface' into msal-node-cache-…
sameerag May 14, 2020
7ca83ab
fix linter issues
sameerag May 14, 2020
87ce032
Changing the filter interface for users to be able to pass optional p…
sameerag May 15, 2020
06ecbaf
Merge branch 'msal-node-cache-lookup-interface' into msal-node-cache-…
sameerag May 15, 2020
d109982
fixing compile issues
sameerag May 15, 2020
7369e51
Response Changes
sameerag May 19, 2020
9649a91
fixing linter issues
sameerag May 20, 2020
703b6c1
Merge branch 'msal-node-cache-lookup-interface' into msal-node-cache-…
sameerag May 20, 2020
e19fdad
fix linter issues
sameerag May 20, 2020
5b66edc
Merge branch 'dev' into msal-node-cache-keys
sameerag May 27, 2020
87fa687
Merge branch 'dev' into msal-node-cache-lookup-interface
sameerag May 27, 2020
1660b16
Merge branch 'msal-node-cache-keys' into msal-node-cache-lookup-inter…
sameerag May 27, 2020
403e81a
Merge branch 'msal-node-cache-lookup-interface' into msal-node-cache-…
sameerag May 27, 2020
bdd0162
Merge branch 'dev' into msal-node-cache-keys
sameerag Jun 3, 2020
036ac45
fixing linter errors
sameerag Jun 3, 2020
ef1f729
Merge pull request #1680 from AzureAD/msal-node-cache-response
sameerag Jun 3, 2020
2da03d9
Pushing changes for cacheType and some other oprtimizations! Not final.
sameerag Jun 3, 2020
533a907
Merge branch 'msal-node-cache-lookup-interface' of https://github.com…
sameerag Jun 3, 2020
4dcd69d
Merge branch 'dev' into msal-node-cache-keys
sameerag Jun 3, 2020
3da2e57
Cache save/delete/lookup moved to platform specific implementation
sameerag Jun 4, 2020
4a4dc16
Merge pull request #1655 from AzureAD/msal-node-cache-lookup-interface
sameerag Jun 4, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions lib/msal-browser/src/cache/BrowserStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -268,7 +269,36 @@ export class BrowserStorage implements ICacheStorage {
/**
* Dummy implementation until browser cache is migrated
*/
setCache() {
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;
};
}
26 changes: 24 additions & 2 deletions lib/msal-common/src/cache/ICacheStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,39 @@ 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
*/
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
*/
removeItem(key: string): void;
removeItemFromMemory(key: string, type?: string): boolean;

/**
* Function which returns boolean whether cache contains a specific key.
Expand Down
12 changes: 12 additions & 0 deletions lib/msal-common/src/config/ClientConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion lib/msal-common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,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";
Expand Down Expand Up @@ -50,7 +51,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";
13 changes: 5 additions & 8 deletions lib/msal-common/src/response/AuthenticationResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>;
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;
}
122 changes: 46 additions & 76 deletions lib/msal-common/src/response/ResponseHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +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 { CacheRecord } from "../unifiedCache/entities/CacheRecord";

/**
* Class that handles response parsing.
Expand Down Expand Up @@ -104,81 +105,32 @@ 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);

// 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);
}
// save the response tokens
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;
// 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);
const authenticationResult: AuthenticationResult = {
uniqueId: idTokenObj.claims.oid || idTokenObj.claims.sub,
tenantId: idTokenObj.claims.tid,
scopes: responseScopes.asArray(),
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 environment = authority.canonicalAuthorityUrlComponents.HostNameAndPort;
let accountEntity: AccountEntity;
const cachedAccount: AccountEntity = this.uCacheManager.getAccount(this.homeAccountIdentifier, environment, idToken.claims.tid);
if (!cachedAccount) {
accountEntity = this.generateAccountEntity(serverTokenResponse, idToken, authority);
this.uCacheManager.addAccountEntity(accountEntity);
}
return authenticationResult;
}

/**
Expand All @@ -205,35 +157,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 {

const 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;
}
}
Loading