@mattbillfred/mgt-msal2-provider
Version:
The Microsoft Graph Toolkit Msal 2.0 Provider
515 lines • 18.9 kB
JavaScript
/**
* -------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
* See License in the project root for license information.
* -------------------------------------------------------------------------------------------
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { IProvider, LoginType, ProviderState, createFromProvider, error } from '@microsoft/mgt-element';
import { PublicClientApplication, InteractionRequiredAuthError } from '@azure/msal-browser';
/**
* Prompt type enum
*
* @export
* @enum {number}
*/
export var PromptType;
(function (PromptType) {
PromptType["SELECT_ACCOUNT"] = "select_account";
PromptType["LOGIN"] = "login";
PromptType["CONSENT"] = "consent";
})(PromptType || (PromptType = {}));
/**
* MSAL2Provider using msal-browser to acquire tokens for authentication
*
* @export
* @class Msal2Provider
* @extends {IProvider}
*/
export class Msal2Provider extends IProvider {
/**
* Gets the PublicClientApplication Instance
*
* @private
* @type {PublicClientApplication}
* @memberof Msal2Provider
*/
get publicClientApplication() {
return this._publicClientApplication;
}
/**
* Name used for analytics
*
* @readonly
* @memberof IProvider
*/
get name() {
return 'MgtMsal2Provider';
}
/**
* Indicates if multi account functionality is disabled
*
* @protected
* @type {boolean}
* @memberof Msal2Provider
*/
get isMultiAccountDisabled() {
return !this.isMultipleAccountEnabled;
}
/**
* Disables or enables multi account functionality
* Uses isMultipleAccountEnabled as the backing property
* Property provided to ensure adherence to the IProvider interface
*
* @protected
* @memberof Msal2Provider
*/
set isMultiAccountDisabled(value) {
this.isMultipleAccountEnabled = !value;
}
/**
* Specifies if Multi account functionality is supported by the provider and enabled.
*
* @readonly
* @type {boolean}
* @memberof IProvider
*/
get isMultiAccountSupportedAndEnabled() {
return this.isMultipleAccountEnabled;
}
get sessionStorageRequestedScopesKey() {
return 'mgt-requested-scopes';
}
get sessionStorageDeniedScopesKey() {
return 'mgt-denied-scopes';
}
get homeAccountKey() {
return '275f3731-e4a4-468a-bf9c-baca24b31e26';
}
constructor(config) {
super();
/**
* Enables multi account functionality if true, disables if false
*
* @private
* @type {boolean}
* @memberof Msal2Provider
*/
this.isMultipleAccountEnabled = true;
void this.initProvider(config);
}
/**
* Initialize provider with configuration details
*
* @private
* @param {Msal2Config} config
* @memberof Msal2Provider
*/
initProvider(config) {
return __awaiter(this, void 0, void 0, function* () {
const msalConfig = config.options || { auth: { clientId: '' } };
this.ms_config = msalConfig;
this.ms_config.cache = msalConfig.cache || {};
this.ms_config.cache.cacheLocation = msalConfig.cache.cacheLocation || 'localStorage';
if (typeof this.ms_config.cache.storeAuthStateInCookie === 'undefined' ||
this.ms_config.cache.storeAuthStateInCookie === null) {
this.ms_config.cache.storeAuthStateInCookie = true;
}
this.ms_config.system = msalConfig.system || {};
this.ms_config.system.iframeHashTimeout = msalConfig.system.iframeHashTimeout || 10000;
if (config.authority) {
this.ms_config.auth.authority = config.authority;
}
if (config.redirectUri) {
this.ms_config.auth.redirectUri = config.redirectUri;
}
if ('clientId' in config) {
if (config.clientId) {
this.ms_config.auth.clientId = config.clientId;
this._publicClientApplication = new PublicClientApplication(this.ms_config);
}
else {
throw new Error('clientId must be provided');
}
}
else if ('publicClientApplication' in config) {
if (config.publicClientApplication) {
this._publicClientApplication = config.publicClientApplication;
}
else {
throw new Error('publicClientApplication must be provided');
}
}
else {
throw new Error('either clientId or publicClientApplication must be provided');
}
yield this._publicClientApplication.initialize();
this.ms_config.system = msalConfig.system || {};
this.ms_config.system.iframeHashTimeout = msalConfig.system.iframeHashTimeout || 10000;
this._loginType = typeof config.loginType !== 'undefined' ? config.loginType : LoginType.Redirect;
this._loginHint = typeof config.loginHint !== 'undefined' ? config.loginHint : null;
this._sid = typeof config.sid !== 'undefined' ? config.sid : null;
this.isIncrementalConsentDisabled =
typeof config.isIncrementalConsentDisabled !== 'undefined' ? config.isIncrementalConsentDisabled : false;
this._domainHint = typeof config.domainHint !== 'undefined' ? config.domainHint : null;
this.scopes = typeof config.scopes !== 'undefined' ? config.scopes : ['user.read'];
this._prompt = typeof config.prompt !== 'undefined' ? config.prompt : PromptType.SELECT_ACCOUNT;
const msal2config = config;
this.isMultipleAccountEnabled =
typeof msal2config.isMultiAccountEnabled !== 'undefined' ? msal2config.isMultiAccountEnabled : true;
this.baseURL = typeof msal2config.baseURL !== 'undefined' ? msal2config.baseURL : this.baseURL;
this.customHosts = msal2config.customHosts;
this.graph = createFromProvider(this);
try {
const tokenResponse = yield this._publicClientApplication.handleRedirectPromise();
if (tokenResponse !== null) {
this.handleResponse(tokenResponse === null || tokenResponse === void 0 ? void 0 : tokenResponse.account);
}
else {
yield this.trySilentSignIn();
}
}
catch (e) {
error('Problem attempting to sign in', e);
throw e;
}
});
}
/**
* Attempts to sign in user silently
*
* @memberof Msal2Provider
*/
trySilentSignIn() {
return __awaiter(this, void 0, void 0, function* () {
const silentRequest = {
scopes: this.scopes,
domainHint: this._domainHint
};
if (this._sid || this._loginHint) {
silentRequest.sid = this._sid;
silentRequest.loginHint = this._loginHint;
try {
this.setState(ProviderState.Loading);
const response = yield this._publicClientApplication.ssoSilent(silentRequest);
if (response) {
this.handleResponse(response === null || response === void 0 ? void 0 : response.account);
}
}
catch (e) {
this.setState(ProviderState.SignedOut);
}
}
else {
const account = this.getAccount();
if (account) {
if (yield this.getAccessToken(null)) {
this.handleResponse(account);
return;
}
}
this.setState(ProviderState.SignedOut);
}
});
}
/**
* Log in the user
*
* @return {*} {Promise<void>}
* @memberof Msal2Provider
*/
login() {
return __awaiter(this, void 0, void 0, function* () {
const loginRequest = {
scopes: this.scopes,
loginHint: this._loginHint,
prompt: this._prompt,
domainHint: this._domainHint
};
if (this._loginType === LoginType.Popup) {
const response = yield this._publicClientApplication.loginPopup(loginRequest);
this.handleResponse(response === null || response === void 0 ? void 0 : response.account);
}
else {
const loginRedirectRequest = Object.assign({}, loginRequest);
yield this._publicClientApplication.loginRedirect(loginRedirectRequest);
}
});
}
/**
* Get all signed in accounts
*
* @return {*}
* @memberof Msal2Provider
*/
getAllAccounts() {
const usernames = [];
this._publicClientApplication.getAllAccounts().forEach((account) => {
usernames.push({ name: account.name, mail: account.username, id: account.homeAccountId });
});
return usernames;
}
/**
* Switching between accounts
*
* @param {*} user
* @memberof Msal2Provider
*/
setActiveAccount(user) {
this._publicClientApplication.setActiveAccount(this._publicClientApplication.getAccountByHomeId(user.id));
this.setStoredAccount();
super.setActiveAccount(user);
}
/**
* Gets active account
*
* @return {*}
* @memberof Msal2Provider
*/
getActiveAccount() {
const account = this._publicClientApplication.getActiveAccount();
return {
name: account.name,
mail: account.username,
id: account.homeAccountId,
tenantId: account.tenantId
};
}
/**
* Once a succesful login occurs, set the active account and store it
*
* @param {(AuthenticationResult | null)} account
* @memberof Msal2Provider
*/
handleResponse(account) {
if (account !== null) {
this.setActiveAccount({
name: account.name,
id: account.homeAccountId,
mail: account.username
});
this.setState(ProviderState.SignedIn);
}
else {
this.setState(ProviderState.SignedOut);
}
this.clearRequestedScopes();
}
storage() {
switch (this.ms_config.cache.cacheLocation) {
case 'localStorage':
return window.localStorage;
case 'sessionStorage':
default:
return window.sessionStorage;
}
}
/**
* Store the currently signed in account in storage
*
* @private
* @memberof Msal2Provider
*/
setStoredAccount() {
this.clearStoredAccount();
this.storage().setItem(this.homeAccountKey, this._publicClientApplication.getActiveAccount().homeAccountId);
}
/**
* Get the stored account from storage
*
* @private
* @return {*}
* @memberof Msal2Provider
*/
getStoredAccount() {
const homeId = this.storage().getItem(this.homeAccountKey);
return this._publicClientApplication.getAccountByHomeId(homeId);
}
/**
* Clears the stored account from storage
*
* @private
* @memberof Msal2Provider
*/
clearStoredAccount() {
this.storage().removeItem(this.homeAccountKey);
}
/**
* Adds scopes that have already been requested to sessionstorage
*
* @protected
* @param {string[]} scopes
* @memberof Msal2Provider
*/
setRequestedScopes(scopes) {
if (scopes) {
sessionStorage.setItem(this.sessionStorageRequestedScopesKey, JSON.stringify(scopes));
}
}
/**
* Adds denied scopes to session storage
*
* @protected
* @param {string[]} scopes
* @memberof Msal2Provider
*/
addDeniedScopes(scopes) {
if (scopes) {
let deniedScopes = this.getDeniedScopes() || [];
deniedScopes = deniedScopes.concat(scopes);
let index = deniedScopes.indexOf('openid');
if (index !== -1) {
deniedScopes.splice(index, 1);
}
index = deniedScopes.indexOf('profile');
if (index !== -1) {
deniedScopes.splice(index, 1);
}
sessionStorage.setItem(this.sessionStorageDeniedScopesKey, JSON.stringify(deniedScopes));
}
}
/**
* Gets denied scopes
*
* @protected
* @return {*}
* @memberof Msal2Provider
*/
getDeniedScopes() {
const scopesStr = sessionStorage.getItem(this.sessionStorageDeniedScopesKey);
return scopesStr ? JSON.parse(scopesStr) : null;
}
/**
* Checks if scopes were denied previously
*
* @protected
* @param {string[]} scopes
* @return {*}
* @memberof Msal2Provider
*/
areScopesDenied(scopes) {
if (scopes) {
const deniedScopes = this.getDeniedScopes();
if (deniedScopes && deniedScopes.filter(s => -1 !== scopes.indexOf(s)).length > 0) {
return true;
}
}
return false;
}
/**
* Clears all requested scopes from session storage
*
* @protected
* @memberof Msal2Provider
*/
clearRequestedScopes() {
sessionStorage.removeItem(this.sessionStorageRequestedScopesKey);
}
/**
* Gets stored account if available, otherwise fetches the first account in the list of signed in accounts
*
* @private
* @return {*} {(AccountInfo | null)}
* @memberof Msal2Provider
*/
getAccount() {
const account = this.getStoredAccount();
if (account) {
return account;
}
else if (this._publicClientApplication.getAllAccounts().length > 0) {
return this._publicClientApplication.getAllAccounts()[0];
}
return null;
}
/**
* Logs out user
*
* @memberof Msal2Provider
*/
logout() {
return __awaiter(this, void 0, void 0, function* () {
const logOutAccount = this._publicClientApplication.getActiveAccount();
const logOutRequest = {
account: logOutAccount
};
this.clearStoredAccount();
if (this._loginType === LoginType.Redirect) {
this.setState(ProviderState.SignedOut);
yield this._publicClientApplication.logoutRedirect(logOutRequest);
}
else {
yield this._publicClientApplication.logoutPopup(Object.assign({}, logOutRequest));
if (this._publicClientApplication.getAllAccounts.length === 1 || !this.isMultipleAccountEnabled) {
this.setState(ProviderState.SignedOut);
}
else {
yield this.trySilentSignIn();
}
}
});
}
/**
* Returns access token for scopes
*
* @param {AuthenticationProviderOptions} [options]
* @return {*} {Promise<string>}
* @memberof Msal2Provider
*/
getAccessToken(options) {
return __awaiter(this, void 0, void 0, function* () {
const scopes = options ? options.scopes || this.scopes : this.scopes;
const accessTokenRequest = {
scopes,
account: this.getAccount()
};
try {
const silentRequest = accessTokenRequest;
const response = yield this._publicClientApplication.acquireTokenSilent(silentRequest);
return response.accessToken;
}
catch (e) {
if (e instanceof InteractionRequiredAuthError) {
if (this.isIncrementalConsentDisabled) {
return null;
}
if (this._loginType === LoginType.Redirect) {
if (!this.areScopesDenied(scopes)) {
this.setRequestedScopes(scopes);
yield this._publicClientApplication.acquireTokenRedirect(accessTokenRequest);
}
else {
throw e;
}
}
else {
try {
const response = yield this._publicClientApplication.acquireTokenPopup(accessTokenRequest);
return response.accessToken;
}
catch (popUpErr) {
error('problem with pop-up sign in', popUpErr);
throw popUpErr;
}
}
}
else {
// if we don't know what the error is, just ask the user to sign in again
this.setState(ProviderState.SignedOut);
}
}
// TODO: work out if we can throw something more meaningful here
// eslint-disable-next-line no-throw-literal
throw null;
});
}
}
//# sourceMappingURL=Msal2Provider.js.map