Skip to content

Commit 4a4dc16

Browse files
authored
Merge pull request #1655 from AzureAD/msal-node-cache-lookup-interface
[msal-node] Cache Lookup - 2: Save, lookup and delete entities
2 parents 4dcd69d + 3da2e57 commit 4a4dc16

21 files changed

+1312
-226
lines changed

lib/msal-browser/src/cache/BrowserStorage.ts

+31-1
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,13 @@ export class BrowserStorage implements ICacheStorage {
152152
* Will also clear the cookie item if storeAuthStateInCookie is set to true.
153153
* @param key
154154
*/
155-
removeItem(key: string): void {
155+
removeItem(key: string): boolean {
156156
const msalKey = this.generateCacheKey(key);
157157
this.windowStorage.removeItem(msalKey);
158158
if (this.cacheConfig.storeAuthStateInCookie) {
159159
this.clearItemCookie(msalKey);
160160
}
161+
return true;
161162
}
162163

163164
/**
@@ -271,4 +272,33 @@ export class BrowserStorage implements ICacheStorage {
271272
setCache(): void {
272273
// sets nothing
273274
}
275+
276+
/**
277+
* Dummy implementation for interface compat - will change after BrowserCacheMigration
278+
* @param key
279+
* @param value
280+
* @param type
281+
*/
282+
setItemInMemory(key: string, value: object, type?: string): void {
283+
if (key && value && type)
284+
return;
285+
}
286+
287+
/**
288+
* Dummy implementation for interface compat - will change after BrowserCacheMigration
289+
* @param key
290+
* @param type
291+
*/
292+
getItemFromMemory(key: string, type?: string): object {
293+
return key && type ? {} : {};
294+
};
295+
296+
/**
297+
* Dummy implementation for interface compat - will change after BrowserCacheMigration
298+
* @param key
299+
* @param type
300+
*/
301+
removeItemFromMemory(key: string, type?: string): boolean {
302+
return key && type ? true : false;
303+
};
274304
}

lib/msal-common/src/cache/ICacheStorage.ts

+24-2
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,39 @@ export interface ICacheStorage {
2929
*/
3030
setItem(key: string, value: string): void;
3131

32+
/**
33+
* Function to set item in Memory
34+
* @param key
35+
* @param value
36+
* @param type
37+
*/
38+
setItemInMemory(key: string, value: object, type?: string): void;
39+
3240
/**
3341
* Function which retrieves item from cache.
3442
* @param key
3543
*/
3644
getItem(key: string): string;
3745

3846
/**
39-
* Function which removes item from cache.
47+
* Function to get an item from memory
48+
* @param key
49+
* @param type
50+
*/
51+
getItemFromMemory(key: string, type?: string): object;
52+
53+
/**
54+
* Function to remove an item from cache given its key.
55+
* @param key
56+
*/
57+
removeItem(key: string): boolean;
58+
59+
/**
60+
* Function to remove an item from memory given its key
4061
* @param key
62+
* @param type
4163
*/
42-
removeItem(key: string): void;
64+
removeItemFromMemory(key: string, type?: string): boolean;
4365

4466
/**
4567
* Function which returns boolean whether cache contains a specific key.

lib/msal-common/src/config/ClientConfiguration.ts

+12
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ const DEFAULT_STORAGE_IMPLEMENTATION: ICacheStorage = {
125125
const notImplErr = "Storage interface - getItem() has not been implemented for the cacheStorage interface.";
126126
throw AuthError.createUnexpectedError(notImplErr);
127127
},
128+
getItemFromMemory: (): object => {
129+
const notImplErr = "Storage interface - getItemFromMemory() has not been implemented for the cacheStorage interface.";
130+
throw AuthError.createUnexpectedError(notImplErr);
131+
},
128132
getKeys: (): string[] => {
129133
const notImplErr = "Storage interface - getKeys() has not been implemented for the cacheStorage interface.";
130134
throw AuthError.createUnexpectedError(notImplErr);
@@ -133,10 +137,18 @@ const DEFAULT_STORAGE_IMPLEMENTATION: ICacheStorage = {
133137
const notImplErr = "Storage interface - removeItem() has not been implemented for the cacheStorage interface.";
134138
throw AuthError.createUnexpectedError(notImplErr);
135139
},
140+
removeItemFromMemory: () => {
141+
const notImplErr = "Storage interface - removeItemFromMemory() has not been implemented for the cacheStorage interface.";
142+
throw AuthError.createUnexpectedError(notImplErr);
143+
},
136144
setItem: () => {
137145
const notImplErr = "Storage interface - setItem() has not been implemented for the cacheStorage interface.";
138146
throw AuthError.createUnexpectedError(notImplErr);
139147
},
148+
setItemInMemory: () => {
149+
const notImplErr = "Storage interface - setItemInMemory() has not been implemented for the cacheStorage interface.";
150+
throw AuthError.createUnexpectedError(notImplErr);
151+
},
140152
getCache: (): InMemoryCache => {
141153
const notImplErr = "Storage interface - getCache() has not been implemented for the cacheStorage interface.";
142154
throw AuthError.createUnexpectedError(notImplErr);

lib/msal-common/src/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export { UnifiedCacheManager } from "./unifiedCache/UnifiedCacheManager";
2121
export { JsonCache, InMemoryCache } from "./unifiedCache/utils/CacheTypes";
2222
export { Serializer } from "./unifiedCache/serialize/Serializer";
2323
export { Deserializer } from "./unifiedCache/serialize/Deserializer";
24+
export { CacheHelper } from "./unifiedCache/utils/CacheHelper";
2425
// Network Interface
2526
export { INetworkModule, NetworkRequestOptions } from "./network/INetworkModule";
2627
export { NetworkResponse } from "./network/NetworkManager";
@@ -50,7 +51,7 @@ export { ClientAuthError, ClientAuthErrorMessage } from "./error/ClientAuthError
5051
export { ClientConfigurationError, ClientConfigurationErrorMessage } from "./error/ClientConfigurationError";
5152
// Constants and Utils
5253
export {
53-
Constants, PromptValue, TemporaryCacheKeys, PersistentCacheKeys, Prompt, ResponseMode
54+
Constants, PromptValue, TemporaryCacheKeys, PersistentCacheKeys, Prompt, ResponseMode, CacheSchemaType, CredentialType
5455
} from "./utils/Constants";
5556
export { StringUtils } from "./utils/StringUtils";
5657
export { StringDict } from "./utils/MsalTypes";

lib/msal-common/src/response/AuthenticationResult.ts

+5-8
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,15 @@ import { StringDict } from "../utils/MsalTypes";
88
/**
99
* Result returned from the authority's token endpoint.
1010
*/
11-
// TODO: Also consider making an external type and use this as internal
1211
export class AuthenticationResult {
13-
// TODO this is temp class, it will be updated.
14-
uniqueId: string; // TODO: Check applicability
15-
tenantId: string; // TODO: Check applicability
12+
uniqueId: string;
13+
tenantId: string;
1614
scopes: Array<string>;
17-
tokenType: string; // TODO: get rid of this if we can
1815
idToken: string;
1916
idTokenClaims: StringDict;
2017
accessToken: string;
2118
expiresOn: Date;
22-
extExpiresOn?: Date; // TODO: Check what this maps to in other libraries
23-
userRequestState?: string; // TODO: remove, just check how state is handled in other libraries
24-
familyId?: string; // TODO: Check wider audience
19+
extExpiresOn?: Date;
20+
state?: string;
21+
familyId?: string;
2522
}

lib/msal-common/src/response/ResponseHandler.ts

+46-76
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { IdTokenEntity } from "../unifiedCache/entities/IdTokenEntity";
2222
import { AccessTokenEntity } from "../unifiedCache/entities/AccessTokenEntity";
2323
import { RefreshTokenEntity } from "../unifiedCache/entities/RefreshTokenEntity";
2424
import { InteractionRequiredAuthError } from "../error/InteractionRequiredAuthError";
25+
import { CacheRecord } from "../unifiedCache/entities/CacheRecord";
2526

2627
/**
2728
* Class that handles response parsing.
@@ -104,81 +105,32 @@ export class ResponseHandler {
104105
* @param state
105106
*/
106107
generateAuthenticationResult(serverTokenResponse: ServerAuthorizationTokenResponse, authority: Authority): AuthenticationResult {
107-
// Retrieve current account if in Cache
108-
// TODO: add this once the req for cache look up for tokens is confirmed
109108

110-
const authenticationResult = this.processTokenResponse(serverTokenResponse, authority);
111-
112-
const environment = authority.canonicalAuthorityUrlComponents.HostNameAndPort;
113-
this.addCredentialsToCache(authenticationResult, environment, serverTokenResponse.refresh_token);
114-
115-
return authenticationResult;
116-
}
117-
118-
/**
119-
* Returns a new AuthenticationResult with the data from original result filled with the relevant data.
120-
* @param authenticationResult
121-
* @param idTokenString(raw idToken in the server response)
122-
*/
123-
processTokenResponse(serverTokenResponse: ServerAuthorizationTokenResponse, authority: Authority): AuthenticationResult {
124-
const authenticationResult: AuthenticationResult = {
125-
uniqueId: "",
126-
tenantId: "",
127-
tokenType: "",
128-
idToken: null,
129-
idTokenClaims: null,
130-
accessToken: "",
131-
scopes: [],
132-
expiresOn: null,
133-
familyId: null
134-
};
135-
136-
// IdToken
109+
// create an idToken object (not entity)
137110
const idTokenObj = new IdToken(serverTokenResponse.id_token, this.cryptoObj);
138111

139-
// if account is not in cache, append it to the cache
140-
this.addAccountToCache(serverTokenResponse, idTokenObj, authority);
141-
142-
// TODO: Check how this changes for auth code response
143-
const expiresSeconds = Number(idTokenObj.claims.exp);
144-
if (expiresSeconds && !authenticationResult.expiresOn) {
145-
authenticationResult.expiresOn = new Date(expiresSeconds * 1000);
146-
}
112+
// save the response tokens
113+
const cacheRecord = this.generateCacheRecord(serverTokenResponse, idTokenObj, authority);
114+
this.uCacheManager.saveCacheRecord(cacheRecord);
147115

148116
// Expiration calculation
149117
const expiresInSeconds = TimeUtils.nowSeconds() + serverTokenResponse.expires_in;
150118
const extendedExpiresInSeconds = expiresInSeconds + serverTokenResponse.ext_expires_in;
151-
// Set consented scopes in response
152-
const responseScopes = ScopeSet.fromString(serverTokenResponse.scope, this.clientId, true);
153119

154-
return {
155-
...authenticationResult,
120+
const responseScopes = ScopeSet.fromString(serverTokenResponse.scope, this.clientId, true);
121+
const authenticationResult: AuthenticationResult = {
156122
uniqueId: idTokenObj.claims.oid || idTokenObj.claims.sub,
157123
tenantId: idTokenObj.claims.tid,
124+
scopes: responseScopes.asArray(),
158125
idToken: idTokenObj.rawIdToken,
159126
idTokenClaims: idTokenObj.claims,
160127
accessToken: serverTokenResponse.access_token,
161128
expiresOn: new Date(expiresInSeconds),
162129
extExpiresOn: new Date(extendedExpiresInSeconds),
163-
scopes: responseScopes.asArray(),
164-
familyId: serverTokenResponse.foci,
130+
familyId: serverTokenResponse.foci || null,
165131
};
166-
}
167132

168-
/**
169-
* if Account is not in the cache, generateAccount and append it to the cache
170-
* @param serverTokenResponse
171-
* @param idToken
172-
* @param authority
173-
*/
174-
addAccountToCache(serverTokenResponse: ServerAuthorizationTokenResponse, idToken: IdToken, authority: Authority): void {
175-
const environment = authority.canonicalAuthorityUrlComponents.HostNameAndPort;
176-
let accountEntity: AccountEntity;
177-
const cachedAccount: AccountEntity = this.uCacheManager.getAccount(this.homeAccountIdentifier, environment, idToken.claims.tid);
178-
if (!cachedAccount) {
179-
accountEntity = this.generateAccountEntity(serverTokenResponse, idToken, authority);
180-
this.uCacheManager.addAccountEntity(accountEntity);
181-
}
133+
return authenticationResult;
182134
}
183135

184136
/**
@@ -205,35 +157,53 @@ export class ResponseHandler {
205157
}
206158

207159
/**
208-
* Appends the minted tokens to the in-memory cache
209-
* @param authenticationResult
160+
* Generates CacheRecord
161+
* @param serverTokenResponse
162+
* @param idTokenObj
210163
* @param authority
211164
*/
212-
addCredentialsToCache(
213-
authenticationResult: AuthenticationResult,
214-
authority: string,
215-
refreshToken: string
216-
): void {
217-
const idTokenEntity = IdTokenEntity.createIdTokenEntity(
165+
generateCacheRecord(serverTokenResponse: ServerAuthorizationTokenResponse, idTokenObj: IdToken, authority: Authority): CacheRecord {
166+
167+
const cacheRecord = new CacheRecord();
168+
169+
// Account
170+
cacheRecord.account = this.generateAccountEntity(
171+
serverTokenResponse,
172+
idTokenObj,
173+
authority
174+
);
175+
176+
// IdToken
177+
cacheRecord.idToken = IdTokenEntity.createIdTokenEntity(
218178
this.homeAccountIdentifier,
219-
authenticationResult,
179+
authority.canonicalAuthorityUrlComponents.HostNameAndPort,
180+
serverTokenResponse.id_token,
220181
this.clientId,
221-
authority
182+
idTokenObj.claims.tid
222183
);
223-
const accessTokenEntity = AccessTokenEntity.createAccessTokenEntity(
184+
185+
// AccessToken
186+
const responseScopes = ScopeSet.fromString(serverTokenResponse.scope, this.clientId, true);
187+
cacheRecord.accessToken = AccessTokenEntity.createAccessTokenEntity(
224188
this.homeAccountIdentifier,
225-
authenticationResult,
189+
authority.canonicalAuthorityUrlComponents.HostNameAndPort,
190+
serverTokenResponse.access_token,
226191
this.clientId,
227-
authority
192+
idTokenObj.claims.tid,
193+
responseScopes.asArray().join(" "),
194+
serverTokenResponse.expires_in,
195+
serverTokenResponse.ext_expires_in
228196
);
229-
const refreshTokenEntity = RefreshTokenEntity.createRefreshTokenEntity(
197+
198+
// refreshToken
199+
cacheRecord.refreshToken = RefreshTokenEntity.createRefreshTokenEntity(
230200
this.homeAccountIdentifier,
231-
authenticationResult,
232-
refreshToken,
201+
authority.canonicalAuthorityUrlComponents.HostNameAndPort,
202+
serverTokenResponse.refresh_token,
233203
this.clientId,
234-
authority
204+
serverTokenResponse.foci
235205
);
236206

237-
this.uCacheManager.addCredentialCache(accessTokenEntity, idTokenEntity, refreshTokenEntity);
207+
return cacheRecord;
238208
}
239209
}

0 commit comments

Comments
 (0)