397 lines
15 KiB
HTML
397 lines
15 KiB
HTML
<!doctype html>
|
|
<meta charset=utf-8>
|
|
<title>RTCPeerConnection.prototype.getIdentityAssertion</title>
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="identity-helper.sub.js"></script>
|
|
<script>
|
|
'use strict';
|
|
|
|
// Test is based on the following editor draft:
|
|
// https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
|
|
|
|
// The tests here interacts with the mock identity provider located at
|
|
// /.well-known/idp-proxy/mock-idp.js
|
|
|
|
// The following helper functions are called from identity-helper.sub.js
|
|
// parseAssertionResult
|
|
// getIdpDomains
|
|
// assert_rtcerror_rejection
|
|
// hostString
|
|
|
|
/*
|
|
9.6. RTCPeerConnection Interface Extensions
|
|
partial interface RTCPeerConnection {
|
|
void setIdentityProvider(DOMString provider,
|
|
optional RTCIdentityProviderOptions options);
|
|
Promise<DOMString> getIdentityAssertion();
|
|
readonly attribute Promise<RTCIdentityAssertion> peerIdentity;
|
|
readonly attribute DOMString? idpLoginUrl;
|
|
readonly attribute DOMString? idpErrorInfo;
|
|
};
|
|
|
|
dictionary RTCIdentityProviderOptions {
|
|
DOMString protocol = "default";
|
|
DOMString usernameHint;
|
|
DOMString peerIdentity;
|
|
};
|
|
*/
|
|
promise_test(t => {
|
|
const pc = new RTCPeerConnection();
|
|
const port = window.location.port;
|
|
|
|
const [idpDomain] = getIdpDomains();
|
|
const idpHost = hostString(idpDomain, port);
|
|
|
|
pc.setIdentityProvider(idpHost, {
|
|
protocol: 'mock-idp.js?foo=bar',
|
|
usernameHint: `alice@${idpDomain}`,
|
|
peerIdentity: 'bob@example.org'
|
|
});
|
|
|
|
return pc.getIdentityAssertion()
|
|
.then(assertionResultStr => {
|
|
const { idp, assertion } = parseAssertionResult(assertionResultStr);
|
|
|
|
assert_equals(idp.domain, idpHost,
|
|
'Expect mock-idp.js to construct domain from its location.host');
|
|
|
|
assert_equals(idp.protocol, 'mock-idp.js',
|
|
'Expect mock-idp.js to return protocol of itself with no query string');
|
|
|
|
const {
|
|
watermark,
|
|
args,
|
|
env,
|
|
query,
|
|
} = assertion;
|
|
|
|
assert_equals(watermark, 'mock-idp.js.watermark',
|
|
'Expect assertion result to contain watermark left by mock-idp.js');
|
|
|
|
assert_equals(args.origin, window.origin,
|
|
'Expect args.origin argument to be the origin of this window');
|
|
|
|
assert_equals(env.location.href,
|
|
`https://${idpHost}/.well-known/idp-proxy/mock-idp.js?foo=bar`,
|
|
'Expect IdP proxy to be loaded with full well-known URL constructed from provider and protocol');
|
|
|
|
assert_equals(env.location.origin, `https://${idpHost}`,
|
|
'Expect IdP to have its own origin');
|
|
|
|
assert_equals(args.options.protocol, 'mock-idp.js?foo=bar',
|
|
'Expect options.protocol to be the same value as being passed from here');
|
|
|
|
assert_equals(args.options.usernameHint, `alice@${idpDomain}`,
|
|
'Expect options.usernameHint to be the same value as being passed from here');
|
|
|
|
assert_equals(args.options.peerIdentity, 'bob@example.org',
|
|
'Expect options.peerIdentity to be the same value as being passed from here');
|
|
|
|
assert_equals(query.foo, 'bar',
|
|
'Expect query string to be parsed by mock-idp.js and returned back');
|
|
});
|
|
}, 'getIdentityAssertion() should load IdP proxy and return assertion generated');
|
|
|
|
// When generating assertion, the RTCPeerConnection doesn't care if the returned assertion
|
|
// represents identity of different domain
|
|
promise_test(t => {
|
|
const pc = new RTCPeerConnection();
|
|
const port = window.location.port;
|
|
|
|
const [idpDomain1, idpDomain2] = getIdpDomains();
|
|
assert_not_equals(idpDomain1, idpDomain2,
|
|
'Sanity check two idpDomains are different');
|
|
|
|
// Ask mock-idp.js to return a custom domain idpDomain2 and custom protocol foo
|
|
pc.setIdentityProvider(hostString(idpDomain1, port), {
|
|
protocol: `mock-idp.js?generatorAction=return-custom-idp&domain=${idpDomain2}&protocol=foo`,
|
|
usernameHint: `alice@${idpDomain2}`,
|
|
});
|
|
|
|
return pc.getIdentityAssertion()
|
|
.then(assertionResultStr => {
|
|
const { idp, assertion } = parseAssertionResult(assertionResultStr);
|
|
assert_equals(idp.domain, idpDomain2);
|
|
assert_equals(idp.protocol, 'foo');
|
|
assert_equals(assertion.args.options.usernameHint, `alice@${idpDomain2}`);
|
|
});
|
|
}, 'getIdentityAssertion() should succeed if mock-idp.js return different domain and protocol in assertion');
|
|
|
|
/*
|
|
9.3. Requesting Identity Assertions
|
|
4. If the IdP proxy produces an error or returns a promise that does not resolve to
|
|
a valid RTCIdentityValidationResult (see 9.5 IdP Error Handling), then identity
|
|
validation fails.
|
|
|
|
9.5. IdP Error Handling
|
|
- If an identity provider throws an exception or returns a promise that is ultimately
|
|
rejected, then the procedure that depends on the IdP MUST also fail. These types of
|
|
errors will cause an IdP failure with an RTCError with errorDetail set to
|
|
"idp-execution-failure".
|
|
|
|
9.6. RTCPeerConnection Interface Extensions
|
|
idpErrorInfo
|
|
An attribute that the IdP can use to pass additional information back to the
|
|
applications about the error. The format of this string is defined by the IdP and
|
|
may be JSON.
|
|
*/
|
|
promise_test(t => {
|
|
const pc = new RTCPeerConnection();
|
|
|
|
assert_equals(pc.idpErrorInfo, null,
|
|
'Expect initial pc.idpErrorInfo to be null');
|
|
|
|
const port = window.location.port;
|
|
const [idpDomain] = getIdpDomains();
|
|
|
|
// Ask mock-idp.js to throw an error with err.errorInfo set to bar
|
|
pc.setIdentityProvider(hostString(idpDomain, port), {
|
|
protocol: `mock-idp.js?generatorAction=throw-error&errorInfo=bar`,
|
|
usernameHint: `alice@${idpDomain}`,
|
|
});
|
|
|
|
return assert_rtcerror_rejection('idp-execution-failure',
|
|
pc.getIdentityAssertion())
|
|
.then(() => {
|
|
assert_equals(pc.idpErrorInfo, 'bar',
|
|
'Expect pc.idpErrorInfo to be set to the err.idpErrorInfo thrown by mock-idp.js');
|
|
});
|
|
}, `getIdentityAssertion() should reject with RTCError('idp-execution-failure') if mock-idp.js throws error`);
|
|
|
|
/*
|
|
9.5. IdP Error Handling
|
|
- If the script loaded from the identity provider is not valid JavaScript or does
|
|
not implement the correct interfaces, it causes an IdP failure with an RTCError
|
|
with errorDetail set to "idp-bad-script-failure".
|
|
*/
|
|
promise_test(t => {
|
|
const pc = new RTCPeerConnection();
|
|
|
|
const port = window.location.port;
|
|
const [idpDomain] = getIdpDomains();
|
|
|
|
// Ask mock-idp.js to not register its callback to the
|
|
// RTCIdentityProviderRegistrar
|
|
pc.setIdentityProvider(hostString(idpDomain, port), {
|
|
protocol: `mock-idp.js?action=do-not-register`,
|
|
usernameHint: `alice@${idpDomain}`,
|
|
});
|
|
|
|
return assert_rtcerror_rejection('idp-bad-script-failure',
|
|
pc.getIdentityAssertion());
|
|
|
|
}, `getIdentityAssertion() should reject with RTCError('idp-bad-script-failure') if IdP proxy script do not register its callback`);
|
|
|
|
/*
|
|
9.3. Requesting Identity Assertions
|
|
4. If the IdP proxy produces an error or returns a promise that does not resolve
|
|
to a valid RTCIdentityAssertionResult (see 9.5 IdP Error Handling), then assertion
|
|
generation fails.
|
|
*/
|
|
promise_test(t => {
|
|
const pc = new RTCPeerConnection();
|
|
|
|
const port = window.location.port;
|
|
const [idpDomain] = getIdpDomains();
|
|
|
|
// Ask mock-idp.js to return an invalid result that is not proper
|
|
// RTCIdentityAssertionResult
|
|
pc.setIdentityProvider(hostString(idpDomain, port), {
|
|
protocol: `mock-idp.js?generatorAction=return-invalid-result`,
|
|
usernameHint: `alice@${idpDomain}`,
|
|
});
|
|
|
|
return promise_rejects_dom(t, 'OperationError',
|
|
pc.getIdentityAssertion());
|
|
}, `getIdentityAssertion() should reject with OperationError if mock-idp.js return invalid result`);
|
|
|
|
/*
|
|
9.5. IdP Error Handling
|
|
- A RTCPeerConnection might be configured with an identity provider, but loading of
|
|
the IdP URI fails. Any procedure that attempts to invoke such an identity provider
|
|
and cannot load the URI fails with an RTCError with errorDetail set to
|
|
"idp-load-failure" and the httpRequestStatusCode attribute of the error set to the
|
|
HTTP status code of the response.
|
|
*/
|
|
promise_test(t => {
|
|
const pc = new RTCPeerConnection();
|
|
|
|
pc.setIdentityProvider('nonexistent.{{domains[]}}', {
|
|
protocol: `non-existent`,
|
|
usernameHint: `alice@example.org`,
|
|
});
|
|
|
|
return assert_rtcerror_rejection('idp-load-failure',
|
|
pc.getIdentityAssertion());
|
|
}, `getIdentityAssertion() should reject with RTCError('idp-load-failure') if IdP cannot be loaded`);
|
|
|
|
/*
|
|
9.3.1. User Login Procedure
|
|
Rejecting the promise returned by generateAssertion will cause the error to
|
|
propagate to the application. Login errors are indicated by rejecting the
|
|
promise with an RTCError with errorDetail set to "idp-need-login".
|
|
|
|
The URL to login at will be passed to the application in the idpLoginUrl
|
|
attribute of the RTCPeerConnection.
|
|
|
|
9.5. IdP Error Handling
|
|
- If the identity provider requires the user to login, the operation will fail
|
|
RTCError with errorDetail set to "idp-need-login" and the idpLoginUrl attribute
|
|
of the error set to the URL that can be used to login.
|
|
*/
|
|
promise_test(t => {
|
|
const pc = new RTCPeerConnection();
|
|
|
|
assert_equals(pc.idpLoginUrl, null,
|
|
'Expect initial pc.idpLoginUrl to be null');
|
|
|
|
const port = window.location.port;
|
|
const [idpDomain] = getIdpDomains();
|
|
const idpHost = hostString(idpDomain, port);
|
|
|
|
pc.setIdentityProvider(idpHost, {
|
|
protocol: `mock-idp.js?generatorAction=require-login`,
|
|
usernameHint: `alice@${idpDomain}`,
|
|
});
|
|
|
|
return assert_rtcerror_rejection('idp-need-login',
|
|
pc.getIdentityAssertion())
|
|
.then(err => {
|
|
assert_equals(err.idpLoginUrl, `https://${idpHost}/login`,
|
|
'Expect err.idpLoginUrl to be set to url set by mock-idp.js');
|
|
|
|
assert_equals(pc.idpLoginUrl, `https://${idpHost}/login`,
|
|
'Expect pc.idpLoginUrl to be set to url set by mock-idp.js');
|
|
|
|
assert_equals(pc.idpErrorInfo, 'login required',
|
|
'Expect pc.idpErrorInfo to be set to info set by mock-idp.js');
|
|
});
|
|
}, `getIdentityAssertion() should reject with RTCError('idp-need-login') when mock-idp.js requires login`);
|
|
|
|
/*
|
|
RTCIdentityProviderOptions Members
|
|
peerIdentity
|
|
The identity of the peer. For identity providers that bind their assertions to a
|
|
particular pair of communication peers, this allows them to generate an assertion
|
|
that includes both local and remote identities. If this value is omitted, but a
|
|
value is provided for the peerIdentity member of RTCConfiguration, the value from
|
|
RTCConfiguration is used.
|
|
*/
|
|
promise_test(t => {
|
|
const pc = new RTCPeerConnection({
|
|
peerIdentity: 'bob@example.net'
|
|
});
|
|
|
|
const port = window.location.port;
|
|
const [idpDomain] = getIdpDomains();
|
|
const idpHost = hostString(idpDomain, port);
|
|
|
|
pc.setIdentityProvider(idpHost, {
|
|
protocol: 'mock-idp.js'
|
|
});
|
|
|
|
return pc.getIdentityAssertion()
|
|
.then(assertionResultStr => {
|
|
const { assertion } = parseAssertionResult(assertionResultStr);
|
|
assert_equals(assertion.args.options.peerIdentity, 'bob@example.net');
|
|
});
|
|
}, 'setIdentityProvider() with no peerIdentity provided should use peerIdentity value from getConfiguration()');
|
|
|
|
/*
|
|
9.6. setIdentityProvider
|
|
3. If any identity provider value has changed, discard any stored identity assertion.
|
|
*/
|
|
promise_test(t => {
|
|
const pc = new RTCPeerConnection();
|
|
const port = window.location.port;
|
|
const [idpDomain] = getIdpDomains();
|
|
const idpHost = hostString(idpDomain, port);
|
|
|
|
pc.setIdentityProvider(idpHost, {
|
|
protocol: 'mock-idp.js?mark=first'
|
|
});
|
|
|
|
return pc.getIdentityAssertion()
|
|
.then(assertionResultStr => {
|
|
const { assertion } = parseAssertionResult(assertionResultStr);
|
|
assert_equals(assertion.query.mark, 'first');
|
|
|
|
pc.setIdentityProvider(idpHost, {
|
|
protocol: 'mock-idp.js?mark=second'
|
|
});
|
|
|
|
return pc.getIdentityAssertion();
|
|
})
|
|
.then(assertionResultStr => {
|
|
const { assertion } = parseAssertionResult(assertionResultStr);
|
|
assert_equals(assertion.query.mark, 'second',
|
|
'Expect generated assertion is from second IdP config');
|
|
});
|
|
}, `Calling setIdentityProvider() multiple times should reset identity assertions`);
|
|
|
|
promise_test(t => {
|
|
const pc = new RTCPeerConnection();
|
|
const port = window.location.port;
|
|
const [idpDomain] = getIdpDomains();
|
|
|
|
pc.setIdentityProvider(hostString(idpDomain, port), {
|
|
protocol: 'mock-idp.js',
|
|
usernameHint: `alice@${idpDomain}`
|
|
});
|
|
|
|
return pc.getIdentityAssertion()
|
|
.then(assertionResultStr =>
|
|
pc.createOffer()
|
|
.then(offer => {
|
|
assert_true(offer.sdp.includes(`\r\na=identity:${assertionResultStr}`,
|
|
'Expect SDP to have a=identity line containing assertion string'));
|
|
}));
|
|
}, 'createOffer() should return SDP containing identity assertion string if identity provider is set');
|
|
|
|
/*
|
|
6. Requesting Identity Assertions
|
|
|
|
The identity assertion request process is triggered by a call to
|
|
createOffer, createAnswer, or getIdentityAssertion. When these calls are
|
|
invoked and an identity provider has been set, the following steps are
|
|
executed:
|
|
|
|
...
|
|
|
|
If assertion generation fails, then the promise for the corresponding
|
|
function call is rejected with a newly created OperationError. */
|
|
promise_test(t => {
|
|
const pc = new RTCPeerConnection();
|
|
const port = window.location.port;
|
|
const [idpDomain] = getIdpDomains();
|
|
|
|
pc.setIdentityProvider(hostString(idpDomain, port), {
|
|
protocol: 'mock-idp.js?generatorAction=throw-error',
|
|
usernameHint: `alice@${idpDomain}`
|
|
});
|
|
|
|
return promise_rejects_dom(t, 'OperationError',
|
|
pc.createOffer());
|
|
}, 'createOffer() should reject with OperationError if identity assertion request fails');
|
|
|
|
promise_test(t => {
|
|
const pc = new RTCPeerConnection();
|
|
const port = window.location.port;
|
|
const [idpDomain] = getIdpDomains();
|
|
|
|
pc.setIdentityProvider(hostString(idpDomain, port), {
|
|
protocol: 'mock-idp.js?generatorAction=throw-error',
|
|
usernameHint: `alice@${idpDomain}`
|
|
});
|
|
|
|
return new RTCPeerConnection()
|
|
.createOffer()
|
|
.then(offer => pc.setRemoteDescription(offer))
|
|
.then(() =>
|
|
promise_rejects_dom(t, 'OperationError',
|
|
pc.createAnswer()));
|
|
|
|
}, 'createAnswer() should reject with OperationError if identity assertion request fails');
|
|
|
|
</script>
|