/*
|
* 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,
|
CommonSilentFlowRequest,
|
Constants,
|
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: Constants.AuthenticationScheme.BEARER,
|
} as CommonSilentFlowRequest;
|
}
|
}
|
|
export type AuthTokenClaims = TokenClaims & {
|
[key: string]: string | number | string[] | object | undefined | unknown;
|
};
|