Skip to content

Commit 9447c8e

Browse files
Updating to msal.js 1.3 and fixing teams provider redirect loop issue (#381)
* updated to msal 1.3.beta and fixed teams provider auth issue * added support for sign out * minor improvement to auth.html * Update to msal 1.3.0 Co-authored-by: Shane Weaver <[email protected]>
1 parent 1989897 commit 9447c8e

File tree

4 files changed

+91
-66
lines changed

4 files changed

+91
-66
lines changed

auth.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
<html>
22
<head>
33
<script src="https://unpkg.com/@microsoft/teams-js/dist/MicrosoftTeams.min.js" crossorigin="anonymous"></script>
4+
<!-- <script src="./dist/bundle/mgt-loader.js"></script>
5+
<script>
6+
mgt.TeamsProvider.handleAuth();
7+
</script> -->
48
<script type="module">
59
import { TeamsProvider } from './dist/es6/providers/TeamsProvider.js';
610
TeamsProvider.handleAuth();
711
</script>
812
</head>
9-
<body></body>
13+
<body>
14+
Signing You In...
15+
</body>
1016
</html>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
"@microsoft/microsoft-graph-types": "^1.12.0",
6363
"@microsoft/microsoft-graph-types-beta": "github:microsoftgraph/msgraph-typescript-typings#beta",
6464
"lit-element": "^2.3.1",
65-
"msal": "1.1.3",
65+
"msal": "^1.3.0",
6666
"office-ui-fabric-core": "11.0.0"
6767
},
6868
"devDependencies": {

src/providers/MsalProvider.ts

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*/
77

88
import { AuthenticationProviderOptions } from '@microsoft/microsoft-graph-client/lib/es/IAuthenticationProviderOptions';
9-
import { createFromProvider, Graph } from '../Graph';
9+
import { createFromProvider } from '../Graph';
1010
import { IProvider, LoginType, ProviderState } from './IProvider';
1111

1212
import { AuthenticationParameters, AuthError, AuthResponse, Configuration, UserAgentApplication } from 'msal';
@@ -116,7 +116,7 @@ export class MsalProvider extends IProvider {
116116
}
117117

118118
/**
119-
* simplified form of single sign-on (SSO)
119+
* attempts to sign in user silently
120120
*
121121
* @returns
122122
* @memberof MsalProvider
@@ -137,7 +137,7 @@ export class MsalProvider extends IProvider {
137137
}
138138

139139
/**
140-
* login auth Promise, Redirects request, and sets Provider state to SignedIn if response is recieved
140+
* sign in user
141141
*
142142
* @param {AuthenticationParameters} [authenticationParameters]
143143
* @returns {Promise<void>}
@@ -159,7 +159,7 @@ export class MsalProvider extends IProvider {
159159
}
160160

161161
/**
162-
* logout auth Promise, sets Provider state to SignedOut
162+
* sign out user
163163
*
164164
* @returns {Promise<void>}
165165
* @memberof MsalProvider
@@ -168,8 +168,9 @@ export class MsalProvider extends IProvider {
168168
this._userAgentApplication.logout();
169169
this.setState(ProviderState.SignedOut);
170170
}
171+
171172
/**
172-
* recieves acess token Promise
173+
* returns an access token for scopes
173174
*
174175
* @param {AuthenticationProviderOptions} options
175176
* @returns {Promise<string>}
@@ -210,6 +211,7 @@ export class MsalProvider extends IProvider {
210211
}
211212
throw null;
212213
}
214+
213215
/**
214216
* sets scopes
215217
*
@@ -221,7 +223,7 @@ export class MsalProvider extends IProvider {
221223
}
222224

223225
/**
224-
* if login runs into error, require user interaction
226+
* checks if error indicates a user interaction is required
225227
*
226228
* @protected
227229
* @param {*} error
@@ -296,6 +298,7 @@ export class MsalProvider extends IProvider {
296298
sessionStorage.setItem(this.sessionStorageDeniedScopesKey, JSON.stringify(deniedScopes));
297299
}
298300
}
301+
299302
/**
300303
* gets deniedScopes from sessionStorage
301304
*
@@ -307,6 +310,7 @@ export class MsalProvider extends IProvider {
307310
const scopesStr = sessionStorage.getItem(this.sessionStorageDeniedScopesKey);
308311
return scopesStr ? JSON.parse(scopesStr) : null;
309312
}
313+
310314
/**
311315
* if scopes are denied
312316
*
@@ -331,14 +335,6 @@ export class MsalProvider extends IProvider {
331335
this._loginType = typeof config.loginType !== 'undefined' ? config.loginType : LoginType.Redirect;
332336
this._loginHint = config.loginHint;
333337

334-
const tokenReceivedCallbackFunction = ((response: AuthResponse) => {
335-
this.tokenReceivedCallback(response);
336-
}).bind(this);
337-
338-
const errorReceivedCallbackFunction = ((authError: AuthError, accountState: string) => {
339-
this.errorReceivedCallback(authError, status);
340-
}).bind(this);
341-
342338
if (config.clientId) {
343339
const msalConfig: Configuration = config.options || { auth: { clientId: config.clientId } };
344340

@@ -358,7 +354,10 @@ export class MsalProvider extends IProvider {
358354
this.clientId = config.clientId;
359355

360356
this._userAgentApplication = new UserAgentApplication(msalConfig);
361-
this._userAgentApplication.handleRedirectCallback(tokenReceivedCallbackFunction, errorReceivedCallbackFunction);
357+
this._userAgentApplication.handleRedirectCallback(
358+
response => this.tokenReceivedCallback(response),
359+
(error, state) => this.errorReceivedCallback(error, state)
360+
);
362361
} else {
363362
throw new Error('clientId must be provided');
364363
}

src/providers/TeamsProvider.ts

Lines changed: 69 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -143,17 +143,15 @@ export class TeamsProvider extends MsalProvider {
143143

144144
teams.initialize();
145145

146-
// msal checks for the window.opener.msal to check if this is a popup authentication
147-
// and gets a false positive since teams opens a popup for the authentication.
148-
// in reality, we are doing a redirect authentication and need to act as if this is the
149-
// window initiating the authentication
150-
if (window.opener) {
151-
window.opener.msal = null;
146+
// if we were signing out before, then we are done
147+
if (sessionStorage.getItem(this._sessionStorageLogoutInProgress)) {
148+
teams.authentication.notifySuccess();
152149
}
153150

154151
const url = new URL(window.location.href);
152+
const isSignOut = url.searchParams.get('signout');
155153

156-
const paramsString = sessionStorage.getItem(this._sessionStorageParametersKey);
154+
const paramsString = localStorage.getItem(this._localStorageParametersKey);
157155
let authParams: AuthParams;
158156

159157
if (paramsString) {
@@ -162,14 +160,6 @@ export class TeamsProvider extends MsalProvider {
162160
authParams = {};
163161
}
164162

165-
if (!authParams.clientId) {
166-
authParams.clientId = url.searchParams.get('clientId');
167-
authParams.scopes = url.searchParams.get('scopes');
168-
authParams.loginHint = url.searchParams.get('loginHint');
169-
170-
sessionStorage.setItem(this._sessionStorageParametersKey, JSON.stringify(authParams));
171-
}
172-
173163
if (!authParams.clientId) {
174164
teams.authentication.notifyFailure('no clientId provided');
175165
return;
@@ -181,8 +171,7 @@ export class TeamsProvider extends MsalProvider {
181171
clientId: authParams.clientId,
182172
options: {
183173
auth: {
184-
clientId: authParams.clientId,
185-
redirectUri: url.protocol + '//' + url.host + url.pathname
174+
clientId: authParams.clientId
186175
},
187176
system: {
188177
loadFrameTimeout: 10000
@@ -200,17 +189,30 @@ export class TeamsProvider extends MsalProvider {
200189
// how do we handle when user can't sign in
201190
// change to promise and return status
202191
if (provider.state === ProviderState.SignedOut) {
203-
provider.login({
204-
loginHint: authParams.loginHint,
205-
scopes: scopes || provider.scopes
206-
});
192+
if (isSignOut) {
193+
teams.authentication.notifySuccess();
194+
return;
195+
}
196+
197+
// make sure we are calling login only once
198+
if (!sessionStorage.getItem(this._sessionStorageLoginInProgress)) {
199+
sessionStorage.setItem(this._sessionStorageLoginInProgress, 'true');
200+
provider.login({
201+
loginHint: authParams.loginHint,
202+
scopes: scopes || provider.scopes
203+
});
204+
}
207205
} else if (provider.state === ProviderState.SignedIn) {
206+
if (isSignOut) {
207+
sessionStorage.setItem(this._sessionStorageLogoutInProgress, 'true');
208+
await provider.logout();
209+
return;
210+
}
211+
208212
try {
209213
const accessToken = await provider.getAccessTokenForScopes(...provider.scopes);
210-
sessionStorage.removeItem(this._sessionStorageParametersKey);
211214
teams.authentication.notifySuccess(accessToken);
212215
} catch (e) {
213-
sessionStorage.removeItem(this._sessionStorageParametersKey);
214216
teams.authentication.notifyFailure(e);
215217
}
216218
}
@@ -220,15 +222,9 @@ export class TeamsProvider extends MsalProvider {
220222
handleProviderState();
221223
}
222224

223-
private static _sessionStorageParametersKey = 'msg-teamsprovider-auth-parameters';
224-
225-
/**
226-
* Scopes used for authentication
227-
*
228-
* @type {string[]}
229-
* @memberof TeamsProvider
230-
*/
231-
public scopes: string[];
225+
private static _localStorageParametersKey = 'msg-teamsprovider-auth-parameters';
226+
private static _sessionStorageLoginInProgress = 'msg-teamsprovider-login-in-progress';
227+
private static _sessionStorageLogoutInProgress = 'msg-teamsprovider-logout-in-progress';
232228

233229
private teamsContext;
234230
private _authPopupUrl: string;
@@ -261,24 +257,54 @@ export class TeamsProvider extends MsalProvider {
261257
teams.getContext(context => {
262258
this.teamsContext = context;
263259

264-
const url = new URL(this._authPopupUrl, new URL(window.location.href));
265-
url.searchParams.append('clientId', this.clientId);
260+
const authParams: AuthParams = {
261+
clientId: this.clientId,
262+
loginHint: context.loginHint,
263+
scopes: this.scopes.join(',')
264+
};
266265

267-
if (context.loginHint) {
268-
url.searchParams.append('loginHint', context.loginHint);
269-
}
266+
localStorage.setItem(TeamsProvider._localStorageParametersKey, JSON.stringify(authParams));
270267

271-
if (this.scopes) {
272-
url.searchParams.append('scopes', this.scopes.join(','));
273-
}
268+
const url = new URL(this._authPopupUrl, new URL(window.location.href));
274269

275270
teams.authentication.authenticate({
276271
failureCallback: reason => {
277272
this.setState(ProviderState.SignedOut);
278273
reject();
279274
},
280275
successCallback: result => {
281-
this.setState(ProviderState.SignedIn);
276+
this.trySilentSignIn();
277+
resolve();
278+
},
279+
url: url.href
280+
});
281+
});
282+
});
283+
}
284+
285+
/**
286+
* sign out user
287+
*
288+
* @returns {Promise<void>}
289+
* @memberof MsalProvider
290+
*/
291+
public async logout(): Promise<void> {
292+
const teams = TeamsHelper.microsoftTeamsLib;
293+
294+
return new Promise((resolve, reject) => {
295+
teams.getContext(context => {
296+
this.teamsContext = context;
297+
298+
const url = new URL(this._authPopupUrl, new URL(window.location.href));
299+
url.searchParams.append('signout', 'true');
300+
301+
teams.authentication.authenticate({
302+
failureCallback: reason => {
303+
this.trySilentSignIn();
304+
reject();
305+
},
306+
successCallback: result => {
307+
this.trySilentSignIn();
282308
resolve();
283309
},
284310
url: url.href
@@ -295,8 +321,9 @@ export class TeamsProvider extends MsalProvider {
295321
* @memberof TeamsProvider
296322
*/
297323
public async getAccessToken(options: AuthenticationProviderOptions): Promise<string> {
298-
if (!this.teamsContext) {
324+
if (!this.teamsContext && TeamsHelper.microsoftTeamsLib) {
299325
const teams = TeamsHelper.microsoftTeamsLib;
326+
teams.initialize();
300327
this.teamsContext = await teams.getContext();
301328
}
302329

@@ -309,17 +336,10 @@ export class TeamsProvider extends MsalProvider {
309336
accessTokenRequest.loginHint = this.teamsContext.loginHint;
310337
}
311338

312-
const currentParent = window.parent;
313-
if (document.referrer.startsWith('https://teams.microsoft.com/')) {
314-
(window as any).parent = window;
315-
}
316-
317339
try {
318340
const response = await this._userAgentApplication.acquireTokenSilent(accessTokenRequest);
319-
(window as any).parent = currentParent;
320341
return response.accessToken;
321342
} catch (e) {
322-
(window as any).parent = currentParent;
323343
if (this.requiresInteraction(e)) {
324344
// nothing we can do now until we can do incremental consent
325345
return null;

0 commit comments

Comments
 (0)