Skip to content

Commit ef1f729

Browse files
authored
Merge pull request #1680 from AzureAD/msal-node-cache-response
[msal-node] Cache Lookup - 3: Msal node cache response
2 parents 1660b16 + 403e81a commit ef1f729

File tree

8 files changed

+100
-148
lines changed

8 files changed

+100
-148
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ export class BrowserStorage implements ICacheStorage {
268268
/**
269269
* Dummy implementation until browser cache is migrated
270270
*/
271-
setCache() {
271+
setCache(): void {
272272
// sets nothing
273273
}
274274
}

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-79
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,84 +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 accountEntity: AccountEntity = this.generateAccountEntity(
176-
serverTokenResponse,
177-
idToken,
178-
authority
179-
);
180-
181-
const cachedAccount: AccountEntity = this.uCacheManager.getAccount(accountEntity.generateAccountKey());
182-
if (!cachedAccount) {
183-
this.uCacheManager.addAccountEntity(accountEntity);
184-
}
133+
return authenticationResult;
185134
}
186135

187136
/**
@@ -208,35 +157,53 @@ export class ResponseHandler {
208157
}
209158

210159
/**
211-
* Appends the minted tokens to the in-memory cache
212-
* @param authenticationResult
160+
* Generates CacheRecord
161+
* @param serverTokenResponse
162+
* @param idTokenObj
213163
* @param authority
214164
*/
215-
addCredentialsToCache(
216-
authenticationResult: AuthenticationResult,
217-
authority: string,
218-
refreshToken: string
219-
): void {
220-
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(
221178
this.homeAccountIdentifier,
222-
authenticationResult,
179+
authority.canonicalAuthorityUrlComponents.HostNameAndPort,
180+
serverTokenResponse.id_token,
223181
this.clientId,
224-
authority
182+
idTokenObj.claims.tid
225183
);
226-
const accessTokenEntity = AccessTokenEntity.createAccessTokenEntity(
184+
185+
// AccessToken
186+
const responseScopes = ScopeSet.fromString(serverTokenResponse.scope, this.clientId, true);
187+
cacheRecord.accessToken = AccessTokenEntity.createAccessTokenEntity(
227188
this.homeAccountIdentifier,
228-
authenticationResult,
189+
authority.canonicalAuthorityUrlComponents.HostNameAndPort,
190+
serverTokenResponse.access_token,
229191
this.clientId,
230-
authority
192+
idTokenObj.claims.tid,
193+
responseScopes.asArray().join(" "),
194+
serverTokenResponse.expires_in,
195+
serverTokenResponse.ext_expires_in
231196
);
232-
const refreshTokenEntity = RefreshTokenEntity.createRefreshTokenEntity(
197+
198+
// refreshToken
199+
cacheRecord.refreshToken = RefreshTokenEntity.createRefreshTokenEntity(
233200
this.homeAccountIdentifier,
234-
authenticationResult,
235-
refreshToken,
201+
authority.canonicalAuthorityUrlComponents.HostNameAndPort,
202+
serverTokenResponse.refresh_token,
236203
this.clientId,
237-
authority
204+
serverTokenResponse.foci
238205
);
239206

240-
this.uCacheManager.addCredentialCache(accessTokenEntity, idTokenEntity, refreshTokenEntity);
207+
return cacheRecord;
241208
}
242209
}

lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts

+11-33
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
} from "./utils/CacheTypes";
2727
import { ICacheManager } from "./interface/ICacheManager";
2828
import { CacheHelper } from "./utils/CacheHelper";
29+
import { CacheRecord } from "./entities/CacheRecord";
2930

3031
export class UnifiedCacheManager implements ICacheManager {
3132
// Storage interface
@@ -71,43 +72,21 @@ export class UnifiedCacheManager implements ICacheManager {
7172
}
7273

7374
/**
74-
* append credential cache to in memory cach
75-
* @param accessToken
76-
* @param idToken
77-
* @param refreshToken
78-
*/
79-
addCredentialCache(
80-
accessToken: AccessTokenEntity,
81-
idToken: IdTokenEntity,
82-
refreshToken: RefreshTokenEntity
83-
): void {
84-
this.inMemoryCache.accessTokens[
85-
accessToken.generateCredentialKey()
86-
] = accessToken;
87-
this.inMemoryCache.idTokens[
88-
idToken.generateCredentialKey()
89-
] = idToken;
90-
this.inMemoryCache.refreshTokens[
91-
refreshToken.generateCredentialKey()
92-
] = refreshToken;
93-
}
94-
95-
/**
96-
* append account to the in memory cache
97-
* @param account
75+
* Returns all accounts in memory
9876
*/
99-
addAccountEntity(account: AccountEntity): void {
100-
const accKey = account.generateAccountKey();
101-
if (!this.inMemoryCache.accounts[accKey]) {
102-
this.inMemoryCache.accounts[accKey] = account;
103-
}
77+
getAllAccounts(): AccountCache {
78+
return this.inMemoryCache.accounts;
10479
}
10580

10681
/**
107-
* Returns all accounts in memory
82+
* saves a cache record
83+
* @param cacheRecord
10884
*/
109-
getAllAccounts(): AccountCache {
110-
return this.inMemoryCache.accounts;
85+
saveCacheRecord(cacheRecord: CacheRecord): void {
86+
this.saveAccount(cacheRecord.account);
87+
this.saveCredential(cacheRecord.idToken);
88+
this.saveCredential(cacheRecord.accessToken);
89+
this.saveCredential(cacheRecord.refreshToken);
11190
}
11291

11392
/**
@@ -248,7 +227,6 @@ export class UnifiedCacheManager implements ICacheManager {
248227
getCredentialsFilteredBy(
249228
filter: CredentialFilter
250229
): CredentialCache {
251-
252230
return this.getCredentialsFilteredByInternal(
253231
filter.homeAccountId,
254232
filter.environment,

lib/msal-common/src/unifiedCache/entities/AccessTokenEntity.ts

+12-17
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import { Credential } from "./Credential";
77
import { CredentialType } from "../../utils/Constants";
8-
import { AuthenticationResult } from "../../response/AuthenticationResult";
98

109
/**
1110
* ACCESS_TOKEN Credential Type
@@ -22,22 +21,22 @@ export class AccessTokenEntity extends Credential {
2221

2322
/**
2423
* Create AccessTokenEntity
25-
* @param homeAccountId
26-
* @param authenticationResult
27-
* @param clientId
28-
* @param authority
29-
*/
24+
*/
3025
static createAccessTokenEntity(
3126
homeAccountId: string,
32-
authenticationResult: AuthenticationResult,
27+
environment: string,
28+
accessToken: string,
3329
clientId: string,
34-
environment: string
30+
tenantId: string,
31+
scopes: string,
32+
expiresOn: number,
33+
extExpiresOn: number
3534
): AccessTokenEntity {
3635
const atEntity: AccessTokenEntity = new AccessTokenEntity();
3736

3837
atEntity.homeAccountId = homeAccountId;
3938
atEntity.credentialType = CredentialType.ACCESS_TOKEN;
40-
atEntity.secret = authenticationResult.accessToken;
39+
atEntity.secret = accessToken;
4140

4241
const date = new Date();
4342
const currentTime = date.getMilliseconds() / 1000;
@@ -46,17 +45,13 @@ export class AccessTokenEntity extends Credential {
4645
// TODO: Crosscheck the exact conversion UTC
4746
// Token expiry time.
4847
// This value should be  calculated based on the current UTC time measured locally and the value  expires_in Represented as a string in JSON.
49-
atEntity.expiresOn = authenticationResult.expiresOn
50-
.getMilliseconds()
51-
.toString();
52-
atEntity.extendedExpiresOn = authenticationResult.extExpiresOn
53-
.getMilliseconds()
54-
.toString();
48+
atEntity.expiresOn = expiresOn.toString();
49+
atEntity.extendedExpiresOn = extExpiresOn.toString();
5550

5651
atEntity.environment = environment;
5752
atEntity.clientId = clientId;
58-
atEntity.realm = authenticationResult.tenantId;
59-
atEntity.target = authenticationResult.scopes.join(" ");
53+
atEntity.realm = tenantId;
54+
atEntity.target = scopes;
6055

6156
return atEntity;
6257
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
import { AccountEntity } from "./AccountEntity";
7+
import { IdTokenEntity } from "./IdTokenEntity";
8+
import { AccessTokenEntity } from "./AccessTokenEntity";
9+
import { RefreshTokenEntity } from "./RefreshTokenEntity";
10+
11+
export class CacheRecord {
12+
account: AccountEntity;
13+
idToken: IdTokenEntity;
14+
accessToken: AccessTokenEntity;
15+
refreshToken: RefreshTokenEntity;
16+
}

lib/msal-common/src/unifiedCache/entities/IdTokenEntity.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import { Credential } from "./Credential";
77
import { CredentialType } from "../../utils/Constants";
8-
import { AuthenticationResult } from "../../response/AuthenticationResult";
98

109
/**
1110
* ID_TOKEN Cache
@@ -22,18 +21,19 @@ export class IdTokenEntity extends Credential {
2221
*/
2322
static createIdTokenEntity(
2423
homeAccountId: string,
25-
authenticationResult: AuthenticationResult,
24+
environment: string,
25+
idToken: string,
2626
clientId: string,
27-
environment: string
27+
tenantId: string
2828
): IdTokenEntity {
2929
const idTokenEntity = new IdTokenEntity();
3030

3131
idTokenEntity.credentialType = CredentialType.ID_TOKEN;
3232
idTokenEntity.homeAccountId = homeAccountId;
3333
idTokenEntity.environment = environment;
3434
idTokenEntity.clientId = clientId;
35-
idTokenEntity.secret = authenticationResult.idToken;
36-
idTokenEntity.realm = authenticationResult.tenantId;
35+
idTokenEntity.secret = idToken;
36+
idTokenEntity.realm = tenantId;
3737

3838
return idTokenEntity;
3939
}

0 commit comments

Comments
 (0)