blob: 64ad212a5ba4e0c6bf5589f4cda3a4c7a508cdc2 [file] [log] [blame]
Soares Chene5a65672017-07-21 10:08:301<!doctype html>
2<meta charset=utf-8>
3<title>RTCPeerConnection.prototype.peerIdentity</title>
4<script src="/resources/testharness.js"></script>
5<script src="/resources/testharnessreport.js"></script>
Mike Pennisiad2e6b72018-05-07 22:17:436<script src="identity-helper.sub.js"></script>
Soares Chene5a65672017-07-21 10:08:307<script>
8 'use strict';
9
10 // Test is based on the following editor draft:
11 // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
12
13 // The tests here interacts with the mock identity provider located at
14 // /.well-known/idp-proxy/mock-idp.js
15
Mike Pennisiad2e6b72018-05-07 22:17:4316 // The following helper functions are called from identity-helper.sub.js
Soares Chene5a65672017-07-21 10:08:3017 // parseAssertionResult
18 // getIdpDomains
19 // assert_rtcerror_rejection
20 // hostString
21
22 /*
23 9.6. RTCPeerConnection Interface Extensions
24 partial interface RTCPeerConnection {
25 void setIdentityProvider(DOMString provider,
26 optional RTCIdentityProviderOptions options);
27 Promise<DOMString> getIdentityAssertion();
28 readonly attribute Promise<RTCIdentityAssertion> peerIdentity;
29 readonly attribute DOMString? idpLoginUrl;
30 readonly attribute DOMString? idpErrorInfo;
31 };
32
33 dictionary RTCIdentityProviderOptions {
34 DOMString protocol = "default";
35 DOMString usernameHint;
36 DOMString peerIdentity;
37 };
38
39 [Constructor(DOMString idp, DOMString name)]
40 interface RTCIdentityAssertion {
41 attribute DOMString idp;
42 attribute DOMString name;
43 };
44 */
45
46 /*
47 4.3.2. setRemoteDescription
48 If an a=identity attribute is present in the session description, the browser
49 validates the identity assertion..
50
51 If the "peerIdentity" configuration is applied to the RTCPeerConnection, this
52 establishes a target peer identity of the provided value. Alternatively, if the
53 RTCPeerConnection has previously authenticated the identity of the peer (that
54 is, there is a current value for peerIdentity ), then this also establishes a
55 target peer identity.
56 */
Philipp Hancke45f94222018-05-08 14:58:3357 promise_test(t => {
Soares Chene5a65672017-07-21 10:08:3058 const pc1 = new RTCPeerConnection();
Philipp Hancke1622a022018-06-11 10:00:5359 t.add_cleanup(() => pc1.close());
Soares Chene5a65672017-07-21 10:08:3060 const pc2 = new RTCPeerConnection();
61
Philipp Hancke1622a022018-06-11 10:00:5362 t.add_cleanup(() => pc2.close());
63
Soares Chene5a65672017-07-21 10:08:3064 const port = window.location.port;
65 const [idpDomain] = getIdpDomains();
66 const idpHost = hostString(idpDomain, port);
67
68 pc1.setIdentityProvider(idpHost, {
69 protocol: 'mock-idp.js',
70 usernameHint: `alice@${idpDomain}`
71 });
72
73 return pc1.createOffer()
74 .then(offer => pc2.setRemoteDescription(offer))
75 .then(() => pc2.peerIdentity)
76 .then(identityAssertion => {
77 const { idp, name } = identityAssertion;
78 assert_equals(idp, idpDomain, `Expect IdP domain to be ${idpDomain}`);
79 assert_equals(identityAssertion, `alice@${idpDomain}`,
80 `Expect validated identity from mock-idp.js to be same as specified in usernameHint`);
81 });
82 }, 'setRemoteDescription() on offer with a=identity should establish peerIdentity');
83
84 /*
85 4.3.2. setRemoteDescription
86 The target peer identity cannot be changed once set. Once set, if a different
87 value is provided, the user agent MUST reject the returned promise with a newly
88 created InvalidModificationError and abort this operation. The RTCPeerConnection
89 MUST be closed if the validated peer identity does not match the target peer
90 identity.
91 */
92 promise_test(t => {
93 const port = window.location.port;
94 const [idpDomain] = getIdpDomains();
95 const idpHost = hostString(idpDomain, port);
96
97 const pc1 = new RTCPeerConnection();
Philipp Hancke1622a022018-06-11 10:00:5398 t.add_cleanup(() => pc1.close());
Soares Chene5a65672017-07-21 10:08:3099 const pc2 = new RTCPeerConnection({
100 peerIdentity: `bob@${idpDomain}`
101 });
102
Philipp Hancke1622a022018-06-11 10:00:53103 t.add_cleanup(() => pc2.close());
104
Soares Chene5a65672017-07-21 10:08:30105 pc1.setIdentityProvider(idpHost, {
106 protocol: 'mock-idp.js',
107 usernameHint: `alice@${idpDomain}`
108 });
109
110 return pc1.createOffer()
111 .then(offer =>
112 promise_rejects(t, 'InvalidModificationError',
113 pc2.setRemoteDescription(offer)))
114 .then(() => {
115 assert_true(pc2.signalingState, 'closed',
116 'Expect peer connection to be closed after mismatch peer identity');
117 });
118 }, 'setRemoteDescription() on offer with a=identity that resolve to value different from target peer identity should reject with InvalidModificationError');
119
120 /*
121 9.4. Verifying Identity Assertions
122 8. The RTCPeerConnection decodes the contents and validates that it contains a
123 fingerprint value for every a=fingerprint attribute in the session description.
124 This ensures that the certificate used by the remote peer for communications
125 is covered by the identity assertion.
126
127 If identity validation fails, the peerIdentity promise is rejected with a newly
128 created OperationError.
129
130 If identity validation fails and there is a target peer identity for the
131 RTCPeerConnection, the promise returned by setRemoteDescription MUST be rejected
132 with the same DOMException.
133 */
134 promise_test(t => {
135 const port = window.location.port;
136 const [idpDomain] = getIdpDomains();
137 const idpHost = hostString(idpDomain, port);
138
139 const pc1 = new RTCPeerConnection();
Philipp Hancke1622a022018-06-11 10:00:53140 t.add_cleanup(() => pc1.close());
Soares Chene5a65672017-07-21 10:08:30141 const pc2 = new RTCPeerConnection({
142 peerIdentity: `alice@${idpDomain}`
143 });
144
Philipp Hancke1622a022018-06-11 10:00:53145 t.add_cleanup(() => pc2.close());
146
Soares Chene5a65672017-07-21 10:08:30147 // Ask mockidp.js to return custom contents in validation result
148 pc1.setIdentityProvider(idpHost, {
149 protocol: 'mock-idp.js?validatorAction=return-custom-contents&contents=bogus',
150 usernameHint: `alice@${idpDomain}`
151 });
152
153 const peerIdentityPromise = pc2.peerIdentity;
154
155 return pc1.createOffer()
156 .then(offer => Promise.all([
157 promise_rejects(t, 'OperationError',
158 pc2.setRemoteDescription(offer)),
159 promise_rejects(t, 'OperationError',
160 peerIdentityPromise)
161 ]));
162 }, 'setRemoteDescription() with peerIdentity set and with IdP proxy that return validationAssertion with mismatch contents should reject with OperationError');
163
164 /*
165 9.4. Verifying Identity Assertions
166 9. The RTCPeerConnection validates that the domain portion of the identity matches
167 the domain of the IdP as described in [RTCWEB-SECURITY-ARCH]. If this check
168 fails then the identity validation fails.
169 */
170 promise_test(t => {
171 const port = window.location.port;
172 const [idpDomain1, idpDomain2] = getIdpDomains();
173 assert_not_equals(idpDomain1, idpDomain2,
174 'Sanity check two idpDomains are different');
175
176 const idpHost1 = hostString(idpDomain1, port);
177
178 const pc1 = new RTCPeerConnection();
Philipp Hancke1622a022018-06-11 10:00:53179 t.add_cleanup(() => pc1.close());
Soares Chene5a65672017-07-21 10:08:30180 const pc2 = new RTCPeerConnection({
181 peerIdentity: `alice@${idpDomain2}`
182 });
183
Philipp Hancke1622a022018-06-11 10:00:53184 t.add_cleanup(() => pc2.close());
185
Soares Chene5a65672017-07-21 10:08:30186 // mock-idp.js will return assertion of domain2 identity
187 // with domain1 in the idp.domain field
188 pc1.setIdentityProvider(idpHost1, {
189 protocol: 'mock-idp.js',
190 usernameHint: `alice@${idpDomain2}`
191 });
192
193 return pc1.getIdentityAssertion()
194 .then(assertionResultStr => {
195 const { idp, assertion } = parseAssertionResult(assertionResultStr);
196
197 assert_equals(idp.domain, idpDomain1,
198 'Sanity check domain of assertion is domain1');
199
200 assert_equals(assertion.options.usernameHint, `alice@${idpDomain2}`,
201 'Sanity check domain1 is going to validate a domain2 identity');
202
203 return pc1.createOffer();
204 })
205 .then(offer => Promise.all([
206 promise_rejects(t, 'OperationError',
207 pc2.setRemoteDescription(offer)),
208 promise_rejects(t, 'OperationError',
209 pc2.peerIdentity)
210 ]));
211 }, 'setRemoteDescription() and peerIdentity should reject with OperationError if IdP return validated identity that is different from its own domain');
212
213 /*
214 9.4 Verifying Identity Assertions
215 If identity validation fails and there is a target peer identity for the
216 RTCPeerConnection, the promise returned by setRemoteDescription MUST be rejected
217 with the same DOMException.
218
219 9.5 IdP Error Handling
220 - If an identity provider throws an exception or returns a promise that is ultimately
221 rejected, then the procedure that depends on the IdP MUST also fail. These types of
222 errors will cause an IdP failure with an RTCError with errorDetail set to
223 "idp-execution-failure".
224
225 Any error generated by the IdP MAY provide additional information in the
226 idpErrorInfo attribute. The information in this string is defined by the
227 IdP in use.
228 */
229 promise_test(t => {
230 const port = window.location.port;
231 const [idpDomain] = getIdpDomains();
232 const idpHost = hostString(idpDomain, port);
233
234 const pc1 = new RTCPeerConnection();
Philipp Hancke1622a022018-06-11 10:00:53235 t.add_cleanup(() => pc1.close());
Soares Chene5a65672017-07-21 10:08:30236 const pc2 = new RTCPeerConnection({
237 peerIdentity: `alice@${idpDomain}`
238 });
239
Philipp Hancke1622a022018-06-11 10:00:53240 t.add_cleanup(() => pc2.close());
241
Soares Chene5a65672017-07-21 10:08:30242 // Ask mock-idp.js to throw error during validation,
243 // i.e. during pc2.setRemoteDescription()
244 pc1.setIdentityProvider(idpHost, {
245 protocol: 'mock-idp.js?validatorAction=throw-error&errorInfo=bar',
246 usernameHint: `alice@${idpDomain}`
247 });
248
249 return pc1.createOffer()
250 .then(offer => Promise.all([
251 assert_rtcerror_rejection('idp-execution-failure',
252 pc2.setRemoteDescription(offer)),
253 assert_rtcerror_rejection('idp-execution-failure',
254 pc2.peerIdentity)
255 ]))
256 .then(() => {
257 assert_equals(pc2.idpErrorInfo, 'bar',
258 'Expect pc2.idpErrorInfo to be set to the err.idpErrorInfo thrown by mock-idp.js');
259 });
260 }, `When IdP throws error and pc has target peer identity, setRemoteDescription() and peerIdentity rejected with RTCError('idp-execution-error')`);
261
262 /*
263 4.3.2. setRemoteDescription
264 If there is no target peer identity, then setRemoteDescription does not await the
265 completion of identity validation.
266
267 9.5. IdP Error Handling
268 - If an identity provider throws an exception or returns a promise that is
269 ultimately rejected, then the procedure that depends on the IdP MUST also fail.
270 These types of errors will cause an IdP failure with an RTCError with errorDetail
271 set to "idp-execution-failure".
272
273 9.4. Verifying Identity Assertions
274 If identity validation fails and there is no a target peer identity, the value of
275 the peerIdentity MUST be set to a new, unresolved promise instance. This permits
276 the use of renegotiation (or a subsequent answer, if the session description was
277 a provisional answer) to resolve or reject the identity.
278 */
279 promise_test(t => {
280 const pc1 = new RTCPeerConnection();
Philipp Hancke1622a022018-06-11 10:00:53281 t.add_cleanup(() => pc1.close());
Soares Chene5a65672017-07-21 10:08:30282 const pc2 = new RTCPeerConnection();
283
Philipp Hancke1622a022018-06-11 10:00:53284 t.add_cleanup(() => pc2.close());
285
Soares Chene5a65672017-07-21 10:08:30286 const port = window.location.port;
287 const [idpDomain] = getIdpDomains();
288 const idpHost = hostString(idpDomain, port);
289
290 // Ask mock-idp.js to throw error during validation,
291 // i.e. during pc2.setRemoteDescription()
292 pc1.setIdentityProvider(idpHost, {
293 protocol: 'mock-idp.js?validatorAction=throw-error',
294 usernameHint: `alice@${idpDomain}`
295 });
296
297 const peerIdentityPromise1 = pc2.peerIdentity;
298
299 return pc1.createOffer()
300 .then(offer =>
301 // setRemoteDescription should succeed because there is no target peer identity set
302 pc2.setRemoteDescription(offer))
303 .then(() =>
304 assert_rtcerror_rejection('idp-execution-failure',
Soares Chend97f6c22017-11-20 14:20:48305 peerIdentityPromise1,
Soares Chene5a65672017-07-21 10:08:30306 `Expect first peerIdentity promise to be rejected with RTCError('idp-execution-failure')`))
307 .then(() => {
308 const peerIdentityPromise2 = pc2.peerIdentity;
309 assert_not_equals(peerIdentityPromise2, peerIdentityPromise1,
310 'Expect pc2.peerIdentity to be replaced with a fresh unresolved promise');
311
312 // regenerate an identity assertion with no test option to throw error
313 pc1.setIdentityProvider(idpHost, {
314 protocol: 'idp-test.js',
315 usernameHint: `alice@${idpDomain}`
316 });
317
318 return pc1.createOffer()
319 .then(offer => pc2.setRemoteDescription(offer))
320 .then(peerIdentityPromise2)
321 .then(identityAssertion => {
322 const { idp, name } = identityAssertion;
323
324 assert_equals(idp, idpDomain,
325 `Expect IdP domain to be ${idpDomain}`);
326
327 assert_equals(name, `alice@${idpDomain}`,
328 `Expect validated identity to be alice@${idpDomain}`);
329
330 assert_equals(pc2.peeridentity, peerIdentityPromise2,
331 'Expect pc2.peerIdentity to stay fixed after identity is validated');
332 });
333 });
334 }, 'IdP failure with no target peer identity should have following setRemoteDescription() succeed and replace pc.peerIdentity with a new promise');
335
336</script>