Issues (30)

Resources/public/u2f.js (26 issues)

1
//Copyright 2014-2015 Google Inc. All rights reserved.
2
3
//Use of this source code is governed by a BSD-style
4
//license that can be found in the LICENSE file or at
5
//https://developers.google.com/open-source/licenses/bsd
6
7
/**
8
 * @fileoverview The U2F api.
9
 */
10
'use strict';
11
12
13
/**
14
 * Namespace for the U2F api.
15
 * @type {Object}
16
 */
17
var u2f = u2f || {};
0 ignored issues
show
The variable u2f seems to be never initialized.
Loading history...
18
19
/**
20
 * FIDO U2F Javascript API Version
21
 * @number
22
 */
23
var js_api_version;
24
25
/**
26
 * The U2F extension id
27
 * @const {string}
28
 */
29
// The Chrome packaged app extension ID.
30
// Uncomment this if you want to deploy a server instance that uses
31
// the package Chrome app and does not require installing the U2F Chrome extension.
32
 u2f.EXTENSION_ID = 'kmendfapggjehodndflmmgagdbamhnfd';
33
// The U2F Chrome extension ID.
34
// Uncomment this if you want to deploy a server instance that uses
35
// the U2F Chrome extension to authenticate.
36
// u2f.EXTENSION_ID = 'pfboblefjcgdjicmnffhdgionmgcdmne';
37
38
39
/**
40
 * Message types for messsages to/from the extension
41
 * @const
42
 * @enum {string}
43
 */
44
u2f.MessageTypes = {
45
    'U2F_REGISTER_REQUEST': 'u2f_register_request',
46
    'U2F_REGISTER_RESPONSE': 'u2f_register_response',
47
    'U2F_SIGN_REQUEST': 'u2f_sign_request',
48
    'U2F_SIGN_RESPONSE': 'u2f_sign_response',
49
    'U2F_GET_API_VERSION_REQUEST': 'u2f_get_api_version_request',
50
    'U2F_GET_API_VERSION_RESPONSE': 'u2f_get_api_version_response'
51
};
52
53
54
/**
55
 * Response status codes
56
 * @const
57
 * @enum {number}
58
 */
59
u2f.ErrorCodes = {
60
    'OK': 0,
61
    'OTHER_ERROR': 1,
62
    'BAD_REQUEST': 2,
63
    'CONFIGURATION_UNSUPPORTED': 3,
64
    'DEVICE_INELIGIBLE': 4,
65
    'TIMEOUT': 5
66
};
67
68
69
/**
70
 * A message for registration requests
71
 * @typedef {{
72
 *   type: u2f.MessageTypes,
73
 *   appId: ?string,
74
 *   timeoutSeconds: ?number,
75
 *   requestId: ?number
76
 * }}
77
 */
78
u2f.U2fRequest;
0 ignored issues
show
The result of the property access to u2f.U2fRequest is not used.
Loading history...
79
80
81
/**
82
 * A message for registration responses
83
 * @typedef {{
84
 *   type: u2f.MessageTypes,
85
 *   responseData: (u2f.Error | u2f.RegisterResponse | u2f.SignResponse),
86
 *   requestId: ?number
87
 * }}
88
 */
89
u2f.U2fResponse;
0 ignored issues
show
The result of the property access to u2f.U2fResponse is not used.
Loading history...
90
91
92
/**
93
 * An error object for responses
94
 * @typedef {{
95
 *   errorCode: u2f.ErrorCodes,
96
 *   errorMessage: ?string
97
 * }}
98
 */
99
u2f.Error;
0 ignored issues
show
The result of the property access to u2f.Error is not used.
Loading history...
100
101
/**
102
 * Data object for a single sign request.
103
 * @typedef {enum {BLUETOOTH_RADIO, BLUETOOTH_LOW_ENERGY, USB, NFC, USB_INTERNAL}}
104
 */
105
u2f.Transport;
0 ignored issues
show
The result of the property access to u2f.Transport is not used.
Loading history...
106
107
108
/**
109
 * Data object for a single sign request.
110
 * @typedef {Array<u2f.Transport>}
111
 */
112
u2f.Transports;
0 ignored issues
show
The result of the property access to u2f.Transports is not used.
Loading history...
113
114
/**
115
 * Data object for a single sign request.
116
 * @typedef {{
117
 *   version: string,
118
 *   challenge: string,
119
 *   keyHandle: string,
120
 *   appId: string
121
 * }}
122
 */
123
u2f.SignRequest;
0 ignored issues
show
The result of the property access to u2f.SignRequest is not used.
Loading history...
124
125
126
/**
127
 * Data object for a sign response.
128
 * @typedef {{
129
 *   keyHandle: string,
130
 *   signatureData: string,
131
 *   clientData: string
132
 * }}
133
 */
134
u2f.SignResponse;
0 ignored issues
show
The result of the property access to u2f.SignResponse is not used.
Loading history...
135
136
137
/**
138
 * Data object for a registration request.
139
 * @typedef {{
140
 *   version: string,
141
 *   challenge: string
142
 * }}
143
 */
144
u2f.RegisterRequest;
0 ignored issues
show
The result of the property access to u2f.RegisterRequest is not used.
Loading history...
145
146
147
/**
148
 * Data object for a registration response.
149
 * @typedef {{
150
 *   version: string,
151
 *   keyHandle: string,
152
 *   transports: Transports,
153
 *   appId: string
154
 * }}
155
 */
156
u2f.RegisterResponse;
0 ignored issues
show
The result of the property access to u2f.RegisterResponse is not used.
Loading history...
157
158
159
/**
160
 * Data object for a registered key.
161
 * @typedef {{
162
 *   version: string,
163
 *   keyHandle: string,
164
 *   transports: ?Transports,
165
 *   appId: ?string
166
 * }}
167
 */
168
u2f.RegisteredKey;
0 ignored issues
show
The result of the property access to u2f.RegisteredKey is not used.
Loading history...
169
170
171
/**
172
 * Data object for a get API register response.
173
 * @typedef {{
174
 *   js_api_version: number
175
 * }}
176
 */
177
u2f.GetJsApiVersionResponse;
0 ignored issues
show
The result of the property access to u2f.GetJsApiVersionResponse is not used.
Loading history...
178
179
180
//Low level MessagePort API support
181
182
/**
183
 * Sets up a MessagePort to the U2F extension using the
184
 * available mechanisms.
185
 * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback
186
 */
187
u2f.getMessagePort = function(callback) {
188
  if (typeof chrome != 'undefined' && chrome.runtime) {
0 ignored issues
show
The variable chrome seems to be never declared. If this is a global, consider adding a /** global: chrome */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
189
    // The actual message here does not matter, but we need to get a reply
190
    // for the callback to run. Thus, send an empty signature request
191
    // in order to get a failure response.
192
    var msg = {
193
        type: u2f.MessageTypes.U2F_SIGN_REQUEST,
194
        signRequests: []
195
    };
196
    chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function() {
197
      if (!chrome.runtime.lastError) {
0 ignored issues
show
The variable chrome seems to be never declared. If this is a global, consider adding a /** global: chrome */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
198
        // We are on a whitelisted origin and can talk directly
199
        // with the extension.
200
        u2f.getChromeRuntimePort_(callback);
201
      } else {
202
        // chrome.runtime was available, but we couldn't message
203
        // the extension directly, use iframe
204
        u2f.getIframePort_(callback);
205
      }
206
    });
207
  } else if (u2f.isAndroidChrome_()) {
208
    u2f.getAuthenticatorPort_(callback);
209
  } else if (u2f.isIosChrome_()) {
210
    u2f.getIosPort_(callback);
211
  } else {
212
    // chrome.runtime was not available at all, which is normal
213
    // when this origin doesn't have access to any extensions.
214
    u2f.getIframePort_(callback);
215
  }
216
};
217
218
/**
219
 * Detect chrome running on android based on the browser's useragent.
220
 * @private
221
 */
222
u2f.isAndroidChrome_ = function() {
223
  var userAgent = navigator.userAgent;
0 ignored issues
show
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
224
  return userAgent.indexOf('Chrome') != -1 &&
225
  userAgent.indexOf('Android') != -1;
226
};
227
228
/**
229
 * Detect chrome running on iOS based on the browser's platform.
230
 * @private
231
 */
232
u2f.isIosChrome_ = function() {
233
  return ["iPhone", "iPad", "iPod"].indexOf(navigator.platform) > -1;
0 ignored issues
show
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
234
};
235
236
/**
237
 * Connects directly to the extension via chrome.runtime.connect.
238
 * @param {function(u2f.WrappedChromeRuntimePort_)} callback
239
 * @private
240
 */
241
u2f.getChromeRuntimePort_ = function(callback) {
242
  var port = chrome.runtime.connect(u2f.EXTENSION_ID,
0 ignored issues
show
The variable chrome seems to be never declared. If this is a global, consider adding a /** global: chrome */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
243
      {'includeTlsChannelId': true});
244
  setTimeout(function() {
245
    callback(new u2f.WrappedChromeRuntimePort_(port));
246
  }, 0);
247
};
248
249
/**
250
 * Return a 'port' abstraction to the Authenticator app.
251
 * @param {function(u2f.WrappedAuthenticatorPort_)} callback
252
 * @private
253
 */
254
u2f.getAuthenticatorPort_ = function(callback) {
255
  setTimeout(function() {
256
    callback(new u2f.WrappedAuthenticatorPort_());
257
  }, 0);
258
};
259
260
/**
261
 * Return a 'port' abstraction to the iOS client app.
262
 * @param {function(u2f.WrappedIosPort_)} callback
263
 * @private
264
 */
265
u2f.getIosPort_ = function(callback) {
266
  setTimeout(function() {
267
    callback(new u2f.WrappedIosPort_());
268
  }, 0);
269
};
270
271
/**
272
 * A wrapper for chrome.runtime.Port that is compatible with MessagePort.
273
 * @param {Port} port
274
 * @constructor
275
 * @private
276
 */
277
u2f.WrappedChromeRuntimePort_ = function(port) {
278
  this.port_ = port;
279
};
280
281
/**
282
 * Format and return a sign request compliant with the JS API version supported by the extension.
283
 * @param {Array<u2f.SignRequest>} signRequests
284
 * @param {number} timeoutSeconds
285
 * @param {number} reqId
286
 * @return {Object}
287
 */
288
u2f.formatSignRequest_ =
289
  function(appId, challenge, registeredKeys, timeoutSeconds, reqId) {
290
  if (js_api_version === undefined || js_api_version < 1.1) {
291
    // Adapt request to the 1.0 JS API
292
    var signRequests = [];
293
    for (var i = 0; i < registeredKeys.length; i++) {
294
      signRequests[i] = {
295
          version: registeredKeys[i].version,
296
          challenge: challenge,
297
          keyHandle: registeredKeys[i].keyHandle,
298
          appId: appId
299
      };
300
    }
301
    return {
302
      type: u2f.MessageTypes.U2F_SIGN_REQUEST,
303
      signRequests: signRequests,
304
      timeoutSeconds: timeoutSeconds,
305
      requestId: reqId
306
    };
307
  }
308
  // JS 1.1 API
309
  return {
310
    type: u2f.MessageTypes.U2F_SIGN_REQUEST,
311
    appId: appId,
312
    challenge: challenge,
313
    registeredKeys: registeredKeys,
314
    timeoutSeconds: timeoutSeconds,
315
    requestId: reqId
316
  };
317
};
318
319
/**
320
 * Format and return a register request compliant with the JS API version supported by the extension..
321
 * @param {Array<u2f.SignRequest>} signRequests
322
 * @param {Array<u2f.RegisterRequest>} signRequests
323
 * @param {number} timeoutSeconds
324
 * @param {number} reqId
325
 * @return {Object}
326
 */
327
u2f.formatRegisterRequest_ =
328
  function(appId, registeredKeys, registerRequests, timeoutSeconds, reqId) {
329
  if (js_api_version === undefined || js_api_version < 1.1) {
330
    // Adapt request to the 1.0 JS API
331
    for (var i = 0; i < registerRequests.length; i++) {
332
      registerRequests[i].appId = appId;
333
    }
334
    var signRequests = [];
335
    for (var i = 0; i < registeredKeys.length; i++) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 331. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
336
      signRequests[i] = {
337
          version: registeredKeys[i].version,
338
          challenge: registerRequests[0],
339
          keyHandle: registeredKeys[i].keyHandle,
340
          appId: appId
341
      };
342
    }
343
    return {
344
      type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
345
      signRequests: signRequests,
346
      registerRequests: registerRequests,
347
      timeoutSeconds: timeoutSeconds,
348
      requestId: reqId
349
    };
350
  }
351
  // JS 1.1 API
352
  return {
353
    type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
354
    appId: appId,
355
    registerRequests: registerRequests,
356
    registeredKeys: registeredKeys,
357
    timeoutSeconds: timeoutSeconds,
358
    requestId: reqId
359
  };
360
};
361
362
363
/**
364
 * Posts a message on the underlying channel.
365
 * @param {Object} message
366
 */
367
u2f.WrappedChromeRuntimePort_.prototype.postMessage = function(message) {
368
  this.port_.postMessage(message);
369
};
370
371
372
/**
373
 * Emulates the HTML 5 addEventListener interface. Works only for the
374
 * onmessage event, which is hooked up to the chrome.runtime.Port.onMessage.
375
 * @param {string} eventName
376
 * @param {function({data: Object})} handler
377
 */
378
u2f.WrappedChromeRuntimePort_.prototype.addEventListener =
379
    function(eventName, handler) {
380
  var name = eventName.toLowerCase();
381
  if (name == 'message' || name == 'onmessage') {
382
    this.port_.onMessage.addListener(function(message) {
383
      // Emulate a minimal MessageEvent object
384
      handler({'data': message});
385
    });
386
  } else {
387
    console.error('WrappedChromeRuntimePort only supports onMessage');
388
  }
389
};
390
391
/**
392
 * Wrap the Authenticator app with a MessagePort interface.
393
 * @constructor
394
 * @private
395
 */
396
u2f.WrappedAuthenticatorPort_ = function() {
397
  this.requestId_ = -1;
398
  this.requestObject_ = null;
399
}
400
401
/**
402
 * Launch the Authenticator intent.
403
 * @param {Object} message
404
 */
405
u2f.WrappedAuthenticatorPort_.prototype.postMessage = function(message) {
406
  var intentUrl =
407
    u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ +
408
    ';S.request=' + encodeURIComponent(JSON.stringify(message)) +
409
    ';end';
410
  document.location = intentUrl;
411
};
412
413
/**
414
 * Tells what type of port this is.
415
 * @return {String} port type
416
 */
417
u2f.WrappedAuthenticatorPort_.prototype.getPortType = function() {
418
  return "WrappedAuthenticatorPort_";
419
};
420
421
422
/**
423
 * Emulates the HTML 5 addEventListener interface.
424
 * @param {string} eventName
425
 * @param {function({data: Object})} handler
426
 */
427
u2f.WrappedAuthenticatorPort_.prototype.addEventListener = function(eventName, handler) {
428
  var name = eventName.toLowerCase();
429
  if (name == 'message') {
430
    var self = this;
431
    /* Register a callback to that executes when
432
     * chrome injects the response. */
433
    window.addEventListener(
434
        'message', self.onRequestUpdate_.bind(self, handler), false);
435
  } else {
436
    console.error('WrappedAuthenticatorPort only supports message');
437
  }
438
};
439
440
/**
441
 * Callback invoked  when a response is received from the Authenticator.
442
 * @param function({data: Object}) callback
443
 * @param {Object} message message Object
444
 */
445
u2f.WrappedAuthenticatorPort_.prototype.onRequestUpdate_ =
446
    function(callback, message) {
447
  var messageObject = JSON.parse(message.data);
448
  var intentUrl = messageObject['intentURL'];
0 ignored issues
show
The variable intentUrl seems to be never used. Consider removing it.
Loading history...
449
450
  var errorCode = messageObject['errorCode'];
0 ignored issues
show
The variable errorCode seems to be never used. Consider removing it.
Loading history...
451
  var responseObject = null;
452
  if (messageObject.hasOwnProperty('data')) {
453
    responseObject = /** @type {Object} */ (
454
        JSON.parse(messageObject['data']));
455
  }
456
457
  callback({'data': responseObject});
458
};
459
460
/**
461
 * Base URL for intents to Authenticator.
462
 * @const
463
 * @private
464
 */
465
u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ =
466
  'intent:#Intent;action=com.google.android.apps.authenticator.AUTHENTICATE';
467
468
/**
469
 * Wrap the iOS client app with a MessagePort interface.
470
 * @constructor
471
 * @private
472
 */
473
u2f.WrappedIosPort_ = function() {};
474
475
/**
476
 * Launch the iOS client app request
477
 * @param {Object} message
478
 */
479
u2f.WrappedIosPort_.prototype.postMessage = function(message) {
480
  var str = JSON.stringify(message);
481
  var url = "u2f://auth?" + encodeURI(str);
482
  location.replace(url);
483
};
484
485
/**
486
 * Tells what type of port this is.
487
 * @return {String} port type
488
 */
489
u2f.WrappedIosPort_.prototype.getPortType = function() {
490
  return "WrappedIosPort_";
491
};
492
493
/**
494
 * Emulates the HTML 5 addEventListener interface.
495
 * @param {string} eventName
496
 * @param {function({data: Object})} handler
497
 */
498
u2f.WrappedIosPort_.prototype.addEventListener = function(eventName, handler) {
0 ignored issues
show
The parameter handler is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
499
  var name = eventName.toLowerCase();
500
  if (name !== 'message') {
501
    console.error('WrappedIosPort only supports message');
502
  }
503
};
504
505
/**
506
 * Sets up an embedded trampoline iframe, sourced from the extension.
507
 * @param {function(MessagePort)} callback
508
 * @private
509
 */
510
u2f.getIframePort_ = function(callback) {
511
  // Create the iframe
512
  var iframeOrigin = 'chrome-extension://' + u2f.EXTENSION_ID;
513
  var iframe = document.createElement('iframe');
514
  iframe.src = iframeOrigin + '/u2f-comms.html';
515
  iframe.setAttribute('style', 'display:none');
516
  document.body.appendChild(iframe);
517
518
  var channel = new MessageChannel();
0 ignored issues
show
The variable MessageChannel seems to be never declared. If this is a global, consider adding a /** global: MessageChannel */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
519
  var ready = function(message) {
520
    if (message.data == 'ready') {
521
      channel.port1.removeEventListener('message', ready);
522
      callback(channel.port1);
523
    } else {
524
      console.error('First event on iframe port was not "ready"');
525
    }
526
  };
527
  channel.port1.addEventListener('message', ready);
528
  channel.port1.start();
529
530
  iframe.addEventListener('load', function() {
531
    // Deliver the port to the iframe and initialize
532
    iframe.contentWindow.postMessage('init', iframeOrigin, [channel.port2]);
533
  });
534
};
535
536
537
//High-level JS API
538
539
/**
540
 * Default extension response timeout in seconds.
541
 * @const
542
 */
543
u2f.EXTENSION_TIMEOUT_SEC = 30;
544
545
/**
546
 * A singleton instance for a MessagePort to the extension.
547
 * @type {MessagePort|u2f.WrappedChromeRuntimePort_}
548
 * @private
549
 */
550
u2f.port_ = null;
551
552
/**
553
 * Callbacks waiting for a port
554
 * @type {Array<function((MessagePort|u2f.WrappedChromeRuntimePort_))>}
555
 * @private
556
 */
557
u2f.waitingForPort_ = [];
558
559
/**
560
 * A counter for requestIds.
561
 * @type {number}
562
 * @private
563
 */
564
u2f.reqCounter_ = 0;
565
566
/**
567
 * A map from requestIds to client callbacks
568
 * @type {Object.<number,(function((u2f.Error|u2f.RegisterResponse))
569
 *                       |function((u2f.Error|u2f.SignResponse)))>}
570
 * @private
571
 */
572
u2f.callbackMap_ = {};
573
574
/**
575
 * Creates or retrieves the MessagePort singleton to use.
576
 * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback
577
 * @private
578
 */
579
u2f.getPortSingleton_ = function(callback) {
580
  if (u2f.port_) {
581
    callback(u2f.port_);
582
  } else {
583
    if (u2f.waitingForPort_.length == 0) {
0 ignored issues
show
Comparing u2f.waitingForPort_.length to 0 using the == operator is not safe. Consider using === instead.
Loading history...
584
      u2f.getMessagePort(function(port) {
585
        u2f.port_ = port;
586
        u2f.port_.addEventListener('message',
587
            /** @type {function(Event)} */ (u2f.responseHandler_));
588
589
        // Careful, here be async callbacks. Maybe.
590
        while (u2f.waitingForPort_.length)
591
          u2f.waitingForPort_.shift()(u2f.port_);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
592
      });
593
    }
594
    u2f.waitingForPort_.push(callback);
595
  }
596
};
597
598
/**
599
 * Handles response messages from the extension.
600
 * @param {MessageEvent.<u2f.Response>} message
601
 * @private
602
 */
603
u2f.responseHandler_ = function(message) {
604
  var response = message.data;
605
  var reqId = response['requestId'];
606
  if (!reqId || !u2f.callbackMap_[reqId]) {
607
    console.error('Unknown or missing requestId in response.');
608
    return;
609
  }
610
  var cb = u2f.callbackMap_[reqId];
611
  delete u2f.callbackMap_[reqId];
612
  cb(response['responseData']);
613
};
614
615
/**
616
 * Dispatches an array of sign requests to available U2F tokens.
617
 * If the JS API version supported by the extension is unknown, it first sends a
618
 * message to the extension to find out the supported API version and then it sends
619
 * the sign request.
620
 * @param {string=} appId
621
 * @param {string=} challenge
622
 * @param {Array<u2f.RegisteredKey>} registeredKeys
623
 * @param {function((u2f.Error|u2f.SignResponse))} callback
624
 * @param {number=} opt_timeoutSeconds
625
 */
626
u2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
627
  if (js_api_version === undefined) {
628
    // Send a message to get the extension to JS API version, then send the actual sign request.
629
    u2f.getApiVersion(
630
        function (response) {
631
          js_api_version = response['js_api_version'] === undefined ? 0 : response['js_api_version'];
632
          console.log("Extension JS API Version: ", js_api_version);
0 ignored issues
show
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
633
          u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds);
634
        });
635
  } else {
636
    // We know the JS API version. Send the actual sign request in the supported API version.
637
    u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds);
638
  }
639
};
640
641
/**
642
 * Dispatches an array of sign requests to available U2F tokens.
643
 * @param {string=} appId
644
 * @param {string=} challenge
645
 * @param {Array<u2f.RegisteredKey>} registeredKeys
646
 * @param {function((u2f.Error|u2f.SignResponse))} callback
647
 * @param {number=} opt_timeoutSeconds
648
 */
649
u2f.sendSignRequest = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
650
  u2f.getPortSingleton_(function(port) {
651
    var reqId = ++u2f.reqCounter_;
652
    u2f.callbackMap_[reqId] = callback;
653
    var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
654
        opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC);
655
    var req = u2f.formatSignRequest_(appId, challenge, registeredKeys, timeoutSeconds, reqId);
656
    port.postMessage(req);
657
  });
658
};
659
660
/**
661
 * Dispatches register requests to available U2F tokens. An array of sign
662
 * requests identifies already registered tokens.
663
 * If the JS API version supported by the extension is unknown, it first sends a
664
 * message to the extension to find out the supported API version and then it sends
665
 * the register request.
666
 * @param {string=} appId
667
 * @param {Array<u2f.RegisterRequest>} registerRequests
668
 * @param {Array<u2f.RegisteredKey>} registeredKeys
669
 * @param {function((u2f.Error|u2f.RegisterResponse))} callback
670
 * @param {number=} opt_timeoutSeconds
671
 */
672
u2f.register = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {
673
  if (js_api_version === undefined) {
674
    // Send a message to get the extension to JS API version, then send the actual register request.
675
    u2f.getApiVersion(
676
        function (response) {
677
          js_api_version = response['js_api_version'] === undefined ? 0: response['js_api_version'];
678
          console.log("Extension JS API Version: ", js_api_version);
0 ignored issues
show
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
679
          u2f.sendRegisterRequest(appId, registerRequests, registeredKeys,
680
              callback, opt_timeoutSeconds);
681
        });
682
  } else {
683
    // We know the JS API version. Send the actual register request in the supported API version.
684
    u2f.sendRegisterRequest(appId, registerRequests, registeredKeys,
685
        callback, opt_timeoutSeconds);
686
  }
687
};
688
689
/**
690
 * Dispatches register requests to available U2F tokens. An array of sign
691
 * requests identifies already registered tokens.
692
 * @param {string=} appId
693
 * @param {Array<u2f.RegisterRequest>} registerRequests
694
 * @param {Array<u2f.RegisteredKey>} registeredKeys
695
 * @param {function((u2f.Error|u2f.RegisterResponse))} callback
696
 * @param {number=} opt_timeoutSeconds
697
 */
698
u2f.sendRegisterRequest = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {
699
  u2f.getPortSingleton_(function(port) {
700
    var reqId = ++u2f.reqCounter_;
701
    u2f.callbackMap_[reqId] = callback;
702
    var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
703
        opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC);
704
    var req = u2f.formatRegisterRequest_(
705
        appId, registeredKeys, registerRequests, timeoutSeconds, reqId);
706
    port.postMessage(req);
707
  });
708
};
709
710
711
/**
712
 * Dispatches a message to the extension to find out the supported
713
 * JS API version.
714
 * If the user is on a mobile phone and is thus using Google Authenticator instead
715
 * of the Chrome extension, don't send the request and simply return 0.
716
 * @param {function((u2f.Error|u2f.GetJsApiVersionResponse))} callback
717
 * @param {number=} opt_timeoutSeconds
718
 */
719
u2f.getApiVersion = function(callback, opt_timeoutSeconds) {
720
 u2f.getPortSingleton_(function(port) {
721
   // If we are using Android Google Authenticator or iOS client app,
722
   // do not fire an intent to ask which JS API version to use.
723
   if (port.getPortType) {
724
     var apiVersion;
725
     switch (port.getPortType()) {
726
       case 'WrappedIosPort_':
727
       case 'WrappedAuthenticatorPort_':
728
         apiVersion = 1.1;
729
         break;
730
731
       default:
732
         apiVersion = 0;
733
         break;
734
     }
735
     callback({ 'js_api_version': apiVersion });
736
     return;
737
   }
738
    var reqId = ++u2f.reqCounter_;
739
    u2f.callbackMap_[reqId] = callback;
740
    var req = {
741
      type: u2f.MessageTypes.U2F_GET_API_VERSION_REQUEST,
742
      timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ?
743
          opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC),
744
      requestId: reqId
745
    };
746
    port.postMessage(req);
747
  });
748
};
749