Skip to content

[msal-node] Cache Lookup - 3: Msal node cache response #1680

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 8 commits into from
Jun 3, 2020
2 changes: 1 addition & 1 deletion lib/msal-browser/src/cache/BrowserStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ export class BrowserStorage implements ICacheStorage {
/**
* Dummy implementation until browser cache is migrated
*/
setCache() {
setCache(): void {
// sets nothing
}
}
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;
}
125 changes: 46 additions & 79 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,84 +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 accountEntity: AccountEntity = this.generateAccountEntity(
serverTokenResponse,
idToken,
authority
);

const cachedAccount: AccountEntity = this.uCacheManager.getAccount(accountEntity.generateAccountKey());
if (!cachedAccount) {
this.uCacheManager.addAccountEntity(accountEntity);
}
return authenticationResult;
}

/**
Expand All @@ -208,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;
}
}
44 changes: 11 additions & 33 deletions lib/msal-common/src/unifiedCache/UnifiedCacheManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -248,7 +227,6 @@ export class UnifiedCacheManager implements ICacheManager {
getCredentialsFilteredBy(
filter: CredentialFilter
): CredentialCache {

return this.getCredentialsFilteredByInternal(
filter.homeAccountId,
filter.environment,
Expand Down
29 changes: 12 additions & 17 deletions lib/msal-common/src/unifiedCache/entities/AccessTokenEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import { Credential } from "./Credential";
import { CredentialType } from "../../utils/Constants";
import { AuthenticationResult } from "../../response/AuthenticationResult";

/**
* ACCESS_TOKEN Credential Type
Expand All @@ -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;
Expand All @@ -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;
}
Expand Down
16 changes: 16 additions & 0 deletions lib/msal-common/src/unifiedCache/entities/CacheRecord.ts
Original file line number Diff line number Diff line change
@@ -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;
}
10 changes: 5 additions & 5 deletions lib/msal-common/src/unifiedCache/entities/IdTokenEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import { Credential } from "./Credential";
import { CredentialType } from "../../utils/Constants";
import { AuthenticationResult } from "../../response/AuthenticationResult";

/**
* ID_TOKEN Cache
Expand All @@ -22,18 +21,19 @@ 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();

idTokenEntity.credentialType = CredentialType.ID_TOKEN;
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;
}
Expand Down
Loading