@microsoft/mgt
Version: 
The Microsoft Graph Toolkit
224 lines • 9.75 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 { UserAgentApplication } from 'msal';
import { LoginType, ProviderState } from './IProvider';
import { MsalProvider } from './MsalProvider';
/**
 * Enables authentication of Single page apps inside of a Microsoft Teams tab
 *
 * @export
 * @class TeamsProvider
 * @extends {MsalProvider}
 */
export class TeamsProvider extends MsalProvider {
    constructor(config) {
        super({
            clientId: config.clientId,
            loginType: LoginType.Redirect,
            options: config.msalOptions,
            scopes: config.scopes
        });
        const teams = TeamsProvider.microsoftTeamsLib || microsoftTeams;
        this._authPopupUrl = config.authPopupUrl;
        teams.initialize();
    }
    /**
     * Gets whether the Teams provider can be used in the current context
     * (Whether the app is running in Microsoft Teams)
     *
     * @readonly
     * @static
     * @memberof TeamsProvider
     */
    static get isAvailable() {
        if (window.parent === window.self && window.nativeInterface) {
            // In Teams mobile client
            return true;
        }
        else if (window.name === 'embedded-page-container' || window.name === 'extension-tab-frame') {
            // In Teams web/desktop client
            return true;
        }
        else {
            return false;
        }
    }
    /**
     * Handle all authentication redirects in the authentication page and authenticates the user
     *
     * @static
     * @returns
     * @memberof TeamsProvider
     */
    static handleAuth() {
        return __awaiter(this, void 0, void 0, function* () {
            // we are in popup world now - authenticate and handle it
            const teams = TeamsProvider.microsoftTeamsLib || microsoftTeams;
            if (!teams) {
                // tslint:disable-next-line: no-console
                console.error('Make sure you have referenced the Microsoft Teams sdk before using the TeamsProvider');
                return;
            }
            teams.initialize();
            // msal checks for the window.opener.msal to check if this is a popup authentication
            // and gets a false positive since teams opens a popup for the authentication.
            // in reality, we are doing a redirect authentication and need to act as if this is the
            // window initiating the authentication
            if (window.opener) {
                window.opener.msal = null;
            }
            const url = new URL(window.location.href);
            const paramsString = sessionStorage.getItem(this._sessionStorageParametersKey);
            let authParams;
            if (paramsString) {
                authParams = JSON.parse(paramsString);
            }
            else {
                authParams = {};
            }
            if (!authParams.clientId) {
                authParams.clientId = url.searchParams.get('clientId');
                authParams.scopes = url.searchParams.get('scopes');
                authParams.loginHint = url.searchParams.get('loginHint');
                sessionStorage.setItem(this._sessionStorageParametersKey, JSON.stringify(authParams));
            }
            if (!authParams.clientId) {
                teams.authentication.notifyFailure('no clientId provided');
                return;
            }
            const scopes = authParams.scopes ? authParams.scopes.split(',') : null;
            const provider = new MsalProvider({
                clientId: authParams.clientId,
                options: {
                    auth: {
                        clientId: authParams.clientId,
                        redirectUri: url.protocol + '//' + url.host + url.pathname
                    },
                    system: {
                        loadFrameTimeout: 10000
                    }
                },
                scopes
            });
            if (UserAgentApplication.prototype.urlContainsHash(window.location.hash)) {
                // the page should redirect again
                return;
            }
            const handleProviderState = () => __awaiter(this, void 0, void 0, function* () {
                // how do we handle when user can't sign in
                // change to promise and return status
                if (provider.state === ProviderState.SignedOut) {
                    provider.login({
                        loginHint: authParams.loginHint,
                        scopes: scopes || provider.scopes
                    });
                }
                else if (provider.state === ProviderState.SignedIn) {
                    try {
                        const accessToken = yield provider.getAccessTokenForScopes(...provider.scopes);
                        sessionStorage.removeItem(this._sessionStorageParametersKey);
                        teams.authentication.notifySuccess(accessToken);
                    }
                    catch (e) {
                        sessionStorage.removeItem(this._sessionStorageParametersKey);
                        teams.authentication.notifyFailure(e);
                    }
                }
            });
            provider.onStateChanged(handleProviderState);
            handleProviderState();
        });
    }
    /**
     * Opens the teams authentication popup to the authentication page
     *
     * @returns {Promise<void>}
     * @memberof TeamsProvider
     */
    login() {
        return __awaiter(this, void 0, void 0, function* () {
            this.setState(ProviderState.Loading);
            const teams = TeamsProvider.microsoftTeamsLib || microsoftTeams;
            return new Promise((resolve, reject) => {
                teams.getContext(context => {
                    this.teamsContext = context;
                    const url = new URL(this._authPopupUrl, new URL(window.location.href));
                    url.searchParams.append('clientId', this.clientId);
                    if (context.loginHint) {
                        url.searchParams.append('loginHint', context.loginHint);
                    }
                    if (this.scopes) {
                        url.searchParams.append('scopes', this.scopes.join(','));
                    }
                    teams.authentication.authenticate({
                        failureCallback: reason => {
                            this.setState(ProviderState.SignedOut);
                            reject();
                        },
                        successCallback: result => {
                            this.setState(ProviderState.SignedIn);
                            resolve();
                        },
                        url: url.href
                    });
                });
            });
        });
    }
    /**
     * Returns an access token that can be used for making calls to the Microsoft Graph
     *
     * @param {AuthenticationProviderOptions} options
     * @returns {Promise<string>}
     * @memberof TeamsProvider
     */
    getAccessToken(options) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!this.teamsContext) {
                const teams = TeamsProvider.microsoftTeamsLib || microsoftTeams;
                this.teamsContext = yield teams.getContext();
            }
            const scopes = options ? options.scopes || this.scopes : this.scopes;
            const accessTokenRequest = {
                scopes
            };
            if (this.teamsContext && this.teamsContext.loginHint) {
                accessTokenRequest.loginHint = this.teamsContext.loginHint;
            }
            const currentParent = window.parent;
            if (document.referrer.startsWith('https://teams.microsoft.com/')) {
                window.parent = window;
            }
            try {
                const response = yield this._userAgentApplication.acquireTokenSilent(accessTokenRequest);
                window.parent = currentParent;
                return response.accessToken;
            }
            catch (e) {
                window.parent = currentParent;
                if (this.requiresInteraction(e)) {
                    // nothing we can do now until we can do incremental consent
                    return null;
                }
                else {
                    throw e;
                }
            }
        });
    }
}
TeamsProvider._sessionStorageParametersKey = 'msg-teamsprovider-auth-parameters';
//# sourceMappingURL=TeamsProvider.js.map