Skip to content

Commit 4dcd69d

Browse files
committed
Merge branch 'dev' into msal-node-cache-keys
2 parents 036ac45 + 68fa685 commit 4dcd69d

30 files changed

+356
-100
lines changed

lib/msal-browser/src/app/PublicClientApplication.ts

+17-11
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,23 @@
44
*/
55
import {
66
Account,
7-
SPAClient,
87
AuthenticationParameters,
8+
Authority,
9+
AuthorityFactory, ClientAuthError,
910
INetworkModule,
10-
TokenResponse,
11-
UrlString,
12-
TemporaryCacheKeys,
13-
TokenRenewParameters,
14-
StringUtils,
1511
PromptValue,
1612
ServerError,
17-
Authority,
18-
AuthorityFactory,
19-
InteractionRequiredAuthError,
20-
B2cAuthority
13+
SPAClient,
14+
StringUtils,
15+
TemporaryCacheKeys,
16+
TokenRenewParameters,
17+
TokenResponse,
18+
UrlString,
19+
AuthorityType,
20+
B2cAuthority,
21+
InteractionRequiredAuthError
2122
} from "@azure/msal-common";
22-
import { Configuration, buildConfiguration } from "../config/Configuration";
23+
import { buildConfiguration, Configuration } from "../config/Configuration";
2324
import { BrowserStorage } from "../cache/BrowserStorage";
2425
import { CryptoOps } from "../crypto/CryptoOps";
2526
import { RedirectHandler } from "../interaction_handler/RedirectHandler";
@@ -100,6 +101,11 @@ export class PublicClientApplication {
100101
this.config.system.networkClient
101102
);
102103

104+
// This is temporary. Remove when ADFS is supported for browser
105+
if(this.defaultAuthorityInstance.authorityType === AuthorityType.Adfs){
106+
throw ClientAuthError.createInvalidAuthorityTypeError(this.defaultAuthorityInstance.canonicalAuthority);
107+
}
108+
103109
// Create auth module.
104110
this.authModule = new SPAClient({
105111
authOptions: {

lib/msal-browser/test/client/PublicClientApplication.spec.ts

+50-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,42 @@
11
import * as Mocha from "mocha";
22
import chai from "chai";
33
import chaiAsPromised from "chai-as-promised";
4+
45
chai.use(chaiAsPromised);
56
const expect = chai.expect;
67
import { PublicClientApplication } from "../../src/app/PublicClientApplication";
7-
import { TEST_CONFIG, TEST_URIS, TEST_HASHES, TEST_TOKENS, TEST_DATA_CLIENT_INFO, TEST_TOKEN_LIFETIMES, RANDOM_TEST_GUID, DEFAULT_OPENID_CONFIG_RESPONSE, testNavUrl, testLogoutUrl } from "../utils/StringConstants";
8-
import { AuthError, ServerError, AuthResponse, LogLevel, Constants, TemporaryCacheKeys, TokenResponse, Account, TokenExchangeParameters, IdTokenClaims, SPAClient, PromptValue, AuthenticationParameters } from "@azure/msal-common";
8+
import {
9+
TEST_CONFIG,
10+
TEST_URIS,
11+
TEST_HASHES,
12+
TEST_TOKENS,
13+
TEST_DATA_CLIENT_INFO,
14+
TEST_TOKEN_LIFETIMES,
15+
RANDOM_TEST_GUID,
16+
DEFAULT_OPENID_CONFIG_RESPONSE,
17+
testNavUrl,
18+
testLogoutUrl
19+
} from "../utils/StringConstants";
20+
import {
21+
AuthError,
22+
ServerError,
23+
AuthResponse,
24+
LogLevel,
25+
Constants,
26+
TemporaryCacheKeys,
27+
TokenResponse,
28+
Account,
29+
TokenExchangeParameters,
30+
IdTokenClaims,
31+
SPAClient,
32+
PromptValue,
33+
AuthenticationParameters, ClientAuthError, ClientAuthErrorMessage
34+
} from "@azure/msal-common";
935
import { AuthCallback } from "../../src/types/AuthCallback";
10-
import { BrowserConfigurationAuthErrorMessage, BrowserConfigurationAuthError } from "../../src/error/BrowserConfigurationAuthError";
36+
import {
37+
BrowserConfigurationAuthErrorMessage,
38+
BrowserConfigurationAuthError
39+
} from "../../src/error/BrowserConfigurationAuthError";
1140
import sinon from "sinon";
1241
import { BrowserUtils } from "../../src/utils/BrowserUtils";
1342
import { BrowserConstants } from "../../src/utils/BrowserConstants";
@@ -74,6 +103,20 @@ describe("PublicClientApplication.ts Class Unit Tests", () => {
74103
});
75104
expect(window.sessionStorage.getItem(`${Constants.CACHE_PREFIX}.${TEST_CONFIG.MSAL_CLIENT_ID}.${TemporaryCacheKeys.URL_HASH}`)).to.be.eq(TEST_HASHES.TEST_SUCCESS_CODE_HASH);
76105
});
106+
107+
it("ADFS authority throws error", () => {
108+
109+
expect(() =>{
110+
new PublicClientApplication({
111+
auth: {
112+
clientId: TEST_CONFIG.MSAL_CLIENT_ID,
113+
authority: TEST_CONFIG.ADFS_AUTHORITY
114+
}
115+
});
116+
117+
}).to.throw(ClientAuthErrorMessage.invalidAuthorityType.desc);
118+
119+
});
77120
});
78121

79122
describe("Redirect Flow Unit tests", () => {
@@ -110,7 +153,7 @@ describe("PublicClientApplication.ts Class Unit Tests", () => {
110153
const testServerTokenResponse = {
111154
headers: null,
112155
status: 200,
113-
body : {
156+
body: {
114157
token_type: TEST_CONFIG.TOKEN_TYPE_BEARER,
115158
scope: TEST_CONFIG.DEFAULT_SCOPES.join(" "),
116159
expires_in: TEST_TOKEN_LIFETIMES.DEFAULT_EXPIRES_IN,
@@ -213,7 +256,7 @@ describe("PublicClientApplication.ts Class Unit Tests", () => {
213256
const testServerTokenResponse = {
214257
headers: null,
215258
status: 200,
216-
body : {
259+
body: {
217260
token_type: TEST_CONFIG.TOKEN_TYPE_BEARER,
218261
scope: TEST_CONFIG.DEFAULT_SCOPES.join(" "),
219262
expires_in: TEST_TOKEN_LIFETIMES.DEFAULT_EXPIRES_IN,
@@ -561,7 +604,7 @@ describe("PublicClientApplication.ts Class Unit Tests", () => {
561604
refreshToken: testServerTokenResponse.refresh_token,
562605
expiresOn: new Date(Date.now() + (testServerTokenResponse.expires_in * 1000)),
563606
account: testAccount,
564-
userRequestState: ""
607+
userRequestState: ""
565608
};
566609
sinon.stub(SPAClient.prototype, "createLoginUrl").resolves(testNavUrl);
567610
const loadFrameSyncSpy = sinon.spy(SilentHandler.prototype, <any>"loadFrameSync");
@@ -666,7 +709,7 @@ describe("PublicClientApplication.ts Class Unit Tests", () => {
666709
refreshToken: testServerTokenResponse.refresh_token,
667710
expiresOn: new Date(Date.now() + (testServerTokenResponse.expires_in * 1000)),
668711
account: testAccount,
669-
userRequestState: ""
712+
userRequestState: ""
670713
};
671714
const createAcqTokenStub = sinon.stub(SPAClient.prototype, "createAcquireTokenUrl").resolves(testNavUrl);
672715
const silentTokenHelperStub = sinon.stub(pca, <any>"silentTokenHelper").resolves(testTokenResponse);

lib/msal-browser/test/utils/StringConstants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export const TEST_CONFIG = {
2222
MSAL_TENANT_ID: "3338040d-6c67-4c5b-b112-36a304b66dad",
2323
validAuthority: TEST_URIS.DEFAULT_INSTANCE + "common",
2424
alternateValidAuthority: TEST_URIS.ALTERNATE_INSTANCE + "common",
25+
ADFS_AUTHORITY: "https://authority.com/adfs",
2526
applicationName: "msal.js-tests",
2627
applicationVersion: "msal.js-tests.1.0.fake",
2728
STATE: "1234",

lib/msal-common/docs/ADFS.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# ADFS Support
2+
3+
MSAL supports connecting to Azure AD, which signs in managed-users (users managed in Azure AD) or federated users (users managed by another identity provider such as ADFS). MSAL does not differentiate between these two types of users. As far as it’s concerned, it talks to Azure AD. The authority that you would pass in this case is the normal Azure AD Authority: `https://login.microsoftonline.com/{Enter_the_Tenant_Info_Here}`
4+
5+
MSAL also supports directly connection to AD FS 2019, which is Open ID Connect compliant and has support scopes and PKCE. This support requires that a service pack [KB 4490481](https://support.microsoft.com/en-us/help/4490481/windows-10-update-kb4490481) is applied to Windows Server. When connecting directly to AD FS, the authority you'll want to use to build your application will be of form `https://mysite.contoso.com/adfs/`
6+
7+
Currently, there are no plans to support a direct connection to ADFS 16 or ADFS v2. ADFS 16 does not support scopes, and ADFS v2 is not OIDC compliant.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
import { Authority } from "./Authority";
7+
import { AuthorityType } from "./AuthorityType";
8+
import { INetworkModule } from "../network/INetworkModule";
9+
10+
/**
11+
* The AdfsAuthority class extends the Authority class and adds functionality specific to ADFS 2019
12+
*/
13+
export class AdfsAuthority extends Authority {
14+
15+
/**
16+
* Return authority type
17+
*/
18+
public get authorityType(): AuthorityType {
19+
return AuthorityType.Adfs;
20+
}
21+
22+
public constructor(authority: string, networkInterface: INetworkModule) {
23+
super(authority, networkInterface);
24+
}
25+
26+
/**
27+
* Returns a promise which resolves to the OIDC endpoint
28+
*/
29+
public async getOpenIdConfigurationEndpointAsync(): Promise<string> {
30+
return `${this.canonicalAuthority}.well-known/openid-configuration`;
31+
}
32+
}

lib/msal-common/src/authority/Authority.ts

+8
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ export abstract class Authority {
8484
}
8585
}
8686

87+
public get deviceCodeEndpoint(): string {
88+
if(this.discoveryComplete()) {
89+
return this.tenantDiscoveryResponse.token_endpoint.replace("/token", "/devicecode");
90+
} else {
91+
throw ClientAuthError.createEndpointDiscoveryIncompleteError("Discovery incomplete.");
92+
}
93+
}
94+
8795
/**
8896
* OAuth logout endpoint for requests
8997
*/

lib/msal-common/src/authority/AuthorityFactory.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { INetworkModule } from "./../network/INetworkModule";
1212
import { StringUtils } from "./../utils/StringUtils";
1313
import { UrlString } from "./../url/UrlString";
1414
import { Constants } from "../utils/Constants";
15+
import { AdfsAuthority } from "./AdfsAuthority";
1516

1617
export class AuthorityFactory {
1718

@@ -50,7 +51,8 @@ export class AuthorityFactory {
5051
return new AadAuthority(authorityUrl, networkInterface);
5152
case AuthorityType.B2C:
5253
return new B2cAuthority(authorityUrl, networkInterface);
53-
// TODO: Support ADFS here in a later PR
54+
case AuthorityType.Adfs:
55+
return new AdfsAuthority(authorityUrl, networkInterface);
5456
default:
5557
throw ClientAuthError.createInvalidAuthorityTypeError(`${authorityUrl}`);
5658
}

lib/msal-common/src/client/BaseClient.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { Logger } from "../logger/Logger";
1414
import { AADServerParamKeys, Constants, HeaderNames } from "../utils/Constants";
1515
import { NetworkResponse } from "../network/NetworkManager";
1616
import { ServerAuthorizationTokenResponse } from "../server/ServerAuthorizationTokenResponse";
17+
import { B2cAuthority } from "../authority/B2cAuthority";
1718
import { UnifiedCacheManager } from "../unifiedCache/UnifiedCacheManager";
1819

1920
/**
@@ -70,7 +71,8 @@ export abstract class BaseClient {
7071
// Set the network interface
7172
this.networkClient = this.config.networkInterface;
7273

73-
// Default authority instance.
74+
B2cAuthority.setKnownAuthorities(this.config.authOptions.knownAuthorities);
75+
7476
this.defaultAuthority = this.config.authOptions.authority;
7577
}
7678

lib/msal-common/src/client/DeviceCodeClient.ts

+15-17
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,22 @@ export class DeviceCodeClient extends BaseClient {
4646
*/
4747
private async getDeviceCode(request: DeviceCodeRequest): Promise<DeviceCodeResponse> {
4848

49-
const deviceCodeUrl = this.createDeviceCodeUrl(request);
49+
const queryString = this.createQueryString(request);
5050
const headers = this.createDefaultLibraryHeaders();
5151

52-
return this.executeGetRequestToDeviceCodeEndpoint(deviceCodeUrl, headers);
52+
return this.executePostRequestToDeviceCodeEndpoint(this.defaultAuthority.deviceCodeEndpoint, queryString, headers);
5353
}
5454

5555
/**
56-
* Executes GET request to device code endpoint
57-
* @param deviceCodeUrl
56+
* Executes POST request to device code endpoint
57+
* @param deviceCodeEndpoint
58+
* @param queryString
5859
* @param headers
5960
*/
60-
private async executeGetRequestToDeviceCodeEndpoint(deviceCodeUrl: string, headers: Map<string, string>): Promise<DeviceCodeResponse> {
61+
private async executePostRequestToDeviceCodeEndpoint(
62+
deviceCodeEndpoint: string,
63+
queryString: string,
64+
headers: Map<string, string>): Promise<DeviceCodeResponse> {
6165

6266
const {
6367
body: {
@@ -68,7 +72,12 @@ export class DeviceCodeClient extends BaseClient {
6872
interval,
6973
message
7074
}
71-
} = await this.networkClient.sendGetRequestAsync<ServerDeviceCodeResponse>(deviceCodeUrl, {headers});
75+
} = await this.networkClient.sendPostRequestAsync<ServerDeviceCodeResponse>(
76+
deviceCodeEndpoint,
77+
{
78+
body: queryString,
79+
headers: headers
80+
});
7281

7382
return {
7483
userCode,
@@ -80,17 +89,6 @@ export class DeviceCodeClient extends BaseClient {
8089
};
8190
}
8291

83-
/**
84-
* Create device code endpoint url
85-
* @param request
86-
*/
87-
private createDeviceCodeUrl(request: DeviceCodeRequest): string {
88-
const queryString: string = this.createQueryString(request);
89-
90-
// TODO add device code endpoint to authority class
91-
return `${this.defaultAuthority.canonicalAuthority}${Constants.DEVICE_CODE_ENDPOINT_PATH}?${queryString}`;
92-
}
93-
9492
/**
9593
* Create device code endpoint query parameters and returns string
9694
*/

lib/msal-common/src/client/RefreshTokenClient.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export class RefreshTokenClient extends BaseClient {
4141

4242
const scopeSet = new ScopeSet(request.scopes || [],
4343
this.config.authOptions.clientId,
44-
true);
44+
false);
4545
parameterBuilder.addScopes(scopeSet);
4646
parameterBuilder.addClientId(this.config.authOptions.clientId);
4747
parameterBuilder.addGrantType(GrantType.REFRESH_TOKEN_GRANT);

lib/msal-common/src/client/SPAClient.ts

+25
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { StringUtils } from "../utils/StringUtils";
2626
import { UrlString } from "../url/UrlString";
2727
import { Account } from "../account/Account";
2828
import { buildClientInfo } from "../account/ClientInfo";
29+
import { AuthorityType } from "../authority/AuthorityType";
2930

3031
/**
3132
* SPAClient class
@@ -67,6 +68,12 @@ export class SPAClient extends BaseClient {
6768
private async createUrl(request: AuthenticationParameters, isLoginCall: boolean): Promise<string> {
6869
// Initialize authority or use default, and perform discovery endpoint check.
6970
const acquireTokenAuthority = (request && request.authority) ? AuthorityFactory.createInstance(request.authority, this.networkClient) : this.defaultAuthority;
71+
72+
// This is temporary. Remove when ADFS is supported for browser
73+
if(acquireTokenAuthority.authorityType == AuthorityType.Adfs){
74+
throw ClientAuthError.createInvalidAuthorityTypeError(acquireTokenAuthority.canonicalAuthority);
75+
}
76+
7077
try {
7178
await acquireTokenAuthority.resolveEndpointsAsync();
7279
} catch (e) {
@@ -143,6 +150,12 @@ export class SPAClient extends BaseClient {
143150

144151
// Initialize authority or use default, and perform discovery endpoint check.
145152
const acquireTokenAuthority = (tokenRequest && tokenRequest.authority) ? AuthorityFactory.createInstance(tokenRequest.authority, this.networkClient) : this.defaultAuthority;
153+
154+
// This is temporary. Remove when ADFS is supported for browser
155+
if(acquireTokenAuthority.authorityType == AuthorityType.Adfs){
156+
throw ClientAuthError.createInvalidAuthorityTypeError(acquireTokenAuthority.canonicalAuthority);
157+
}
158+
146159
if (!acquireTokenAuthority.discoveryComplete()) {
147160
try {
148161
await acquireTokenAuthority.resolveEndpointsAsync();
@@ -199,6 +212,12 @@ export class SPAClient extends BaseClient {
199212

200213
// Initialize authority or use default, and perform discovery endpoint check.
201214
const acquireTokenAuthority = request.authority ? AuthorityFactory.createInstance(request.authority, this.networkClient) : this.defaultAuthority;
215+
216+
// This is temporary. Remove when ADFS is supported for browser
217+
if(acquireTokenAuthority.authorityType == AuthorityType.Adfs){
218+
throw ClientAuthError.createInvalidAuthorityTypeError(acquireTokenAuthority.canonicalAuthority);
219+
}
220+
202221
if (!acquireTokenAuthority.discoveryComplete()) {
203222
try {
204223
await acquireTokenAuthority.resolveEndpointsAsync();
@@ -271,6 +290,12 @@ export class SPAClient extends BaseClient {
271290

272291
// Acquire token authorities.
273292
const acquireTokenAuthority = (authorityUri) ? AuthorityFactory.createInstance(authorityUri, this.networkClient) : this.defaultAuthority;
293+
294+
// This is temporary. Remove when ADFS is supported for browser
295+
if(acquireTokenAuthority.authorityType == AuthorityType.Adfs){
296+
throw ClientAuthError.createInvalidAuthorityTypeError(acquireTokenAuthority.canonicalAuthority);
297+
}
298+
274299
if (!acquireTokenAuthority.discoveryComplete()) {
275300
try {
276301
await acquireTokenAuthority.resolveEndpointsAsync();

lib/msal-common/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export { IdTokenClaims } from "./account/IdTokenClaims";
1414
export { Authority } from "./authority/Authority";
1515
export { B2cAuthority } from "./authority/B2cAuthority";
1616
export { AuthorityFactory } from "./authority/AuthorityFactory";
17+
export { AuthorityType } from "./authority/AuthorityType";
1718
// Cache
1819
export { ICacheStorage } from "./cache/ICacheStorage";
1920
export { UnifiedCacheManager } from "./unifiedCache/UnifiedCacheManager";

lib/msal-common/src/request/ScopeSet.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ export class ScopeSet {
2727
clientId: string,
2828
scopesRequired: boolean,
2929
) {
30-
this.clientId = clientId;
30+
// lower case need for replaceDefaultScopes() because ADFS clientids don't have to be GUIDS.
31+
this.clientId = clientId.toLowerCase();
3132
this.scopesRequired = scopesRequired;
3233

3334
// Filter empty string and null/undefined array items

0 commit comments

Comments
 (0)