-
Notifications
You must be signed in to change notification settings - Fork 93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: msal upgrade #883
Changes from 3 commits
3bc16fc
d434316
c9e0117
735b038
26cbb09
dffb722
ca13997
522bca6
8e013d1
bd5da7c
80983a9
d765db6
ba89fe8
d8a47ef
5845455
6c6938b
cc6765e
68a210a
178a841
b6eee86
51b96bf
cbf7ff2
b230e4d
d0f7ae0
1f2d81a
f844bd3
602958b
c8dab2b
e14ecda
c0a0fe4
f02ac9e
c209706
886d42c
f269d2d
5884ca8
57a32f8
467ad75
9a26ad4
eef484b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { | ||
AccountInfo, | ||
AuthenticationResult, InteractionRequiredAuthError, | ||
PopupRequest, PublicClientApplication, SilentRequest | ||
} from '@azure/msal-browser'; | ||
|
||
import { geLocale } from '../../../../appLocale'; | ||
import { AUTH_URL, DEFAULT_USER_SCOPES } from '../../graph-constants'; | ||
|
||
const defaultScopes = DEFAULT_USER_SCOPES.split(' '); | ||
|
||
export class AuthenticationModule { | ||
|
||
private msalApplication: PublicClientApplication; | ||
|
||
constructor(msalApplication: PublicClientApplication) { | ||
thewahome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this.msalApplication = msalApplication; | ||
} | ||
|
||
public getAccount(): AccountInfo | undefined { | ||
if (this.msalApplication) { | ||
const allAccounts = this.msalApplication.getAllAccounts(); | ||
if (allAccounts && allAccounts.length > 0) { | ||
return allAccounts[0]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it guaranteed that the first account that is the account[0] will always be the valid or intended account? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AzureAD/microsoft-authentication-library-for-js#2196 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for pointing this out. I have seen that the MSAL.js team recommends that we track this user ourselves There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Any idea about this? My concern is about picking up the wrong account. It would be a major issue if that happens. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah...
This means I'd have to create a way to track either of the identifiers |
||
} | ||
} | ||
return undefined; | ||
} | ||
|
||
public async getToken() { | ||
const silentRequest = { | ||
thewahome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
scopes: defaultScopes, | ||
authority: this.getAuthority(), | ||
account: this.getAccount() | ||
}; | ||
const authResponse = await this.msalApplication.acquireTokenSilent(silentRequest); | ||
thewahome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return authResponse; | ||
thewahome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
public async getAuthResult(scopes: string[] = [], sessionId?: string): Promise<AuthenticationResult> { | ||
const userScopes = (scopes.length > 0) ? scopes : defaultScopes; | ||
const silentRequest: SilentRequest = { | ||
scopes: userScopes, | ||
authority: this.getAuthority(), | ||
account: this.getAccount() | ||
}; | ||
|
||
try { | ||
const authResponse: AuthenticationResult = await this.msalApplication.acquireTokenSilent(silentRequest); | ||
thewahome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return authResponse; | ||
} catch (error) { | ||
if (error instanceof InteractionRequiredAuthError || this.getAccount() === undefined) { | ||
return this.loginWithInteraction(userScopes, sessionId); | ||
} else { | ||
throw error; | ||
} | ||
} | ||
} | ||
|
||
private getAuthority(): string { | ||
thewahome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// support for tenanted endpoint | ||
const urlParams = new URLSearchParams(location.search); | ||
let tenant = urlParams.get('tenant'); | ||
|
||
if (tenant === null) { | ||
thewahome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
tenant = 'common'; | ||
} | ||
|
||
return `${AUTH_URL}/${tenant}/`; | ||
} | ||
|
||
private async loginWithInteraction(userScopes: string[], sessionId?: string) { | ||
const popUpRequest: PopupRequest = { | ||
scopes: userScopes, | ||
authority: this.getAuthority(), | ||
prompt: 'select_account', | ||
redirectUri: this.getCurrentUri(), | ||
extraQueryParameters: { mkt: geLocale } | ||
}; | ||
|
||
if (sessionId) { | ||
popUpRequest.sid = sessionId; | ||
} | ||
|
||
try { | ||
const authResponse: AuthenticationResult = await this.msalApplication.loginPopup(popUpRequest); | ||
return authResponse; | ||
} catch (error) { | ||
throw error; | ||
} | ||
} | ||
|
||
/** | ||
* get current uri for redirect uri purpose | ||
* ref - https://github.com/AzureAD/microsoft-authentication-library-for | ||
* -js/blob/9274fac6d100a6300eb2faa4c94aa2431b1ca4b0/lib/msal-browser/src/utils/BrowserUtils.ts#L49 | ||
*/ | ||
private getCurrentUri(): string { | ||
const currentUrl = window.location.href.split('?')[0].split('#')[0]; | ||
return currentUrl.toLowerCase(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { PublicClientApplication } from '@azure/msal-browser'; | ||
import { AuthenticationProvider } from '@microsoft/microsoft-graph-client'; | ||
|
||
import { AuthenticationModule } from './authentication-module'; | ||
|
||
export class CustomAuthenticationProvider implements AuthenticationProvider { | ||
|
||
private msalApplication: PublicClientApplication; | ||
thewahome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
constructor(msalApplication: PublicClientApplication) { | ||
this.msalApplication = msalApplication; | ||
} | ||
|
||
/** | ||
* getAccessToken | ||
*/ | ||
public async getAccessToken(): Promise<string> { | ||
try { | ||
const authModule = new AuthenticationModule(this.msalApplication); | ||
const authResult = await authModule.getAuthResult(); | ||
return authResult.accessToken; | ||
} catch (error) { | ||
throw error; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { LoginType } from '../../../../types/enums'; | ||
thewahome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** | ||
* Returns whether to load the POPUP/REDIRECT interaction | ||
* @returns string | ||
*/ | ||
|
||
export function getLoginType() { | ||
const userAgent = window.navigator.userAgent; | ||
const msie = userAgent.indexOf('MSIE '); | ||
const msie11 = userAgent.indexOf('Trident/'); | ||
const msedge = userAgent.indexOf('Edge/'); | ||
const isIE = msie > 0 || msie11 > 0; | ||
const isEdge = msedge > 0; | ||
return isIE || isEdge ? LoginType.Redirect : LoginType.Popup; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how did you test that this is now doing the right thing as far as PKCE.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ddyett
Frankly, I have trusted that the msal team has done the right thing. My tests were geared towards whether GE was able to sign in and out effectively into all the accounts. I may need guidance on ways to test this the PKCE way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thewahome Turn on Fiddler, you'll see a call to /authorize with &code_challenge parameter. That indicates that it is using PKCE.
https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code
You'll also see the refresh token supplied in the response to /token.
Graph Explorer and MSAL are using the auth code flow as expected. The one thing that we likely aren't doing but should do is set the state token in the initial request to /authorize and then checking on the response from /token that we are getting the same state token. This helps prevent CSRF