Skip to content
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

Extend acquireTokenSilent Instrumentation #1629

Merged
merged 14 commits into from
May 19, 2020
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 36 additions & 11 deletions lib/msal-core/src/UserAgentApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -630,32 +630,42 @@ export class UserAgentApplication {
*
*/
acquireTokenSilent(userRequest: AuthenticationParameters): Promise<AuthResponse> {
this.logger.info("AcquireTokenSilent has been called");
jo-arroyo marked this conversation as resolved.
Show resolved Hide resolved
this.logger.verbose(`AcquireTokenSilent params:\n${JSON.stringify(userRequest)}`);
jo-arroyo marked this conversation as resolved.
Show resolved Hide resolved
jo-arroyo marked this conversation as resolved.
Show resolved Hide resolved
// validate the request
const request = RequestUtils.validateRequest(userRequest, false, this.clientId, Constants.interactionTypeSilent);
const apiEvent: ApiEvent = this.telemetryManager.createAndStartApiEvent(request.correlationId, API_EVENT_IDENTIFIER.AcquireTokenSilent, this.logger);
const requestSignature = RequestUtils.createRequestSignature(request);

return new Promise<AuthResponse>((resolve, reject) => {

// block the request if made from the hidden iframe
// block the request if made from the hidden iframe TODO: Ask about why to extend comment
WindowUtils.blockReloadInHiddenIframes();

const scope = request.scopes.join(" ").toLowerCase();
this.logger.verbose(`Serialized scopes: ${scope}`);
jo-arroyo marked this conversation as resolved.
Show resolved Hide resolved

// if the developer passes an account, give that account the priority
const account: Account = request.account || this.getAccount();
this.logger.verbosePii(`Set account to: ${JSON.stringify(account)}`);

// extract if there is an adalIdToken stashed in the cache
// extract if there is an adalIdToken stashed in the cache TODO: Ask about why
jo-arroyo marked this conversation as resolved.
Show resolved Hide resolved
const adalIdToken = this.cacheStorage.getItem(Constants.adalIdToken);

// if there is no account logged in and no login_hint/sid is passed in the request
/**
* if there is no account logged in and no login_hint/sid is passed in the request
jo-arroyo marked this conversation as resolved.
Show resolved Hide resolved
* TODO: consider explaining what sid is
* In the event of no account being passed in the config, no session id, and no pre-existing adalIdToken, user will need to log in
*/
if (!account && !(request.sid || request.loginHint) && StringUtils.isEmpty(adalIdToken) ) {
this.logger.info("User login is required");
// The promise rejects with a UserLoginRequiredError, which should be caught and user should be prompted to log in interactively
return reject(ClientAuthError.createUserLoginRequiredError());
}

// set the response type based on the current cache status / scopes set
const responseType = this.getTokenType(account, request.scopes, true);
jo-arroyo marked this conversation as resolved.
Show resolved Hide resolved
this.logger.verbose(`Response type: ${responseType}`);

// create a serverAuthenticationRequest populating the `queryParameters` to be sent to the Server
const serverAuthenticationRequest = new ServerRequestParameters(
Expand All @@ -668,23 +678,29 @@ export class UserAgentApplication {
request.correlationId,
);

this.logger.verbose(`ServerAuthenticationRequest: ${JSON.stringify(serverAuthenticationRequest)}`);
jo-arroyo marked this conversation as resolved.
Show resolved Hide resolved

// populate QueryParameters (sid/login_hint) and any other extraQueryParameters set by the developer
if (ServerRequestParameters.isSSOParam(request) || account) {
serverAuthenticationRequest.populateQueryParams(account, request, null, true);
jo-arroyo marked this conversation as resolved.
Show resolved Hide resolved
}
// if user didn't pass login_hint/sid and adal's idtoken is present, extract the login_hint from the adalIdToken
// if user didn't pass login_hint/sid and adal's idtoken is present, extract the login_hint from the adalIdToken TODO: ask about why
else if (!account && !StringUtils.isEmpty(adalIdToken)) {
// if adalIdToken exists, extract the SSO info from the same
const adalIdTokenObject = TokenUtils.extractIdToken(adalIdToken);
this.logger.verbose("ADAL's idToken exists. Extracting login information from ADAL's idToken ");
serverAuthenticationRequest.populateQueryParams(account, null, adalIdTokenObject, true);
}

this.logger.verbosePii(`Query Params has been populated: ${serverAuthenticationRequest.queryParameters}`);

// TODO: ask about why/ when they would not be undefined
const userContainedClaims = request.claimsRequest || serverAuthenticationRequest.claimsValue;

let authErr: AuthError;
let cacheResultResponse;

// If request.forceRefresh is set to true, we want to force a request for a new token instead of getting it from the cache TODO:
if (!userContainedClaims && !request.forceRefresh) {
try {
cacheResultResponse = this.getCachedToken(serverAuthenticationRequest, account);
Expand All @@ -695,7 +711,7 @@ export class UserAgentApplication {

// resolve/reject based on cacheResult
if (cacheResultResponse) {
this.logger.info("Token is already in cache for scope:" + scope);
this.logger.info("Token is already in cache for scope: " + scope);
resolve(cacheResultResponse);
return null;
}
Expand All @@ -708,27 +724,33 @@ export class UserAgentApplication {
else {
let logMessage;
if (userContainedClaims) {
logMessage = "Skipped cache lookup since claims were given.";
logMessage = "Skipped cache lookup since claims were given";
} else if (request.forceRefresh) {
logMessage = "Skipped cache lookup since request.forceRefresh option was set to true";
} else {
logMessage = "Token is not in cache for scope:" + scope;
logMessage = "Token is not in cache for scope: " + scope;
jo-arroyo marked this conversation as resolved.
Show resolved Hide resolved
}
this.logger.verbose(logMessage);

// Cache result can return null if cache is empty. In that case, set authority to default value if no authority is passed to the api.
// Cache result can return null if cache is empty. In that case, set authority to default value if no authority is passed to the API.
if (!serverAuthenticationRequest.authorityInstance) {
serverAuthenticationRequest.authorityInstance = request.authority ? AuthorityFactory.CreateInstance(request.authority, this.config.auth.validateAuthority) : this.authorityInstance;
if (!request.authority){
this.logger.verbose("Using default authority since no authority was passed to API");
jo-arroyo marked this conversation as resolved.
Show resolved Hide resolved
}
}
// cache miss

// start http event
// start http event TODO: add why
jo-arroyo marked this conversation as resolved.
Show resolved Hide resolved
return serverAuthenticationRequest.authorityInstance.resolveEndpointsAsync(this.telemetryManager, request.correlationId)
.then(() => {
/*
* refresh attempt with iframe
* Already renewing for this scope, callback when we get the token.
*/
this.logger.verbose(`Updated authority: ${JSON.stringify(serverAuthenticationRequest.authorityInstance)}`);
jo-arroyo marked this conversation as resolved.
Show resolved Hide resolved

// TODO: ask about why
if (window.activeRenewals[requestSignature]) {
this.logger.verbose("Renew token for scope and authority: " + requestSignature + " is in progress. Registering callback");
// Active renewals contains the state for each renewal.
Expand All @@ -740,12 +762,12 @@ export class UserAgentApplication {
* App uses idToken to send to api endpoints
* Default scope is tracked as clientId to store this token
*/
this.logger.verbose("renewing idToken");
this.logger.verbose("renewing idToken"); // TODO: should logs start with caps or lower case?? variable or sentence?
this.silentLogin = true;
this.renewIdToken(requestSignature, resolve, reject, account, serverAuthenticationRequest);
} else {
// renew access token
this.logger.verbose("renewing accesstoken");
this.logger.verbose("renewing access token");
jo-arroyo marked this conversation as resolved.
Show resolved Hide resolved
this.renewToken(requestSignature, resolve, reject, account, serverAuthenticationRequest);
}
}
Expand All @@ -757,12 +779,15 @@ export class UserAgentApplication {
}
})
.then(res => {
this.logger.verbose("Successfully acquired token");
this.telemetryManager.stopAndFlushApiEvent(request.correlationId, apiEvent, true);
this.logger.info("Stopping telemetryManager and flushing API event");
jo-arroyo marked this conversation as resolved.
Show resolved Hide resolved
return res;
})
.catch((error: AuthError) => {
this.cacheStorage.resetTempCacheItems(request.state);
this.telemetryManager.stopAndFlushApiEvent(request.correlationId, apiEvent, false, error.errorCode);
this.logger.info("Stopping telemetryManager and flushing API event with error:");
throw error;
});
}
Expand Down