@azure/msal-angular 4.0.12 → 4.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.beachballrc +3 -0
- package/.editorconfig +16 -0
- package/.eslintrc.json +3 -0
- package/CHANGELOG.json +2315 -0
- package/CHANGELOG.md +869 -0
- package/FAQ.md +182 -0
- package/angular.json +51 -0
- package/docs/angular-universal.md +64 -0
- package/docs/configuration.md +609 -0
- package/docs/errors.md +89 -0
- package/docs/events.md +262 -0
- package/docs/initialization.md +206 -0
- package/docs/known-issues.md +19 -0
- package/docs/logging.md +47 -0
- package/docs/msal-guard.md +208 -0
- package/docs/msal-interceptor.md +160 -0
- package/docs/multi-tenant.md +90 -0
- package/docs/performance.md +50 -0
- package/docs/public-apis.md +29 -0
- package/docs/redirects.md +257 -0
- package/docs/security.md +4 -0
- package/docs/ssosilent.md +84 -0
- package/docs/v0-v1-upgrade-guide.md +61 -0
- package/docs/v1-v2-upgrade-guide.md +70 -0
- package/docs/v2-v3-upgrade-guide.md +52 -0
- package/docs/v3-v4-upgrade-guide.md +36 -0
- package/karma.conf.js +44 -0
- package/ng-package.json +7 -0
- package/package.json +56 -26
- package/src/IMsalService.ts +34 -0
- package/src/constants.ts +20 -0
- package/src/msal.broadcast.config.ts +8 -0
- package/src/msal.broadcast.service.spec.ts +471 -0
- package/src/msal.broadcast.service.ts +84 -0
- package/src/msal.guard.config.ts +27 -0
- package/src/msal.guard.spec.ts +525 -0
- package/src/msal.guard.ts +290 -0
- package/src/msal.interceptor.config.ts +39 -0
- package/src/msal.interceptor.spec.ts +1168 -0
- package/src/msal.interceptor.ts +388 -0
- package/src/msal.module.ts +51 -0
- package/src/msal.navigation.client.spec.ts +96 -0
- package/src/msal.navigation.client.ts +59 -0
- package/src/msal.redirect.component.spec.ts +64 -0
- package/src/msal.redirect.component.ts +26 -0
- package/src/msal.service.spec.ts +468 -0
- package/src/msal.service.ts +104 -0
- package/src/packageMetadata.ts +3 -0
- package/{public-api.d.ts → src/public-api.ts} +35 -17
- package/src/test.ts +19 -0
- package/tsconfig.json +25 -0
- package/tsconfig.lib.json +27 -0
- package/tsconfig.lib.prod.json +10 -0
- package/tsconfig.spec.json +17 -0
- package/typedoc.json +10 -0
- package/IMsalService.d.ts +0 -17
- package/constants.d.ts +0 -5
- package/esm2020/IMsalService.mjs +0 -6
- package/esm2020/azure-msal-angular.mjs +0 -5
- package/esm2020/constants.mjs +0 -10
- package/esm2020/msal.broadcast.config.mjs +0 -6
- package/esm2020/msal.broadcast.service.mjs +0 -66
- package/esm2020/msal.guard.config.mjs +0 -6
- package/esm2020/msal.guard.mjs +0 -218
- package/esm2020/msal.interceptor.config.mjs +0 -6
- package/esm2020/msal.interceptor.mjs +0 -270
- package/esm2020/msal.module.mjs +0 -46
- package/esm2020/msal.navigation.client.mjs +0 -53
- package/esm2020/msal.redirect.component.mjs +0 -31
- package/esm2020/msal.service.mjs +0 -88
- package/esm2020/packageMetadata.mjs +0 -4
- package/esm2020/public-api.mjs +0 -18
- package/fesm2015/azure-msal-angular.mjs +0 -762
- package/fesm2015/azure-msal-angular.mjs.map +0 -1
- package/fesm2020/azure-msal-angular.mjs +0 -758
- package/fesm2020/azure-msal-angular.mjs.map +0 -1
- package/index.d.ts +0 -5
- package/msal.broadcast.config.d.ts +0 -3
- package/msal.broadcast.service.d.ts +0 -19
- package/msal.guard.config.d.ts +0 -9
- package/msal.guard.d.ts +0 -43
- package/msal.interceptor.config.d.ts +0 -13
- package/msal.interceptor.d.ts +0 -70
- package/msal.module.d.ts +0 -13
- package/msal.navigation.client.d.ts +0 -19
- package/msal.redirect.component.d.ts +0 -15
- package/msal.service.d.ts +0 -33
- package/packageMetadata.d.ts +0 -2
|
@@ -0,0 +1,1168 @@
|
|
|
1
|
+
import { TestBed } from "@angular/core/testing";
|
|
2
|
+
import { Location } from "@angular/common";
|
|
3
|
+
import { HTTP_INTERCEPTORS, HttpClient } from "@angular/common/http";
|
|
4
|
+
import {
|
|
5
|
+
HttpClientTestingModule,
|
|
6
|
+
HttpTestingController,
|
|
7
|
+
} from "@angular/common/http/testing";
|
|
8
|
+
import { RouterTestingModule } from "@angular/router/testing";
|
|
9
|
+
import {
|
|
10
|
+
AccountInfo,
|
|
11
|
+
AuthError,
|
|
12
|
+
InteractionStatus,
|
|
13
|
+
InteractionType,
|
|
14
|
+
IPublicClientApplication,
|
|
15
|
+
PublicClientApplication,
|
|
16
|
+
SilentRequest,
|
|
17
|
+
} from "@azure/msal-browser";
|
|
18
|
+
import { BehaviorSubject } from "rxjs";
|
|
19
|
+
import {
|
|
20
|
+
MsalModule,
|
|
21
|
+
MsalService,
|
|
22
|
+
MsalInterceptor,
|
|
23
|
+
MsalBroadcastService,
|
|
24
|
+
MsalInterceptorConfiguration,
|
|
25
|
+
ProtectedResourceScopes,
|
|
26
|
+
} from "./public-api";
|
|
27
|
+
|
|
28
|
+
let interceptor: MsalInterceptor;
|
|
29
|
+
let httpMock: HttpTestingController;
|
|
30
|
+
let httpClient: HttpClient;
|
|
31
|
+
let testInteractionType: InteractionType;
|
|
32
|
+
|
|
33
|
+
let testInterceptorConfig: Partial<MsalInterceptorConfiguration> = {};
|
|
34
|
+
|
|
35
|
+
const sampleAccountInfo: AccountInfo = {
|
|
36
|
+
homeAccountId: "test",
|
|
37
|
+
localAccountId: "test",
|
|
38
|
+
environment: "test",
|
|
39
|
+
tenantId: "test-tenant",
|
|
40
|
+
username: "test",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
function MSALInstanceFactory(): IPublicClientApplication {
|
|
44
|
+
return new PublicClientApplication({
|
|
45
|
+
auth: {
|
|
46
|
+
clientId: "b5c2e510-4a17-4feb-b219-e55aa5b74144",
|
|
47
|
+
redirectUri: "http://localhost:4200",
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function MSALInterceptorFactory(): MsalInterceptorConfiguration {
|
|
53
|
+
return {
|
|
54
|
+
//@ts-ignore
|
|
55
|
+
interactionType: testInteractionType,
|
|
56
|
+
protectedResourceMap: new Map<
|
|
57
|
+
string,
|
|
58
|
+
Array<string | ProtectedResourceScopes> | null
|
|
59
|
+
>([
|
|
60
|
+
["https://MY_API_SITE_2", ["api://MY_API_SITE_2/as_user"]],
|
|
61
|
+
["https://MY_API_SITE_1", ["api://MY_API_SITE_1/as_user"]],
|
|
62
|
+
["https://graph.microsoft.com/v1.0/me", ["user.read"]],
|
|
63
|
+
["relative/me", ["relative.scope"]],
|
|
64
|
+
["https://myapplication.com/user/*", ["customscope.read"]],
|
|
65
|
+
["https://*.myapplication.com/*", ["mail.read"]],
|
|
66
|
+
["https://api.test.com", ["default.scope1"]],
|
|
67
|
+
["https://*.test.com", ["default.scope2"]],
|
|
68
|
+
["http://localhost:3000/unprotect", null],
|
|
69
|
+
[
|
|
70
|
+
"http://localhost:3000/unprotect/post",
|
|
71
|
+
[{ httpMethod: "POST", scopes: null }],
|
|
72
|
+
],
|
|
73
|
+
["http://localhost:3000/", ["base.scope"]],
|
|
74
|
+
["http://localhost:9876/tenant?abc", ["query.scope"]],
|
|
75
|
+
["http://applicationA/slash/", ["customA.scope"]],
|
|
76
|
+
["http://applicationB/noSlash", ["customB.scope"]],
|
|
77
|
+
[
|
|
78
|
+
"http://applicationC.com",
|
|
79
|
+
[
|
|
80
|
+
{
|
|
81
|
+
httpMethod: "POST",
|
|
82
|
+
scopes: ["write.scope"],
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
],
|
|
86
|
+
[
|
|
87
|
+
"http://applicationD.com",
|
|
88
|
+
[
|
|
89
|
+
"all.scope",
|
|
90
|
+
{
|
|
91
|
+
httpMethod: "GET",
|
|
92
|
+
scopes: ["read.scope"],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
httpMethod: "Post",
|
|
96
|
+
scopes: ["info.scope"],
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
],
|
|
100
|
+
["http://applicationE.com/profile/", ["customE.scope"]],
|
|
101
|
+
["http://applicationF.com/profile/", ["customF.scope"]],
|
|
102
|
+
]),
|
|
103
|
+
authRequest: testInterceptorConfig.authRequest,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function initializeMsal() {
|
|
108
|
+
TestBed.resetTestingModule();
|
|
109
|
+
|
|
110
|
+
TestBed.configureTestingModule({
|
|
111
|
+
imports: [
|
|
112
|
+
HttpClientTestingModule,
|
|
113
|
+
RouterTestingModule,
|
|
114
|
+
MsalModule.forRoot(MSALInstanceFactory(), null, MSALInterceptorFactory()),
|
|
115
|
+
],
|
|
116
|
+
providers: [
|
|
117
|
+
MsalInterceptor,
|
|
118
|
+
MsalService,
|
|
119
|
+
MsalBroadcastService,
|
|
120
|
+
{
|
|
121
|
+
provide: HTTP_INTERCEPTORS,
|
|
122
|
+
useClass: MsalInterceptor,
|
|
123
|
+
multi: true,
|
|
124
|
+
},
|
|
125
|
+
Location,
|
|
126
|
+
],
|
|
127
|
+
teardown: { destroyAfterEach: false },
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
interceptor = TestBed.inject(MsalInterceptor);
|
|
131
|
+
httpMock = TestBed.inject(HttpTestingController);
|
|
132
|
+
httpClient = TestBed.inject(HttpClient);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
describe("MsalInterceptor", () => {
|
|
136
|
+
beforeEach(() => {
|
|
137
|
+
testInteractionType = InteractionType.Popup;
|
|
138
|
+
testInterceptorConfig = {};
|
|
139
|
+
initializeMsal();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("throws error if incorrect interaction type set in interceptor configuration", (done) => {
|
|
143
|
+
testInteractionType = InteractionType.Silent;
|
|
144
|
+
initializeMsal();
|
|
145
|
+
|
|
146
|
+
httpClient.get("https://graph.microsoft.com/v1.0/me").subscribe({
|
|
147
|
+
error: (error) => {
|
|
148
|
+
expect(error.errorCode).toBe("invalid_interaction_type");
|
|
149
|
+
expect(error.errorMessage).toBe(
|
|
150
|
+
"Invalid interaction type provided to MSAL Interceptor. InteractionType.Popup, InteractionType.Redirect must be provided in the msalInterceptorConfiguration"
|
|
151
|
+
);
|
|
152
|
+
testInteractionType = InteractionType.Popup;
|
|
153
|
+
done();
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("does not attach authorization header for unprotected resource", (done) => {
|
|
159
|
+
httpClient
|
|
160
|
+
.get("http://localhost/api")
|
|
161
|
+
.subscribe((response) => expect(response).toBeTruthy());
|
|
162
|
+
|
|
163
|
+
const request = httpMock.expectOne("http://localhost/api");
|
|
164
|
+
request.flush({ data: "test" });
|
|
165
|
+
expect(request.request.headers.get("Authorization")).toBeUndefined;
|
|
166
|
+
httpMock.verify();
|
|
167
|
+
done();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("does not attach authorization header for own domain", (done) => {
|
|
171
|
+
httpClient
|
|
172
|
+
.get("http://localhost:9876")
|
|
173
|
+
.subscribe((response) => expect(response).toBeTruthy());
|
|
174
|
+
|
|
175
|
+
const request = httpMock.expectOne("http://localhost:9876");
|
|
176
|
+
request.flush({ data: "test" });
|
|
177
|
+
expect(request.request.headers.get("Authorization")).toBeUndefined;
|
|
178
|
+
httpMock.verify();
|
|
179
|
+
done();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("attaches authorization header with access token for protected resource with exact match", (done) => {
|
|
183
|
+
const spy = spyOn(
|
|
184
|
+
PublicClientApplication.prototype,
|
|
185
|
+
"acquireTokenSilent"
|
|
186
|
+
).and.returnValue(
|
|
187
|
+
new Promise((resolve) => {
|
|
188
|
+
//@ts-ignore
|
|
189
|
+
resolve({
|
|
190
|
+
accessToken: "access-token",
|
|
191
|
+
});
|
|
192
|
+
})
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
spyOn(
|
|
196
|
+
PublicClientApplication.prototype,
|
|
197
|
+
"getActiveAccount"
|
|
198
|
+
).and.returnValue(sampleAccountInfo);
|
|
199
|
+
|
|
200
|
+
httpClient.get("https://graph.microsoft.com/v1.0/me").subscribe();
|
|
201
|
+
setTimeout(() => {
|
|
202
|
+
const request = httpMock.expectOne("https://graph.microsoft.com/v1.0/me");
|
|
203
|
+
request.flush({ data: "test" });
|
|
204
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
205
|
+
"Bearer access-token"
|
|
206
|
+
);
|
|
207
|
+
expect(spy).toHaveBeenCalledWith({
|
|
208
|
+
account: sampleAccountInfo,
|
|
209
|
+
scopes: ["user.read"],
|
|
210
|
+
});
|
|
211
|
+
httpMock.verify();
|
|
212
|
+
done();
|
|
213
|
+
}, 200);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("attaches authorization header with access token via interaction if acquireTokenSilent returns null access token, interaction type is Popup and interaction invocation waits for interaction status becomes None", (done) => {
|
|
217
|
+
const _inProgress = new BehaviorSubject<InteractionStatus>(
|
|
218
|
+
InteractionStatus.Startup
|
|
219
|
+
);
|
|
220
|
+
const msalBroadcastService = TestBed.inject(MsalBroadcastService);
|
|
221
|
+
|
|
222
|
+
msalBroadcastService.inProgress$ = _inProgress.asObservable();
|
|
223
|
+
|
|
224
|
+
const spy1 = spyOn(
|
|
225
|
+
PublicClientApplication.prototype,
|
|
226
|
+
"acquireTokenSilent"
|
|
227
|
+
).and.returnValue(
|
|
228
|
+
new Promise((resolve) => {
|
|
229
|
+
//@ts-ignore
|
|
230
|
+
resolve({
|
|
231
|
+
accessToken: null,
|
|
232
|
+
});
|
|
233
|
+
})
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
const spy2 = spyOn(
|
|
237
|
+
PublicClientApplication.prototype,
|
|
238
|
+
"acquireTokenPopup"
|
|
239
|
+
).and.returnValue(
|
|
240
|
+
new Promise((resolve) => {
|
|
241
|
+
//@ts-ignore
|
|
242
|
+
resolve({
|
|
243
|
+
accessToken: "access-token",
|
|
244
|
+
});
|
|
245
|
+
})
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
spyOn(
|
|
249
|
+
PublicClientApplication.prototype,
|
|
250
|
+
"getActiveAccount"
|
|
251
|
+
).and.returnValue(sampleAccountInfo);
|
|
252
|
+
|
|
253
|
+
httpClient.get("https://graph.microsoft.com/v1.0/me").subscribe();
|
|
254
|
+
setTimeout(() => {
|
|
255
|
+
const request = httpMock.expectNone(
|
|
256
|
+
"https://graph.microsoft.com/v1.0/me"
|
|
257
|
+
);
|
|
258
|
+
expect(request).toBeUndefined();
|
|
259
|
+
expect(spy1).toHaveBeenCalledWith({
|
|
260
|
+
account: sampleAccountInfo,
|
|
261
|
+
scopes: ["user.read"],
|
|
262
|
+
});
|
|
263
|
+
expect(spy2).not.toHaveBeenCalled();
|
|
264
|
+
httpMock.verify();
|
|
265
|
+
|
|
266
|
+
_inProgress.next(InteractionStatus.None);
|
|
267
|
+
}, 200);
|
|
268
|
+
|
|
269
|
+
setTimeout(() => {
|
|
270
|
+
const request = httpMock.expectOne("https://graph.microsoft.com/v1.0/me");
|
|
271
|
+
request.flush({ data: "test" });
|
|
272
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
273
|
+
"Bearer access-token"
|
|
274
|
+
);
|
|
275
|
+
expect(spy2).toHaveBeenCalledWith({
|
|
276
|
+
account: sampleAccountInfo,
|
|
277
|
+
scopes: ["user.read"],
|
|
278
|
+
});
|
|
279
|
+
httpMock.verify();
|
|
280
|
+
done();
|
|
281
|
+
}, 400);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it("attaches authorization header with access token for protected resource with wildcard", (done) => {
|
|
285
|
+
const spy = spyOn(
|
|
286
|
+
PublicClientApplication.prototype,
|
|
287
|
+
"acquireTokenSilent"
|
|
288
|
+
).and.returnValue(
|
|
289
|
+
new Promise((resolve) => {
|
|
290
|
+
//@ts-ignore
|
|
291
|
+
resolve({
|
|
292
|
+
accessToken: "access-token",
|
|
293
|
+
});
|
|
294
|
+
})
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
298
|
+
sampleAccountInfo,
|
|
299
|
+
]);
|
|
300
|
+
|
|
301
|
+
httpClient.get("https://myapplication.com/user/1").subscribe();
|
|
302
|
+
setTimeout(() => {
|
|
303
|
+
const request = httpMock.expectOne("https://myapplication.com/user/1");
|
|
304
|
+
request.flush({ data: "test" });
|
|
305
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
306
|
+
"Bearer access-token"
|
|
307
|
+
);
|
|
308
|
+
expect(spy).toHaveBeenCalledWith({
|
|
309
|
+
account: sampleAccountInfo,
|
|
310
|
+
scopes: ["customscope.read"],
|
|
311
|
+
});
|
|
312
|
+
httpMock.verify();
|
|
313
|
+
done();
|
|
314
|
+
}, 200);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it("attaches authorization header with access token to url for protected resource with wildcard, url has multiple slashes", (done) => {
|
|
318
|
+
const spy = spyOn(
|
|
319
|
+
PublicClientApplication.prototype,
|
|
320
|
+
"acquireTokenSilent"
|
|
321
|
+
).and.returnValue(
|
|
322
|
+
new Promise((resolve) => {
|
|
323
|
+
//@ts-ignore
|
|
324
|
+
resolve({
|
|
325
|
+
accessToken: "access-token",
|
|
326
|
+
});
|
|
327
|
+
})
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
331
|
+
sampleAccountInfo,
|
|
332
|
+
]);
|
|
333
|
+
|
|
334
|
+
httpClient.get("https://myapplication.com/user/1/2/3").subscribe();
|
|
335
|
+
setTimeout(() => {
|
|
336
|
+
const request = httpMock.expectOne(
|
|
337
|
+
"https://myapplication.com/user/1/2/3"
|
|
338
|
+
);
|
|
339
|
+
request.flush({ data: "test" });
|
|
340
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
341
|
+
"Bearer access-token"
|
|
342
|
+
);
|
|
343
|
+
expect(spy).toHaveBeenCalledWith({
|
|
344
|
+
account: sampleAccountInfo,
|
|
345
|
+
scopes: ["customscope.read"],
|
|
346
|
+
});
|
|
347
|
+
httpMock.verify();
|
|
348
|
+
done();
|
|
349
|
+
}, 200);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it("attaches authorization header with access token for protected resource with multiple wildcards", (done) => {
|
|
353
|
+
const spy = spyOn(
|
|
354
|
+
PublicClientApplication.prototype,
|
|
355
|
+
"acquireTokenSilent"
|
|
356
|
+
).and.returnValue(
|
|
357
|
+
new Promise((resolve) => {
|
|
358
|
+
//@ts-ignore
|
|
359
|
+
resolve({
|
|
360
|
+
accessToken: "access-token",
|
|
361
|
+
});
|
|
362
|
+
})
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
366
|
+
sampleAccountInfo,
|
|
367
|
+
]);
|
|
368
|
+
|
|
369
|
+
httpClient.get("https://mail.myapplication.com/me").subscribe();
|
|
370
|
+
setTimeout(() => {
|
|
371
|
+
const request = httpMock.expectOne("https://mail.myapplication.com/me");
|
|
372
|
+
request.flush({ data: "test" });
|
|
373
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
374
|
+
"Bearer access-token"
|
|
375
|
+
);
|
|
376
|
+
expect(spy).toHaveBeenCalledWith({
|
|
377
|
+
account: sampleAccountInfo,
|
|
378
|
+
scopes: ["mail.read"],
|
|
379
|
+
});
|
|
380
|
+
httpMock.verify();
|
|
381
|
+
done();
|
|
382
|
+
}, 200);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it("attaches authorization header with access token for base url as protected resource", (done) => {
|
|
386
|
+
const spy = spyOn(
|
|
387
|
+
PublicClientApplication.prototype,
|
|
388
|
+
"acquireTokenSilent"
|
|
389
|
+
).and.returnValue(
|
|
390
|
+
new Promise((resolve) => {
|
|
391
|
+
//@ts-ignore
|
|
392
|
+
resolve({
|
|
393
|
+
accessToken: "access-token",
|
|
394
|
+
});
|
|
395
|
+
})
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
399
|
+
sampleAccountInfo,
|
|
400
|
+
]);
|
|
401
|
+
|
|
402
|
+
httpClient.get("http://localhost:3000/base").subscribe();
|
|
403
|
+
setTimeout(() => {
|
|
404
|
+
const request = httpMock.expectOne("http://localhost:3000/base");
|
|
405
|
+
request.flush({ data: "test" });
|
|
406
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
407
|
+
"Bearer access-token"
|
|
408
|
+
);
|
|
409
|
+
expect(spy).toHaveBeenCalledWith({
|
|
410
|
+
account: sampleAccountInfo,
|
|
411
|
+
scopes: ["base.scope"],
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
httpMock.verify();
|
|
415
|
+
done();
|
|
416
|
+
}, 200);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it("attaches authorization header with access token for multiple matching entries in protected resource, scopes are for first matching entry", (done) => {
|
|
420
|
+
const spy = spyOn(
|
|
421
|
+
PublicClientApplication.prototype,
|
|
422
|
+
"acquireTokenSilent"
|
|
423
|
+
).and.returnValue(
|
|
424
|
+
new Promise((resolve) => {
|
|
425
|
+
//@ts-ignore
|
|
426
|
+
resolve({
|
|
427
|
+
accessToken: "access-token",
|
|
428
|
+
});
|
|
429
|
+
})
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
433
|
+
sampleAccountInfo,
|
|
434
|
+
]);
|
|
435
|
+
|
|
436
|
+
httpClient.get("https://api.test.com").subscribe();
|
|
437
|
+
setTimeout(() => {
|
|
438
|
+
const request = httpMock.expectOne("https://api.test.com");
|
|
439
|
+
request.flush({ data: "test" });
|
|
440
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
441
|
+
"Bearer access-token"
|
|
442
|
+
);
|
|
443
|
+
expect(spy).toHaveBeenCalledWith({
|
|
444
|
+
account: sampleAccountInfo,
|
|
445
|
+
scopes: ["default.scope1"],
|
|
446
|
+
});
|
|
447
|
+
httpMock.verify();
|
|
448
|
+
done();
|
|
449
|
+
}, 200);
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it("does not attach authorization header when scopes set to null, and resource is before any base url or wildcards", (done) => {
|
|
453
|
+
httpClient
|
|
454
|
+
.get("http://localhost:3000/unprotect")
|
|
455
|
+
.subscribe((response) => expect(response).toBeTruthy());
|
|
456
|
+
|
|
457
|
+
const request = httpMock.expectOne("http://localhost:3000/unprotect");
|
|
458
|
+
request.flush({ data: "test" });
|
|
459
|
+
expect(request.request.headers.get("Authorization")).toBeUndefined;
|
|
460
|
+
httpMock.verify();
|
|
461
|
+
done();
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
it("does not attach authorization header when scopes set to null on specific http method, and resource is before any base url or wildcards", (done) => {
|
|
465
|
+
httpClient
|
|
466
|
+
.post("http://localhost:3000/unprotect/post", {})
|
|
467
|
+
.subscribe((response) => expect(response).toBeTruthy());
|
|
468
|
+
|
|
469
|
+
const request = httpMock.expectOne("http://localhost:3000/unprotect/post");
|
|
470
|
+
request.flush({ data: "test" });
|
|
471
|
+
expect(request.request.headers.get("Authorization")).toBeUndefined;
|
|
472
|
+
httpMock.verify();
|
|
473
|
+
done();
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it("attaches authorization header with access token from acquireTokenPopup if acquireTokenSilent fails in interceptor, interaction type is Popup and interaction status is None", (done) => {
|
|
477
|
+
const _inProgress = new BehaviorSubject<InteractionStatus>(
|
|
478
|
+
InteractionStatus.None
|
|
479
|
+
);
|
|
480
|
+
const msalBroadcastService = TestBed.inject(MsalBroadcastService);
|
|
481
|
+
|
|
482
|
+
msalBroadcastService.inProgress$ = _inProgress.asObservable();
|
|
483
|
+
|
|
484
|
+
const sampleError = new AuthError("123", "message");
|
|
485
|
+
const sampleAccessToken = {
|
|
486
|
+
accessToken: "123abc",
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
const spy1 = spyOn(
|
|
490
|
+
PublicClientApplication.prototype,
|
|
491
|
+
"acquireTokenSilent"
|
|
492
|
+
).and.returnValue(
|
|
493
|
+
new Promise((resolve, reject) => {
|
|
494
|
+
reject(sampleError);
|
|
495
|
+
})
|
|
496
|
+
);
|
|
497
|
+
|
|
498
|
+
const spy2 = spyOn(
|
|
499
|
+
PublicClientApplication.prototype,
|
|
500
|
+
"acquireTokenPopup"
|
|
501
|
+
).and.returnValue(
|
|
502
|
+
new Promise((resolve) => {
|
|
503
|
+
//@ts-ignore
|
|
504
|
+
resolve(sampleAccessToken);
|
|
505
|
+
})
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
509
|
+
sampleAccountInfo,
|
|
510
|
+
]);
|
|
511
|
+
|
|
512
|
+
httpClient.get("https://graph.microsoft.com/v1.0/me").subscribe();
|
|
513
|
+
setTimeout(() => {
|
|
514
|
+
const request = httpMock.expectOne("https://graph.microsoft.com/v1.0/me");
|
|
515
|
+
request.flush({ data: "test" });
|
|
516
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
517
|
+
"Bearer 123abc"
|
|
518
|
+
);
|
|
519
|
+
expect(spy1).toHaveBeenCalledWith({
|
|
520
|
+
account: sampleAccountInfo,
|
|
521
|
+
scopes: ["user.read"],
|
|
522
|
+
});
|
|
523
|
+
expect(spy2).toHaveBeenCalledWith({
|
|
524
|
+
account: sampleAccountInfo,
|
|
525
|
+
scopes: ["user.read"],
|
|
526
|
+
});
|
|
527
|
+
httpMock.verify();
|
|
528
|
+
done();
|
|
529
|
+
}, 200);
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it("does not attach authorization header if acquireTokenSilent fails in interceptor, interaction type is Redirect and interaction status is None", (done) => {
|
|
533
|
+
testInteractionType = InteractionType.Redirect;
|
|
534
|
+
initializeMsal();
|
|
535
|
+
|
|
536
|
+
const _inProgress = new BehaviorSubject<InteractionStatus>(
|
|
537
|
+
InteractionStatus.None
|
|
538
|
+
);
|
|
539
|
+
const msalBroadcastService = TestBed.inject(MsalBroadcastService);
|
|
540
|
+
|
|
541
|
+
msalBroadcastService.inProgress$ = _inProgress.asObservable();
|
|
542
|
+
|
|
543
|
+
const sampleError = new AuthError("123", "message");
|
|
544
|
+
|
|
545
|
+
spyOn(
|
|
546
|
+
PublicClientApplication.prototype,
|
|
547
|
+
"acquireTokenSilent"
|
|
548
|
+
).and.returnValue(
|
|
549
|
+
new Promise((resolve, reject) => {
|
|
550
|
+
reject(sampleError);
|
|
551
|
+
})
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
spyOn(
|
|
555
|
+
PublicClientApplication.prototype,
|
|
556
|
+
"acquireTokenRedirect"
|
|
557
|
+
).and.returnValue(
|
|
558
|
+
new Promise((resolve) => {
|
|
559
|
+
//@ts-ignore
|
|
560
|
+
resolve();
|
|
561
|
+
})
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
565
|
+
sampleAccountInfo,
|
|
566
|
+
]);
|
|
567
|
+
|
|
568
|
+
httpClient.get("https://graph.microsoft.com/v1.0/me").subscribe();
|
|
569
|
+
setTimeout(() => {
|
|
570
|
+
const request = httpMock.expectNone(
|
|
571
|
+
"https://graph.microsoft.com/v1.0/me"
|
|
572
|
+
);
|
|
573
|
+
expect(request).toBeUndefined();
|
|
574
|
+
httpMock.verify();
|
|
575
|
+
done();
|
|
576
|
+
}, 200);
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it("does not invoke interaction if acquireTokenSilent fails in interceptor and interaction status other than None", (done) => {
|
|
580
|
+
const _inProgress = new BehaviorSubject<InteractionStatus>(
|
|
581
|
+
InteractionStatus.Startup
|
|
582
|
+
);
|
|
583
|
+
const msalBroadcastService = TestBed.inject(MsalBroadcastService);
|
|
584
|
+
|
|
585
|
+
msalBroadcastService.inProgress$ = _inProgress.asObservable();
|
|
586
|
+
|
|
587
|
+
const sampleError = new AuthError("123", "message");
|
|
588
|
+
const sampleAccessToken = {
|
|
589
|
+
accessToken: "123abc",
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
const spy1 = spyOn(
|
|
593
|
+
PublicClientApplication.prototype,
|
|
594
|
+
"acquireTokenSilent"
|
|
595
|
+
).and.returnValue(
|
|
596
|
+
new Promise((resolve, reject) => {
|
|
597
|
+
reject(sampleError);
|
|
598
|
+
})
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
const spy2 = spyOn(
|
|
602
|
+
PublicClientApplication.prototype,
|
|
603
|
+
"acquireTokenPopup"
|
|
604
|
+
).and.returnValue(
|
|
605
|
+
new Promise((resolve) => {
|
|
606
|
+
//@ts-ignore
|
|
607
|
+
resolve(sampleAccessToken);
|
|
608
|
+
})
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
const spy3 = spyOn(
|
|
612
|
+
PublicClientApplication.prototype,
|
|
613
|
+
"acquireTokenRedirect"
|
|
614
|
+
).and.returnValue(
|
|
615
|
+
new Promise((resolve) => {
|
|
616
|
+
//@ts-ignore
|
|
617
|
+
resolve();
|
|
618
|
+
})
|
|
619
|
+
);
|
|
620
|
+
|
|
621
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
622
|
+
sampleAccountInfo,
|
|
623
|
+
]);
|
|
624
|
+
|
|
625
|
+
httpClient.get("https://graph.microsoft.com/v1.0/me").subscribe();
|
|
626
|
+
setTimeout(() => {
|
|
627
|
+
const request = httpMock.expectNone(
|
|
628
|
+
"https://graph.microsoft.com/v1.0/me"
|
|
629
|
+
);
|
|
630
|
+
expect(request).toBeUndefined();
|
|
631
|
+
expect(spy1).toHaveBeenCalledWith({
|
|
632
|
+
account: sampleAccountInfo,
|
|
633
|
+
scopes: ["user.read"],
|
|
634
|
+
});
|
|
635
|
+
expect(spy2).not.toHaveBeenCalled();
|
|
636
|
+
expect(spy3).not.toHaveBeenCalled();
|
|
637
|
+
httpMock.verify();
|
|
638
|
+
done();
|
|
639
|
+
}, 200);
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it("does not invoke interaction if acquireTokenSilent fails in interceptor and interaction status other than None and should invoke acquireTokenSilent when status became None", (done) => {
|
|
643
|
+
const _inProgress = new BehaviorSubject<InteractionStatus>(
|
|
644
|
+
InteractionStatus.Startup
|
|
645
|
+
);
|
|
646
|
+
const msalBroadcastService = TestBed.inject(MsalBroadcastService);
|
|
647
|
+
|
|
648
|
+
msalBroadcastService.inProgress$ = _inProgress.asObservable();
|
|
649
|
+
|
|
650
|
+
const sampleError = new AuthError("123", "message");
|
|
651
|
+
const sampleAccessToken = {
|
|
652
|
+
accessToken: "123abc",
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
const spy1 = spyOn(
|
|
656
|
+
PublicClientApplication.prototype,
|
|
657
|
+
"acquireTokenSilent"
|
|
658
|
+
).and.returnValue(
|
|
659
|
+
new Promise((resolve, reject) => {
|
|
660
|
+
reject(sampleError);
|
|
661
|
+
})
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
const spy2 = spyOn(
|
|
665
|
+
PublicClientApplication.prototype,
|
|
666
|
+
"acquireTokenPopup"
|
|
667
|
+
).and.returnValue(
|
|
668
|
+
new Promise((resolve) => {
|
|
669
|
+
//@ts-ignore
|
|
670
|
+
resolve(sampleAccessToken);
|
|
671
|
+
})
|
|
672
|
+
);
|
|
673
|
+
|
|
674
|
+
const spy3 = spyOn(
|
|
675
|
+
PublicClientApplication.prototype,
|
|
676
|
+
"acquireTokenRedirect"
|
|
677
|
+
).and.returnValue(
|
|
678
|
+
new Promise((resolve) => {
|
|
679
|
+
//@ts-ignore
|
|
680
|
+
resolve();
|
|
681
|
+
})
|
|
682
|
+
);
|
|
683
|
+
|
|
684
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
685
|
+
sampleAccountInfo,
|
|
686
|
+
]);
|
|
687
|
+
|
|
688
|
+
httpClient.get("https://graph.microsoft.com/v1.0/me").subscribe();
|
|
689
|
+
setTimeout(() => {
|
|
690
|
+
const request = httpMock.expectNone(
|
|
691
|
+
"https://graph.microsoft.com/v1.0/me"
|
|
692
|
+
);
|
|
693
|
+
expect(request).toBeUndefined();
|
|
694
|
+
expect(spy1).toHaveBeenCalledWith({
|
|
695
|
+
account: sampleAccountInfo,
|
|
696
|
+
scopes: ["user.read"],
|
|
697
|
+
});
|
|
698
|
+
expect(spy2).not.toHaveBeenCalled();
|
|
699
|
+
expect(spy3).not.toHaveBeenCalled();
|
|
700
|
+
httpMock.verify();
|
|
701
|
+
|
|
702
|
+
_inProgress.next(InteractionStatus.None);
|
|
703
|
+
}, 200);
|
|
704
|
+
|
|
705
|
+
setTimeout(() => {
|
|
706
|
+
expect(spy1.calls.mostRecent().args).toEqual([
|
|
707
|
+
{ account: sampleAccountInfo, scopes: ["user.read"] },
|
|
708
|
+
]);
|
|
709
|
+
expect(spy1).toHaveBeenCalledTimes(2);
|
|
710
|
+
done();
|
|
711
|
+
}, 400);
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
it("keeps original authority, https://login.microsoftonline.com/common", (done) => {
|
|
715
|
+
const originalAuthority = "https://login.microsoftonline.com/common";
|
|
716
|
+
|
|
717
|
+
testInterceptorConfig.authRequest = {
|
|
718
|
+
authority: originalAuthority,
|
|
719
|
+
};
|
|
720
|
+
initializeMsal();
|
|
721
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
722
|
+
sampleAccountInfo,
|
|
723
|
+
]);
|
|
724
|
+
const spy = spyOn(
|
|
725
|
+
PublicClientApplication.prototype,
|
|
726
|
+
"acquireTokenSilent"
|
|
727
|
+
).and.callFake(
|
|
728
|
+
(silentRequest: SilentRequest) =>
|
|
729
|
+
new Promise((resolve) => {
|
|
730
|
+
//@ts-ignore
|
|
731
|
+
resolve({
|
|
732
|
+
accessToken: `access-token-for-${silentRequest.authority}`,
|
|
733
|
+
});
|
|
734
|
+
})
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
httpClient.get("https://api.test.com").subscribe();
|
|
738
|
+
setTimeout(() => {
|
|
739
|
+
const request = httpMock.expectOne("https://api.test.com");
|
|
740
|
+
request.flush({ data: "test" });
|
|
741
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
742
|
+
"Bearer access-token-for-https://login.microsoftonline.com/common"
|
|
743
|
+
);
|
|
744
|
+
expect(spy).toHaveBeenCalledWith({
|
|
745
|
+
authority: originalAuthority,
|
|
746
|
+
account: sampleAccountInfo,
|
|
747
|
+
scopes: ["default.scope1"],
|
|
748
|
+
});
|
|
749
|
+
httpMock.verify();
|
|
750
|
+
done();
|
|
751
|
+
}, 200);
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
it("calls dynamic authority with account, authority override", (done) => {
|
|
755
|
+
testInterceptorConfig.authRequest = (msalService, httpReq, authRequest) => {
|
|
756
|
+
return {
|
|
757
|
+
...authRequest,
|
|
758
|
+
authority: `https://login.microsoftonline.com/${authRequest.account.tenantId}`,
|
|
759
|
+
};
|
|
760
|
+
};
|
|
761
|
+
initializeMsal();
|
|
762
|
+
spyOn(
|
|
763
|
+
PublicClientApplication.prototype,
|
|
764
|
+
"getActiveAccount"
|
|
765
|
+
).and.returnValue(sampleAccountInfo);
|
|
766
|
+
const spy = spyOn(
|
|
767
|
+
PublicClientApplication.prototype,
|
|
768
|
+
"acquireTokenSilent"
|
|
769
|
+
).and.callFake(
|
|
770
|
+
(silentRequest: SilentRequest) =>
|
|
771
|
+
new Promise((resolve) => {
|
|
772
|
+
//@ts-ignore
|
|
773
|
+
resolve({
|
|
774
|
+
accessToken: `access-token-for-${silentRequest.authority}`,
|
|
775
|
+
});
|
|
776
|
+
})
|
|
777
|
+
);
|
|
778
|
+
|
|
779
|
+
httpClient.get("https://api.test.com").subscribe();
|
|
780
|
+
setTimeout(() => {
|
|
781
|
+
const request = httpMock.expectOne("https://api.test.com");
|
|
782
|
+
request.flush({ data: "test" });
|
|
783
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
784
|
+
"Bearer access-token-for-https://login.microsoftonline.com/test-tenant"
|
|
785
|
+
);
|
|
786
|
+
expect(spy).toHaveBeenCalledWith({
|
|
787
|
+
account: sampleAccountInfo,
|
|
788
|
+
authority: "https://login.microsoftonline.com/test-tenant",
|
|
789
|
+
scopes: ["default.scope1"],
|
|
790
|
+
});
|
|
791
|
+
httpMock.verify();
|
|
792
|
+
done();
|
|
793
|
+
}, 200);
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
it("attaches authorization header with access token for protected resource with queries", (done) => {
|
|
797
|
+
spyOn(
|
|
798
|
+
PublicClientApplication.prototype,
|
|
799
|
+
"getActiveAccount"
|
|
800
|
+
).and.returnValue(sampleAccountInfo);
|
|
801
|
+
const spy = spyOn(
|
|
802
|
+
PublicClientApplication.prototype,
|
|
803
|
+
"acquireTokenSilent"
|
|
804
|
+
).and.returnValue(
|
|
805
|
+
new Promise((resolve) => {
|
|
806
|
+
//@ts-ignore
|
|
807
|
+
resolve({
|
|
808
|
+
accessToken: "access-token",
|
|
809
|
+
});
|
|
810
|
+
})
|
|
811
|
+
);
|
|
812
|
+
|
|
813
|
+
httpClient.get("http://localhost:9876/tenant?abc").subscribe();
|
|
814
|
+
setTimeout(() => {
|
|
815
|
+
const request = httpMock.expectOne("http://localhost:9876/tenant?abc");
|
|
816
|
+
request.flush({ data: "test" });
|
|
817
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
818
|
+
"Bearer access-token"
|
|
819
|
+
);
|
|
820
|
+
expect(spy).toHaveBeenCalledWith({
|
|
821
|
+
account: sampleAccountInfo,
|
|
822
|
+
scopes: ["query.scope"],
|
|
823
|
+
});
|
|
824
|
+
httpMock.verify();
|
|
825
|
+
done();
|
|
826
|
+
}, 200);
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
it("attaches authorization header with access token for protected resource with trailing slash", (done) => {
|
|
830
|
+
const spy = spyOn(
|
|
831
|
+
PublicClientApplication.prototype,
|
|
832
|
+
"acquireTokenSilent"
|
|
833
|
+
).and.returnValue(
|
|
834
|
+
new Promise((resolve) => {
|
|
835
|
+
//@ts-ignore
|
|
836
|
+
resolve({
|
|
837
|
+
accessToken: "access-token",
|
|
838
|
+
});
|
|
839
|
+
})
|
|
840
|
+
);
|
|
841
|
+
|
|
842
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
843
|
+
sampleAccountInfo,
|
|
844
|
+
]);
|
|
845
|
+
|
|
846
|
+
httpClient.get("http://applicationA/slash").subscribe();
|
|
847
|
+
setTimeout(() => {
|
|
848
|
+
const request = httpMock.expectOne("http://applicationA/slash");
|
|
849
|
+
request.flush({ data: "test" });
|
|
850
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
851
|
+
"Bearer access-token"
|
|
852
|
+
);
|
|
853
|
+
expect(spy).toHaveBeenCalledWith({
|
|
854
|
+
account: sampleAccountInfo,
|
|
855
|
+
scopes: ["customA.scope"],
|
|
856
|
+
});
|
|
857
|
+
httpMock.verify();
|
|
858
|
+
done();
|
|
859
|
+
}, 200);
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
it("attaches authorization header with access token for endpoint with trailing slash", (done) => {
|
|
863
|
+
const spy = spyOn(
|
|
864
|
+
PublicClientApplication.prototype,
|
|
865
|
+
"acquireTokenSilent"
|
|
866
|
+
).and.returnValue(
|
|
867
|
+
new Promise((resolve) => {
|
|
868
|
+
//@ts-ignore
|
|
869
|
+
resolve({
|
|
870
|
+
accessToken: "access-token",
|
|
871
|
+
});
|
|
872
|
+
})
|
|
873
|
+
);
|
|
874
|
+
|
|
875
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
876
|
+
sampleAccountInfo,
|
|
877
|
+
]);
|
|
878
|
+
|
|
879
|
+
httpClient.get("http://applicationB/noSlash/").subscribe();
|
|
880
|
+
setTimeout(() => {
|
|
881
|
+
const request = httpMock.expectOne("http://applicationB/noSlash/");
|
|
882
|
+
request.flush({ data: "test" });
|
|
883
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
884
|
+
"Bearer access-token"
|
|
885
|
+
);
|
|
886
|
+
expect(spy).toHaveBeenCalledWith({
|
|
887
|
+
account: sampleAccountInfo,
|
|
888
|
+
scopes: ["customB.scope"],
|
|
889
|
+
});
|
|
890
|
+
httpMock.verify();
|
|
891
|
+
done();
|
|
892
|
+
}, 200);
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
it("attaches authorization header with access token for relative endpoint", (done) => {
|
|
896
|
+
const spy = spyOn(
|
|
897
|
+
PublicClientApplication.prototype,
|
|
898
|
+
"acquireTokenSilent"
|
|
899
|
+
).and.returnValue(
|
|
900
|
+
new Promise((resolve) => {
|
|
901
|
+
//@ts-ignore
|
|
902
|
+
resolve({
|
|
903
|
+
accessToken: "access-token",
|
|
904
|
+
});
|
|
905
|
+
})
|
|
906
|
+
);
|
|
907
|
+
|
|
908
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
909
|
+
sampleAccountInfo,
|
|
910
|
+
]);
|
|
911
|
+
|
|
912
|
+
httpClient.get("http://localhost:9876/relative/me").subscribe();
|
|
913
|
+
setTimeout(() => {
|
|
914
|
+
const request = httpMock.expectOne("http://localhost:9876/relative/me");
|
|
915
|
+
request.flush({ data: "test" });
|
|
916
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
917
|
+
"Bearer access-token"
|
|
918
|
+
);
|
|
919
|
+
expect(spy).toHaveBeenCalledWith({
|
|
920
|
+
account: sampleAccountInfo,
|
|
921
|
+
scopes: ["relative.scope"],
|
|
922
|
+
});
|
|
923
|
+
httpMock.verify();
|
|
924
|
+
done();
|
|
925
|
+
}, 200);
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
it("attaches authorization header with access token for relative endpoint which includes query", (done) => {
|
|
929
|
+
const spy = spyOn(
|
|
930
|
+
PublicClientApplication.prototype,
|
|
931
|
+
"acquireTokenSilent"
|
|
932
|
+
).and.returnValue(
|
|
933
|
+
new Promise((resolve) => {
|
|
934
|
+
//@ts-ignore
|
|
935
|
+
resolve({
|
|
936
|
+
accessToken: "access-token",
|
|
937
|
+
});
|
|
938
|
+
})
|
|
939
|
+
);
|
|
940
|
+
|
|
941
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
942
|
+
sampleAccountInfo,
|
|
943
|
+
]);
|
|
944
|
+
|
|
945
|
+
httpClient.get("/tenant?abc").subscribe();
|
|
946
|
+
setTimeout(() => {
|
|
947
|
+
const request = httpMock.expectOne("/tenant?abc");
|
|
948
|
+
request.flush({ data: "test" });
|
|
949
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
950
|
+
"Bearer access-token"
|
|
951
|
+
);
|
|
952
|
+
expect(spy).toHaveBeenCalledWith({
|
|
953
|
+
account: sampleAccountInfo,
|
|
954
|
+
scopes: ["query.scope"],
|
|
955
|
+
});
|
|
956
|
+
httpMock.verify();
|
|
957
|
+
done();
|
|
958
|
+
}, 200);
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
it("attaches authorization header with access token for endpoint with HTTP methods specified", (done) => {
|
|
962
|
+
const spy = spyOn(
|
|
963
|
+
PublicClientApplication.prototype,
|
|
964
|
+
"acquireTokenSilent"
|
|
965
|
+
).and.returnValue(
|
|
966
|
+
new Promise((resolve) => {
|
|
967
|
+
//@ts-ignore
|
|
968
|
+
resolve({
|
|
969
|
+
accessToken: "access-token",
|
|
970
|
+
});
|
|
971
|
+
})
|
|
972
|
+
);
|
|
973
|
+
|
|
974
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
975
|
+
sampleAccountInfo,
|
|
976
|
+
]);
|
|
977
|
+
|
|
978
|
+
httpClient.post("http://applicationC.com", {}).subscribe();
|
|
979
|
+
setTimeout(() => {
|
|
980
|
+
const request = httpMock.expectOne("http://applicationC.com");
|
|
981
|
+
request.flush({ data: "test" });
|
|
982
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
983
|
+
"Bearer access-token"
|
|
984
|
+
);
|
|
985
|
+
expect(spy).toHaveBeenCalledWith({
|
|
986
|
+
account: sampleAccountInfo,
|
|
987
|
+
scopes: ["write.scope"],
|
|
988
|
+
});
|
|
989
|
+
httpMock.verify();
|
|
990
|
+
done();
|
|
991
|
+
}, 200);
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
it("does not attach authorization header when request HTTP method is not in protectedResourceMap", (done) => {
|
|
995
|
+
httpClient
|
|
996
|
+
.get("http://applicationC.com")
|
|
997
|
+
.subscribe((response) => expect(response).toBeTruthy());
|
|
998
|
+
|
|
999
|
+
const request = httpMock.expectOne("http://applicationC.com");
|
|
1000
|
+
request.flush({ data: "test" });
|
|
1001
|
+
expect(request.request.headers.get("Authorization")).toBeUndefined;
|
|
1002
|
+
httpMock.verify();
|
|
1003
|
+
done();
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
it("attaches authorization header with access token for endpoint with scopes in string array and with HTTP methods specified", (done) => {
|
|
1007
|
+
const spy = spyOn(
|
|
1008
|
+
PublicClientApplication.prototype,
|
|
1009
|
+
"acquireTokenSilent"
|
|
1010
|
+
).and.returnValue(
|
|
1011
|
+
new Promise((resolve) => {
|
|
1012
|
+
//@ts-ignore
|
|
1013
|
+
resolve({
|
|
1014
|
+
accessToken: "access-token",
|
|
1015
|
+
});
|
|
1016
|
+
})
|
|
1017
|
+
);
|
|
1018
|
+
|
|
1019
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
1020
|
+
sampleAccountInfo,
|
|
1021
|
+
]);
|
|
1022
|
+
|
|
1023
|
+
httpClient.get("http://applicationD.com").subscribe();
|
|
1024
|
+
setTimeout(() => {
|
|
1025
|
+
const request = httpMock.expectOne("http://applicationD.com");
|
|
1026
|
+
request.flush({ data: "test" });
|
|
1027
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
1028
|
+
"Bearer access-token"
|
|
1029
|
+
);
|
|
1030
|
+
expect(spy).toHaveBeenCalledWith({
|
|
1031
|
+
account: sampleAccountInfo,
|
|
1032
|
+
scopes: ["all.scope", "read.scope"],
|
|
1033
|
+
});
|
|
1034
|
+
httpMock.verify();
|
|
1035
|
+
done();
|
|
1036
|
+
}, 200);
|
|
1037
|
+
});
|
|
1038
|
+
|
|
1039
|
+
it("does not attach authorization header if request HTTP method with scope is not in protectedResourceMap", (done) => {
|
|
1040
|
+
httpClient
|
|
1041
|
+
.get("http://applicationC.com")
|
|
1042
|
+
.subscribe((response) => expect(response).toBeTruthy());
|
|
1043
|
+
|
|
1044
|
+
const request = httpMock.expectOne("http://applicationC.com");
|
|
1045
|
+
request.flush({ data: "test" });
|
|
1046
|
+
expect(request.request.headers.get("Authorization")).toBeUndefined;
|
|
1047
|
+
httpMock.verify();
|
|
1048
|
+
done();
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1051
|
+
it("attaches authorization header with access token for endpoint with HTTP methods specified, regardless of casing of HTTP method", (done) => {
|
|
1052
|
+
const spy = spyOn(
|
|
1053
|
+
PublicClientApplication.prototype,
|
|
1054
|
+
"acquireTokenSilent"
|
|
1055
|
+
).and.returnValue(
|
|
1056
|
+
new Promise((resolve) => {
|
|
1057
|
+
//@ts-ignore
|
|
1058
|
+
resolve({
|
|
1059
|
+
accessToken: "access-token",
|
|
1060
|
+
});
|
|
1061
|
+
})
|
|
1062
|
+
);
|
|
1063
|
+
|
|
1064
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
1065
|
+
sampleAccountInfo,
|
|
1066
|
+
]);
|
|
1067
|
+
|
|
1068
|
+
httpClient.post("http://applicationD.com", {}).subscribe();
|
|
1069
|
+
setTimeout(() => {
|
|
1070
|
+
const request = httpMock.expectOne("http://applicationD.com");
|
|
1071
|
+
request.flush({ data: "test" });
|
|
1072
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
1073
|
+
"Bearer access-token"
|
|
1074
|
+
);
|
|
1075
|
+
expect(spy).toHaveBeenCalledWith({
|
|
1076
|
+
account: sampleAccountInfo,
|
|
1077
|
+
scopes: ["all.scope", "info.scope"],
|
|
1078
|
+
});
|
|
1079
|
+
httpMock.verify();
|
|
1080
|
+
done();
|
|
1081
|
+
}, 200);
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
it("does not attach authorization header if relative endpoints match but absolute url does not match", (done) => {
|
|
1085
|
+
httpClient
|
|
1086
|
+
.get("http://applicationZ.com/noSlash")
|
|
1087
|
+
.subscribe((response) => expect(response).toBeTruthy());
|
|
1088
|
+
|
|
1089
|
+
const request = httpMock.expectOne("http://applicationZ.com/noSlash");
|
|
1090
|
+
request.flush({ data: "test" });
|
|
1091
|
+
expect(request.request.headers.get("Authorization")).toBeUndefined;
|
|
1092
|
+
httpMock.verify();
|
|
1093
|
+
done();
|
|
1094
|
+
});
|
|
1095
|
+
|
|
1096
|
+
it("attaches authorization header with access token for correct endpoint even though an earlier endpoint in the protectedResourceMap has a matching relative endpoint", (done) => {
|
|
1097
|
+
const spy = spyOn(
|
|
1098
|
+
PublicClientApplication.prototype,
|
|
1099
|
+
"acquireTokenSilent"
|
|
1100
|
+
).and.returnValue(
|
|
1101
|
+
new Promise((resolve) => {
|
|
1102
|
+
//@ts-ignore
|
|
1103
|
+
resolve({
|
|
1104
|
+
accessToken: "access-token",
|
|
1105
|
+
});
|
|
1106
|
+
})
|
|
1107
|
+
);
|
|
1108
|
+
|
|
1109
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
1110
|
+
sampleAccountInfo,
|
|
1111
|
+
]);
|
|
1112
|
+
|
|
1113
|
+
httpClient.post("http://applicationF.com/profile/", {}).subscribe();
|
|
1114
|
+
setTimeout(() => {
|
|
1115
|
+
const request = httpMock.expectOne("http://applicationF.com/profile/");
|
|
1116
|
+
request.flush({ data: "test" });
|
|
1117
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
1118
|
+
"Bearer access-token"
|
|
1119
|
+
);
|
|
1120
|
+
expect(spy).toHaveBeenCalledWith({
|
|
1121
|
+
account: sampleAccountInfo,
|
|
1122
|
+
scopes: ["customF.scope"],
|
|
1123
|
+
});
|
|
1124
|
+
httpMock.verify();
|
|
1125
|
+
done();
|
|
1126
|
+
}, 200);
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1129
|
+
it("attaches authorization header with access token when endpoint match is in HostNameAndPort instead of query string", (done) => {
|
|
1130
|
+
const spy = spyOn(
|
|
1131
|
+
PublicClientApplication.prototype,
|
|
1132
|
+
"acquireTokenSilent"
|
|
1133
|
+
).and.returnValue(
|
|
1134
|
+
new Promise((resolve) => {
|
|
1135
|
+
//@ts-ignore
|
|
1136
|
+
resolve({
|
|
1137
|
+
accessToken: "access-token",
|
|
1138
|
+
});
|
|
1139
|
+
})
|
|
1140
|
+
);
|
|
1141
|
+
|
|
1142
|
+
spyOn(PublicClientApplication.prototype, "getAllAccounts").and.returnValue([
|
|
1143
|
+
sampleAccountInfo,
|
|
1144
|
+
]);
|
|
1145
|
+
|
|
1146
|
+
httpClient
|
|
1147
|
+
.get(
|
|
1148
|
+
"https://MY_API_SITE_1/api/sites?$filter=siteUrl eq 'https://MY_API_SITE_2'"
|
|
1149
|
+
)
|
|
1150
|
+
.subscribe();
|
|
1151
|
+
|
|
1152
|
+
setTimeout(() => {
|
|
1153
|
+
const request = httpMock.expectOne(
|
|
1154
|
+
"https://MY_API_SITE_1/api/sites?$filter=siteUrl eq 'https://MY_API_SITE_2'"
|
|
1155
|
+
);
|
|
1156
|
+
request.flush({ data: "test" });
|
|
1157
|
+
expect(request.request.headers.get("Authorization")).toEqual(
|
|
1158
|
+
"Bearer access-token"
|
|
1159
|
+
);
|
|
1160
|
+
expect(spy).toHaveBeenCalledWith({
|
|
1161
|
+
account: sampleAccountInfo,
|
|
1162
|
+
scopes: ["api://MY_API_SITE_1/as_user"],
|
|
1163
|
+
});
|
|
1164
|
+
httpMock.verify();
|
|
1165
|
+
done();
|
|
1166
|
+
}, 200);
|
|
1167
|
+
});
|
|
1168
|
+
});
|