@microsoft/mgt
Version: 
The Microsoft Graph Toolkit
292 lines • 10.8 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 { Graph } from '../Graph';
import { IProvider, LoginType, ProviderState } from './IProvider';
import { UserAgentApplication } from 'msal';
/**
 * Msal Provider using MSAL.js to aquire tokens for authentication
 *
 * @export
 * @class MsalProvider
 * @extends {IProvider}
 */
export class MsalProvider extends IProvider {
    constructor(config) {
        super();
        // session storage
        this.sessionStorageRequestedScopesKey = 'mgt-requested-scopes';
        this.sessionStorageDeniedScopesKey = 'mgt-denied-scopes';
        this.initProvider(config);
    }
    /**
     * simplified form of single sign-on (SSO)
     *
     * @returns
     * @memberof MsalProvider
     */
    trySilentSignIn() {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                if (this._userAgentApplication.isCallback(window.location.hash)) {
                    return;
                }
                if (this._userAgentApplication.getAccount() && (yield this.getAccessToken(null))) {
                    this.setState(ProviderState.SignedIn);
                }
                else {
                    this.setState(ProviderState.SignedOut);
                }
            }
            catch (e) {
                this.setState(ProviderState.SignedOut);
            }
        });
    }
    /**
     * login auth Promise, Redirects request, and sets Provider state to SignedIn if response is recieved
     *
     * @param {AuthenticationParameters} [authenticationParameters]
     * @returns {Promise<void>}
     * @memberof MsalProvider
     */
    login(authenticationParameters) {
        return __awaiter(this, void 0, void 0, function* () {
            const loginRequest = authenticationParameters || {
                loginHint: this._loginHint,
                prompt: 'select_account',
                scopes: this.scopes
            };
            if (this._loginType === LoginType.Popup) {
                const response = yield this._userAgentApplication.loginPopup(loginRequest);
                this.setState(response.account ? ProviderState.SignedIn : ProviderState.SignedOut);
            }
            else {
                this._userAgentApplication.loginRedirect(loginRequest);
            }
        });
    }
    /**
     * logout auth Promise, sets Provider state to SignedOut
     *
     * @returns {Promise<void>}
     * @memberof MsalProvider
     */
    logout() {
        return __awaiter(this, void 0, void 0, function* () {
            this._userAgentApplication.logout();
            this.setState(ProviderState.SignedOut);
        });
    }
    /**
     * recieves acess token Promise
     *
     * @param {AuthenticationProviderOptions} options
     * @returns {Promise<string>}
     * @memberof MsalProvider
     */
    getAccessToken(options) {
        return __awaiter(this, void 0, void 0, function* () {
            const scopes = options ? options.scopes || this.scopes : this.scopes;
            const accessTokenRequest = {
                loginHint: this._loginHint,
                scopes
            };
            try {
                const response = yield this._userAgentApplication.acquireTokenSilent(accessTokenRequest);
                return response.accessToken;
            }
            catch (e) {
                if (this.requiresInteraction(e)) {
                    if (this._loginType === LoginType.Redirect) {
                        // check if the user denied the scope before
                        if (!this.areScopesDenied(scopes)) {
                            this.setRequestedScopes(scopes);
                            this._userAgentApplication.acquireTokenRedirect(accessTokenRequest);
                        }
                        else {
                            throw e;
                        }
                    }
                    else {
                        try {
                            const response = yield this._userAgentApplication.acquireTokenPopup(accessTokenRequest);
                            return response.accessToken;
                        }
                        catch (e) {
                            throw e;
                        }
                    }
                }
                else {
                    // if we don't know what the error is, just ask the user to sign in again
                    this.setState(ProviderState.SignedOut);
                    throw e;
                }
            }
            throw null;
        });
    }
    /**
     * sets scopes
     *
     * @param {string[]} scopes
     * @memberof MsalProvider
     */
    updateScopes(scopes) {
        this.scopes = scopes;
    }
    /**
     * if login runs into error, require user interaction
     *
     * @protected
     * @param {*} error
     * @returns
     * @memberof MsalProvider
     */
    requiresInteraction(error) {
        if (!error || !error.errorCode) {
            return false;
        }
        return (error.errorCode.indexOf('consent_required') !== -1 ||
            error.errorCode.indexOf('interaction_required') !== -1 ||
            error.errorCode.indexOf('login_required') !== -1);
    }
    /**
     * setting scopes in sessionStorage
     *
     * @protected
     * @param {string[]} scopes
     * @memberof MsalProvider
     */
    setRequestedScopes(scopes) {
        if (scopes) {
            sessionStorage.setItem(this.sessionStorageRequestedScopesKey, JSON.stringify(scopes));
        }
    }
    /**
     * getting scopes from sessionStorage if they exist
     *
     * @protected
     * @returns
     * @memberof MsalProvider
     */
    getRequestedScopes() {
        const scopesStr = sessionStorage.getItem(this.sessionStorageRequestedScopesKey);
        return scopesStr ? JSON.parse(scopesStr) : null;
    }
    /**
     * clears requested scopes from sessionStorage
     *
     * @protected
     * @memberof MsalProvider
     */
    clearRequestedScopes() {
        sessionStorage.removeItem(this.sessionStorageRequestedScopesKey);
    }
    /**
     * sets Denied scopes to sessionStoage
     *
     * @protected
     * @param {string[]} scopes
     * @memberof MsalProvider
     */
    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 deniedScopes from sessionStorage
     *
     * @protected
     * @returns
     * @memberof MsalProvider
     */
    getDeniedScopes() {
        const scopesStr = sessionStorage.getItem(this.sessionStorageDeniedScopesKey);
        return scopesStr ? JSON.parse(scopesStr) : null;
    }
    /**
     * if scopes are denied
     *
     * @protected
     * @param {string[]} scopes
     * @returns
     * @memberof MsalProvider
     */
    areScopesDenied(scopes) {
        if (scopes) {
            const deniedScopes = this.getDeniedScopes();
            if (deniedScopes && deniedScopes.filter(s => -1 !== scopes.indexOf(s)).length > 0) {
                return true;
            }
        }
        return false;
    }
    initProvider(config) {
        this.scopes = typeof config.scopes !== 'undefined' ? config.scopes : ['user.read'];
        this._loginType = typeof config.loginType !== 'undefined' ? config.loginType : LoginType.Redirect;
        this._loginHint = config.loginHint;
        const tokenReceivedCallbackFunction = ((response) => {
            this.tokenReceivedCallback(response);
        }).bind(this);
        const errorReceivedCallbackFunction = ((authError, accountState) => {
            this.errorReceivedCallback(authError, status);
        }).bind(this);
        if (config.clientId) {
            const msalConfig = config.options || { auth: { clientId: config.clientId } };
            msalConfig.auth.clientId = config.clientId;
            msalConfig.cache = msalConfig.cache || {};
            msalConfig.cache.cacheLocation = msalConfig.cache.cacheLocation || 'localStorage';
            msalConfig.cache.storeAuthStateInCookie = msalConfig.cache.storeAuthStateInCookie || true;
            if (config.authority) {
                msalConfig.auth.authority = config.authority;
            }
            this.clientId = config.clientId;
            this._userAgentApplication = new UserAgentApplication(msalConfig);
            this._userAgentApplication.handleRedirectCallback(tokenReceivedCallbackFunction, errorReceivedCallbackFunction);
        }
        else {
            throw new Error('clientId must be provided');
        }
        this.graph = new Graph(this);
        this.trySilentSignIn();
    }
    tokenReceivedCallback(response) {
        if (response.tokenType === 'id_token') {
            this.setState(ProviderState.SignedIn);
        }
        this.clearRequestedScopes();
    }
    errorReceivedCallback(authError, accountState) {
        const requestedScopes = this.getRequestedScopes();
        if (requestedScopes) {
            this.addDeniedScopes(requestedScopes);
        }
        this.clearRequestedScopes();
    }
}
//# sourceMappingURL=MsalProvider.js.map