@azure/msal-browser
Version:
Microsoft Authentication Library for js
201 lines (175 loc) • 7.05 kB
text/typescript
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { CustomAuthBrowserConfiguration } from "../../configuration/CustomAuthConfiguration.js";
import { SignOutResult } from "./result/SignOutResult.js";
import { GetAccessTokenResult } from "./result/GetAccessTokenResult.js";
import { CustomAuthSilentCacheClient } from "../interaction_client/CustomAuthSilentCacheClient.js";
import { NoCachedAccountFoundError } from "../../core/error/NoCachedAccountFoundError.js";
import { DefaultScopes } from "../../CustomAuthConstants.js";
import { AccessTokenRetrievalInputs } from "../../CustomAuthActionInputs.js";
import {
AccountInfo,
AuthenticationScheme,
CommonSilentFlowRequest,
Logger,
TokenClaims,
} from "@azure/msal-common/browser";
import { SilentRequest } from "../../../request/SilentRequest.js";
import * as ArgumentValidator from "../../core/utils/ArgumentValidator.js";
/*
* Account information.
*/
export class CustomAuthAccountData {
constructor(
private readonly account: AccountInfo,
private readonly config: CustomAuthBrowserConfiguration,
private readonly cacheClient: CustomAuthSilentCacheClient,
private readonly logger: Logger,
private readonly correlationId: string
) {
ArgumentValidator.ensureArgumentIsNotEmptyString(
"correlationId",
correlationId
);
ArgumentValidator.ensureArgumentIsNotNullOrUndefined(
"account",
account,
correlationId
);
}
/**
* This method triggers a sign-out operation,
* which removes the current account info and its tokens from browser cache.
* If sign-out successfully, redirect the page to postLogoutRedirectUri if provided in the configuration.
* @returns {Promise<SignOutResult>} The result of the SignOut operation.
*/
async signOut(): Promise<SignOutResult> {
try {
const currentAccount = this.cacheClient.getCurrentAccount(
this.correlationId
);
if (!currentAccount) {
throw new NoCachedAccountFoundError(this.correlationId);
}
this.logger.verbose("Signing out user", this.correlationId);
await this.cacheClient.logout({
correlationId: this.correlationId,
account: currentAccount,
});
this.logger.verbose("User signed out", this.correlationId);
return new SignOutResult();
} catch (error) {
this.logger.errorPii(
`An error occurred during sign out: ${error}`,
this.correlationId
);
return SignOutResult.createWithError(error);
}
}
getAccount(): AccountInfo {
return this.account;
}
/**
* Gets the raw id-token of current account.
* Idtoken is only issued if openid scope is present in the scopes parameter when requesting for tokens,
* otherwise will return undefined from the response.
* @returns {string|undefined} The account id-token.
*/
getIdToken(): string | undefined {
return this.account.idToken;
}
/**
* Gets the id token claims extracted from raw IdToken of current account.
* @returns {AuthTokenClaims|undefined} The token claims.
*/
getClaims(): AuthTokenClaims | undefined {
return this.account.idTokenClaims;
}
/**
* Gets the access token of current account from browser cache if it is not expired,
* otherwise renew the token using cached refresh token if valid.
* If no refresh token is found or it is expired, then throws error.
* @param {AccessTokenRetrievalInputs} accessTokenRetrievalInputs - The inputs for retrieving the access token.
* @returns {Promise<GetAccessTokenResult>} The result of the operation.
*/
async getAccessToken(
accessTokenRetrievalInputs: AccessTokenRetrievalInputs
): Promise<GetAccessTokenResult> {
try {
ArgumentValidator.ensureArgumentIsNotNullOrUndefined(
"accessTokenRetrievalInputs",
accessTokenRetrievalInputs,
this.correlationId
);
if (accessTokenRetrievalInputs.claims) {
ArgumentValidator.ensureArgumentIsJSONString(
"accessTokenRetrievalInputs.claims",
accessTokenRetrievalInputs.claims,
this.correlationId
);
}
this.logger.verbose("Getting current account.", this.correlationId);
const currentAccount = this.cacheClient.getCurrentAccount(
this.account.username
);
if (!currentAccount) {
throw new NoCachedAccountFoundError(this.correlationId);
}
this.logger.verbose("Getting access token.", this.correlationId);
const newScopes =
accessTokenRetrievalInputs.scopes &&
accessTokenRetrievalInputs.scopes.length > 0
? accessTokenRetrievalInputs.scopes
: [...DefaultScopes];
const commonSilentFlowRequest = this.createCommonSilentFlowRequest(
currentAccount,
accessTokenRetrievalInputs.forceRefresh,
newScopes,
accessTokenRetrievalInputs.claims
);
const result = await this.cacheClient.acquireToken(
commonSilentFlowRequest
);
this.logger.verbose(
"Successfully got access token from cache.",
this.correlationId
);
return new GetAccessTokenResult(result);
} catch (error) {
this.logger.error(
"Failed to get access token from cache.",
this.correlationId
);
return GetAccessTokenResult.createWithError(error);
}
}
private createCommonSilentFlowRequest(
accountInfo: AccountInfo,
forceRefresh: boolean = false,
requestScopes: Array<string>,
claims?: string
): CommonSilentFlowRequest {
const silentRequest: SilentRequest = {
authority: this.config.auth.authority,
correlationId: this.correlationId,
scopes: requestScopes || [],
account: accountInfo,
forceRefresh: forceRefresh || false,
storeInCache: {
idToken: true,
accessToken: true,
refreshToken: true,
},
...(claims && { claims: claims }),
};
return {
...silentRequest,
authenticationScheme: AuthenticationScheme.BEARER,
} as CommonSilentFlowRequest;
}
}
export type AuthTokenClaims = TokenClaims & {
[key: string]: string | number | string[] | object | undefined | unknown;
};