AzureAD / microsoft-authentication-library-for-js

Microsoft Authentication Library (MSAL) for JS
http://aka.ms/aadv2
MIT License
3.64k stars 2.65k forks source link

MSAL authentication failing in Teams Tab #3525

Closed NewReactUser28 closed 3 years ago

NewReactUser28 commented 3 years ago

Core Library

@azure/msal-browser

Core Library Version

2.13.1

Wrapper Library

@azure/msal-react

Wrapper Library Version

2.13.1

Description

We are trying to extend our SPFx Webparts in Teams tab. this.myMSALObj.getAccountByUsername(this.userName) During MSAL authentication, code is failing when we are trying to obtain the signed in account matching the username. We noticed the account info returned is null.

And we are getting the below error: BrowserAuthError: no_account_error: No account object provided to acquireTokenSilent and no active account has been set. Please call setActiveAccount or provide an account on the request.

Error Message

BrowserAuthError: no_account_error: No account object provided to acquireTokenSilent and no active account has been set. Please call setActiveAccount or provide an account on the request.

Msal Logs

No response

MSAL Configuration

const msalConfig = {
    auth: {
        authority: `https://login.microsoftonline.com/microsoft.onmicrosoft.com`,
        clientId: config.default.AppSettings.msalClientId,
        redirectUri: loc
    },
    cache: {
        cacheLocation: 'localStorage',
        storeAuthStateInCookie: false
    }
};

Relevant Code Snippets

constructor(wpContext: any, AuthContextCallback?: any, username?: any, isLogin?: boolean) {
        this.httpClient = wpContext.httpClient;
        this.userName = username;
        this.myMSALObj = new PublicClientApplication(msalConfig);
    }

    public async Authenticate(isLogin?: boolean) {
        await this.myMSALObj
            .handleRedirectPromise()
            .then(async (tokenResponse) => {
                if (tokenResponse !== null) {
                    this.accessTokenCommon = tokenResponse.accessToken;
                } else {
                    if (isLogin) {
                        await this.loginForAccessTokenByMSAL();
                    } else {
                        const currentAccounts = this.myMSALObj.getAllAccounts();
                        await this.handleLoggedInUser(currentAccounts);
                    }
                }
            })
            .catch((error) => {
                return null;
            });
    }

public GetCustomWebAPI(apiSource: APISource, apiRelativePath: string, jsonbody: string = null): Promise<any> {
        return new Promise<any>((resolve: (result: any) => void, reject: (error: any) => void): void => {
            this.getCustomAPIAccessToken(apiSource).then(
                async (accessToken: string): Promise<void> => {
                    let fullAPIUrl: string;
                    const requestHeaders: Headers = new Headers();
                    requestHeaders.append('Authorization', 'Bearer ' + accessToken);
                    requestHeaders.append('Content-type', 'application/json');
                    const httpClientOptions: IHttpClientOptions = {
                        body: jsonbody,
                        headers: requestHeaders
                    };
                    try {
                        if (apiSource == APISource.MSGraph) fullAPIUrl = apiSource + '/' + apiRelativePath;
                        else fullAPIUrl = this.getAPIBaseURL(apiSource) + apiRelativePath;
                        const response = await this.httpClient.get(
                            fullAPIUrl,
                            HttpClient.configurations.v1,
                            httpClientOptions
                        );
                        const output = await response.json();
                        resolve(output);
                    } catch (error) {
                        reject(error);
                    }
                },
                (error: any): void => {
                    reject('token error' + error);
                }
            );
        });
    }

private async fetchAccessTokenByScopeMSAL(specificScope: any): Promise<string> {
        const accessTokenRequest = {
            scopes: specificScope,
            account: this.myMSALObj.getAccountByUsername(this.userName)
        };
        this.ssoRequest.loginHint = this.userName;
        this.ssoRequest.scopes = specificScope;
        return this.myMSALObj
            .acquireTokenSilent(accessTokenRequest)
            .then((response) => {
                return response.accessToken;
            })
            .catch((silentError) => {
                if (silentError instanceof InteractionRequiredAuthError) {
                    return this.myMSALObj
                        .loginPopup()
                        .then((response) => {
                            return response.accessToken;
                        })
                        .catch((popupError) => {
                            if (popupError.message.indexOf('popup_window_error') > -1) {
                                // Popups are blocked
                                return this.redirectLogin(this.ssoRequest);
                            }
                        });
                } else {
                    return null;
                }
            });
    }

private getCustomAPIAccessToken(apiSource: APISource): Promise<string> {
        return new Promise<
            string
        >(async (resolve: (accessToken: string) => void, reject: (error: any) => void): Promise<void> => {
            try {
                var accessToken = null;

                accessToken = await this.fetchAccessTokenByScopeMSAL(allScopes.customAPIScopes);
                if (accessToken) {
                    resolve(accessToken);
                } else {
                    reject("Couldn't retrieve access token");
                }
            } catch (ex) {
                reject(ex);
            }
        });
    }

Reproduction Steps

  1. Build an SPFx Webpart Solution with MSAL authentication.2.
  2. Build a manifest file for teams support and deploy it in TEAMS as a tab.
  3. Initiate multiple custom api calls on page login, this triggers multiple calls to fetch tokens for each custom webapi.

Expected Behavior

MSAL authentication must be successful and an access token must be returned.

Identity Provider

Other

Browsers Affected (Select all that apply)

Edge

Regression

NA

Source

Internal (Microsoft)

jasonnutter commented 3 years ago

@NewReactUser28 SSO in Teams Tab has different considerations than a normal webpage. Please see this sample for how to SSO in Teams when you don't have a cached account: https://github.com/pnp/teams-dev-samples/tree/master/samples/tab-sso/src/nodejs

hectormmg commented 3 years ago

@NewReactUser28 could you please let us know if the sample helped resolve your issue? Thanks.

svrooij commented 3 years ago

@jasonnutter it would however be very nice if the sso in teams could somehow be integrated in MSAL-angular, but that might be a completely new issue.

ghost commented 3 years ago

NewReactUser28 This issue has been automatically marked as stale because it is marked as requiring author feedback but has not had any activity for 5 days. If your issue has not been resolved please leave a comment to keep this open. It will be closed in 7 days if it remains stale.