UNPKG

@mattbillfred/mgt-msal2-provider

Version:
515 lines 18.9 kB
/** * ------------------------------------------------------------------------------------------- * 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