summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/.well-known/idp-proxy/mock-idp.js
blob: e73ca22bb2ae79a6ceec2ebbf407ed22b1d0b435 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
'use strict';

// Code is based on the following editor draft:
//   https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html

/*
  mock-idp.js is a naive IdP that provides absolutely no
  security for authentication. It can generate identity
  assertion for whatever identity that is requested.

  mock-idp.js validates identity assertion by simply decoding
  the JSON and return whatever that is inside, with no integrity
  protection and thus can be spoofed by anyone.

  While being not practical at all, mock-idp.js allows us
  to test various aspects of the identity API and allow tests
  to manipulate the IdP at will.
 */

// We pass around test options as query string to instruct
// the test IdP proxy script on what actions to perform.
// This hack is based on the fact that query string is allowed
// when specifying the IdP protocol.
function parseQueryString(urlStr) {
  const url = new URL(urlStr);
  const result = {};
  for(const [key, value] of url.searchParams) {
    result[key] = value;
  }
  return result;
}

/*
  9.2.1.  Interface Exposed by Identity Providers
    callback GenerateAssertionCallback =
      Promise<RTCIdentityAssertionResult> (
        DOMString contents,
        DOMString origin,
        RTCIdentityProviderOptions options);

    dictionary RTCIdentityProviderOptions {
      DOMString protocol = "default";
      DOMString usernameHint;
      DOMString peerIdentity;
    };

    dictionary RTCIdentityAssertionResult {
      required RTCIdentityProviderDetails idp;
      required DOMString                  assertion;
    };

    dictionary RTCIdentityProviderDetails {
      required DOMString domain;
               DOMString protocol = "default";
    };
 */

const query = parseQueryString(location);

// Generate a naive identity assertion. The result assertion
// is a JSON string that report the various parameters
// received by this function.
//   watermark - a special mark to make sure the result is returned
//               from this function
//   args - the function arguments received
//   env - some global variable values when this function is called
//   query - the parsed query string of the script URL
function generateAssertion(contents, origin, options) {
  const args = {
    contents, origin, options
  };

  const env = {
    origin,
    location
  };

  const assertion = {
    watermark: 'mock-idp.js.watermark',
    args,
    env,
    query
  };

  const assertionStr = JSON.stringify(assertion);

  const { generatorAction } = query;

  if(generatorAction === 'throw-error') {
    const err = new Error('Mock Internal IdP Error');
    err.idpErrorInfo = query.errorInfo;
    throw err;

  } else if(generatorAction === 'require-login') {
    const err = new RTCError('idp-need-login');
    err.idpLoginUrl = `${origin}/login`;
    err.idpErrorInfo = 'login required';
    throw err;

  } else if(generatorAction === 'return-custom-idp') {
    const { domain, protocol } = query;

    return {
      idp: {
        domain,
        protocol
      },
      assertion: assertionStr
    };

  } else if(generatorAction === 'return-invalid-result') {
    return 'invalid-result';

  } else {
    return {
      idp: {
        domain: location.host,
        protocol: 'mock-idp.js'
      },
      assertion: assertionStr
    };
  }
}

/*
  9.2.1.  Interface Exposed by Identity Providers
    callback ValidateAssertionCallback =
      Promise<RTCIdentityValidationResult> (
        DOMString assertion,
        DOMString origin);

    dictionary RTCIdentityValidationResult {
      required DOMString identity;
      required DOMString contents;
    };
 */
function validateAssertion(assertionStr, origin) {
  const assertion = JSON.parse(assertionStr);

  const { args, query } = assertion;
  const { contents, options } = args;

  const identity = options.usernameHint;

  const {
    validatorAction
  } = query;

  if(validatorAction === 'throw-error') {
    const err = new Error('Mock Internal IdP Error');
    err.idpErrorInfo = query.errorInfo;
    throw err;

  } else if(validatorAction === 'return-custom-contents') {
    const { contents } = query;
    return {
      identity,
      contents
    };

  } else {
    return {
      identity, contents
    };
  }
}

/*
  9.2.  Registering an IdP Proxy
    [Global,
     Exposed=RTCIdentityProviderGlobalScope]
    interface RTCIdentityProviderGlobalScope : WorkerGlobalScope {
      readonly attribute RTCIdentityProviderRegistrar rtcIdentityProvider;
    };

    [Exposed=RTCIdentityProviderGlobalScope]
    interface RTCIdentityProviderRegistrar {
      void register(RTCIdentityProvider idp);
    };

    dictionary RTCIdentityProvider {
      required GenerateAssertionCallback generateAssertion;
      required ValidateAssertionCallback validateAssertion;
    };
 */

// if rtcIdentityProvider is defined, and the caller do not ask
// to not register through query string, register our assertion callbacks.
if(rtcIdentityProvider && query.action !== 'do-not-register') {
  rtcIdentityProvider.register({
    generateAssertion,
    validateAssertion
  });
}