Comparing version 0.2.7 to 0.3.0
@@ -1,11 +0,10 @@ | ||
// Copyright 2014 Google Inc. All rights reserved | ||
// | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file or at | ||
// https://developers.google.com/open-source/licenses/bsd | ||
//Copyright 2014-2015 Google Inc. All rights reserved. | ||
//Use of this source code is governed by a BSD-style | ||
//license that can be found in the LICENSE file or at | ||
//https://developers.google.com/open-source/licenses/bsd | ||
/** | ||
* @fileoverview The U2F api. | ||
*/ | ||
'use strict'; | ||
@@ -21,8 +20,21 @@ | ||
/** | ||
* FIDO U2F Javascript API Version | ||
* @number | ||
*/ | ||
var js_api_version; | ||
/** | ||
* The U2F extension id | ||
* @type {string} | ||
* @const | ||
* @const {string} | ||
*/ | ||
// The Chrome packaged app extension ID. | ||
// Uncomment this if you want to deploy a server instance that uses | ||
// the package Chrome app and does not require installing the U2F Chrome extension. | ||
u2f.EXTENSION_ID = 'kmendfapggjehodndflmmgagdbamhnfd'; | ||
// The U2F Chrome extension ID. | ||
// Uncomment this if you want to deploy a server instance that uses | ||
// the U2F Chrome extension to authenticate. | ||
// u2f.EXTENSION_ID = 'pfboblefjcgdjicmnffhdgionmgcdmne'; | ||
/** | ||
@@ -35,7 +47,10 @@ * Message types for messsages to/from the extension | ||
'U2F_REGISTER_REQUEST': 'u2f_register_request', | ||
'U2F_REGISTER_RESPONSE': 'u2f_register_response', | ||
'U2F_SIGN_REQUEST': 'u2f_sign_request', | ||
'U2F_REGISTER_RESPONSE': 'u2f_register_response', | ||
'U2F_SIGN_RESPONSE': 'u2f_sign_response' | ||
'U2F_SIGN_RESPONSE': 'u2f_sign_response', | ||
'U2F_GET_API_VERSION_REQUEST': 'u2f_get_api_version_request', | ||
'U2F_GET_API_VERSION_RESPONSE': 'u2f_get_api_version_response' | ||
}; | ||
/** | ||
@@ -55,8 +70,8 @@ * Response status codes | ||
/** | ||
* A message type for registration requests | ||
* A message for registration requests | ||
* @typedef {{ | ||
* type: u2f.MessageTypes, | ||
* signRequests: Array.<u2f.SignRequest>, | ||
* registerRequests: ?Array.<u2f.RegisterRequest>, | ||
* appId: ?string, | ||
* timeoutSeconds: ?number, | ||
@@ -66,4 +81,5 @@ * requestId: ?number | ||
*/ | ||
u2f.Request; | ||
u2f.U2fRequest; | ||
/** | ||
@@ -77,4 +93,5 @@ * A message for registration responses | ||
*/ | ||
u2f.Response; | ||
u2f.U2fResponse; | ||
/** | ||
@@ -91,2 +108,15 @@ * An error object for responses | ||
* Data object for a single sign request. | ||
* @typedef {enum {BLUETOOTH_RADIO, BLUETOOTH_LOW_ENERGY, USB, NFC, USB_INTERNAL}} | ||
*/ | ||
u2f.Transport; | ||
/** | ||
* Data object for a single sign request. | ||
* @typedef {Array<u2f.Transport>} | ||
*/ | ||
u2f.Transports; | ||
/** | ||
* Data object for a single sign request. | ||
* @typedef {{ | ||
@@ -101,2 +131,3 @@ * version: string, | ||
/** | ||
@@ -112,2 +143,3 @@ * Data object for a sign response. | ||
/** | ||
@@ -117,4 +149,3 @@ * Data object for a registration request. | ||
* version: string, | ||
* challenge: string, | ||
* appId: string | ||
* challenge: string | ||
* }} | ||
@@ -124,7 +155,10 @@ */ | ||
/** | ||
* Data object for a registration response. | ||
* @typedef {{ | ||
* registrationData: string, | ||
* clientData: string | ||
* version: string, | ||
* keyHandle: string, | ||
* transports: Transports, | ||
* appId: string | ||
* }} | ||
@@ -135,14 +169,25 @@ */ | ||
// Low level MessagePort API support | ||
/** | ||
* Data object for a registered key. | ||
* @typedef {{ | ||
* version: string, | ||
* keyHandle: string, | ||
* transports: ?Transports, | ||
* appId: ?string | ||
* }} | ||
*/ | ||
u2f.RegisteredKey; | ||
/** | ||
* Call MessagePort disconnect | ||
* Data object for a get API register response. | ||
* @typedef {{ | ||
* js_api_version: number | ||
* }} | ||
*/ | ||
u2f.disconnect = function() { | ||
if (u2f.port_ && u2f.port_.port_) { | ||
u2f.port_.port_.disconnect(); | ||
u2f.port_ = null; | ||
} | ||
}; | ||
u2f.GetJsApiVersionResponse; | ||
//Low level MessagePort API support | ||
/** | ||
@@ -153,3 +198,3 @@ * Sets up a MessagePort to the U2F extension using the | ||
*/ | ||
u2f.getMessagePort = function(callback) { | ||
u2f.getMessagePort = function (callback) { | ||
if (typeof chrome != 'undefined' && chrome.runtime) { | ||
@@ -163,3 +208,3 @@ // The actual message here does not matter, but we need to get a reply | ||
}; | ||
chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function() { | ||
chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function () { | ||
if (!chrome.runtime.lastError) { | ||
@@ -175,2 +220,6 @@ // We are on a whitelisted origin and can talk directly | ||
}); | ||
} else if (u2f.isAndroidChrome_()) { | ||
u2f.getAuthenticatorPort_(callback); | ||
} else if (u2f.isIosChrome_()) { | ||
u2f.getIosPort_(callback); | ||
} else { | ||
@@ -184,7 +233,25 @@ // chrome.runtime was not available at all, which is normal | ||
/** | ||
* Connects directly to the extension via chrome.runtime.connect | ||
* Detect chrome running on android based on the browser's useragent. | ||
* @private | ||
*/ | ||
u2f.isAndroidChrome_ = function () { | ||
var userAgent = navigator.userAgent; | ||
return userAgent.indexOf('Chrome') != -1 && | ||
userAgent.indexOf('Android') != -1; | ||
}; | ||
/** | ||
* Detect chrome running on iOS based on the browser's platform. | ||
* @private | ||
*/ | ||
u2f.isIosChrome_ = function () { | ||
return ["iPhone", "iPad", "iPod"].indexOf(navigator.platform) > -1; | ||
}; | ||
/** | ||
* Connects directly to the extension via chrome.runtime.connect. | ||
* @param {function(u2f.WrappedChromeRuntimePort_)} callback | ||
* @private | ||
*/ | ||
u2f.getChromeRuntimePort_ = function(callback) { | ||
u2f.getChromeRuntimePort_ = function (callback) { | ||
var port = chrome.runtime.connect(u2f.EXTENSION_ID, | ||
@@ -198,2 +265,24 @@ {'includeTlsChannelId': true}); | ||
/** | ||
* Return a 'port' abstraction to the Authenticator app. | ||
* @param {function(u2f.WrappedAuthenticatorPort_)} callback | ||
* @private | ||
*/ | ||
u2f.getAuthenticatorPort_ = function (callback) { | ||
setTimeout(function () { | ||
callback(null, new u2f.WrappedAuthenticatorPort_()); | ||
}, 0); | ||
}; | ||
/** | ||
* Return a 'port' abstraction to the iOS client app. | ||
* @param {function(u2f.WrappedIosPort_)} callback | ||
* @private | ||
*/ | ||
u2f.getIosPort_ = function (callback) { | ||
setTimeout(function () { | ||
callback(null, new u2f.WrappedIosPort_()); | ||
}, 0); | ||
}; | ||
/** | ||
* A wrapper for chrome.runtime.Port that is compatible with MessagePort. | ||
@@ -204,3 +293,3 @@ * @param {Port} port | ||
*/ | ||
u2f.WrappedChromeRuntimePort_ = function(port) { | ||
u2f.WrappedChromeRuntimePort_ = function (port) { | ||
this.port_ = port; | ||
@@ -210,9 +299,92 @@ }; | ||
/** | ||
* Format and return a sign request compliant with the JS API version supported by the extension. | ||
* @param {Array<u2f.SignRequest>} signRequests | ||
* @param {number} timeoutSeconds | ||
* @param {number} reqId | ||
* @return {Object} | ||
*/ | ||
u2f.formatSignRequest_ = | ||
function (appId, challenge, registeredKeys, timeoutSeconds, reqId) { | ||
if (js_api_version === undefined || js_api_version < 1.1) { | ||
// Adapt request to the 1.0 JS API | ||
var signRequests = []; | ||
for (var i = 0; i < registeredKeys.length; i++) { | ||
signRequests[i] = { | ||
version: registeredKeys[i].version, | ||
challenge: challenge, | ||
keyHandle: registeredKeys[i].keyHandle, | ||
appId: appId | ||
}; | ||
} | ||
return { | ||
type: u2f.MessageTypes.U2F_SIGN_REQUEST, | ||
signRequests: signRequests, | ||
timeoutSeconds: timeoutSeconds, | ||
requestId: reqId | ||
}; | ||
} | ||
// JS 1.1 API | ||
return { | ||
type: u2f.MessageTypes.U2F_SIGN_REQUEST, | ||
appId: appId, | ||
challenge: challenge, | ||
registeredKeys: registeredKeys, | ||
timeoutSeconds: timeoutSeconds, | ||
requestId: reqId | ||
}; | ||
}; | ||
/** | ||
* Format and return a register request compliant with the JS API version supported by the extension.. | ||
* @param {Array<u2f.SignRequest>} signRequests | ||
* @param {Array<u2f.RegisterRequest>} signRequests | ||
* @param {number} timeoutSeconds | ||
* @param {number} reqId | ||
* @return {Object} | ||
*/ | ||
u2f.formatRegisterRequest_ = | ||
function (appId, registeredKeys, registerRequests, timeoutSeconds, reqId) { | ||
if (js_api_version === undefined || js_api_version < 1.1) { | ||
// Adapt request to the 1.0 JS API | ||
for (var i = 0; i < registerRequests.length; i++) { | ||
registerRequests[i].appId = appId; | ||
} | ||
var signRequests = []; | ||
for (var i = 0; i < registeredKeys.length; i++) { | ||
signRequests[i] = { | ||
version: registeredKeys[i].version, | ||
challenge: registerRequests[0], | ||
keyHandle: registeredKeys[i].keyHandle, | ||
appId: appId | ||
}; | ||
} | ||
return { | ||
type: u2f.MessageTypes.U2F_REGISTER_REQUEST, | ||
signRequests: signRequests, | ||
registerRequests: registerRequests, | ||
timeoutSeconds: timeoutSeconds, | ||
requestId: reqId | ||
}; | ||
} | ||
// JS 1.1 API | ||
return { | ||
type: u2f.MessageTypes.U2F_REGISTER_REQUEST, | ||
appId: appId, | ||
registerRequests: registerRequests, | ||
registeredKeys: registeredKeys, | ||
timeoutSeconds: timeoutSeconds, | ||
requestId: reqId | ||
}; | ||
}; | ||
/** | ||
* Posts a message on the underlying channel. | ||
* @param {Object} message | ||
*/ | ||
u2f.WrappedChromeRuntimePort_.prototype.postMessage = function(message) { | ||
u2f.WrappedChromeRuntimePort_.prototype.postMessage = function (message) { | ||
this.port_.postMessage(message); | ||
}; | ||
/** | ||
@@ -225,11 +397,60 @@ * Emulates the HTML 5 addEventListener interface. Works only for the | ||
u2f.WrappedChromeRuntimePort_.prototype.addEventListener = | ||
function(eventName, handler) { | ||
function (eventName, handler) { | ||
var name = eventName.toLowerCase(); | ||
if (name == 'message' || name == 'onmessage') { | ||
this.port_.onMessage.addListener(function (message) { | ||
// Emulate a minimal MessageEvent object | ||
handler({'data': message}); | ||
}); | ||
} else { | ||
console.error('WrappedChromeRuntimePort only supports onMessage'); | ||
} | ||
}; | ||
/** | ||
* Wrap the Authenticator app with a MessagePort interface. | ||
* @constructor | ||
* @private | ||
*/ | ||
u2f.WrappedAuthenticatorPort_ = function () { | ||
this.requestId_ = -1; | ||
this.requestObject_ = null; | ||
} | ||
/** | ||
* Launch the Authenticator intent. | ||
* @param {Object} message | ||
*/ | ||
u2f.WrappedAuthenticatorPort_.prototype.postMessage = function (message) { | ||
var intentUrl = | ||
u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ + | ||
';S.request=' + encodeURIComponent(JSON.stringify(message)) + | ||
';end'; | ||
document.location = intentUrl; | ||
}; | ||
/** | ||
* Tells what type of port this is. | ||
* @return {String} port type | ||
*/ | ||
u2f.WrappedAuthenticatorPort_.prototype.getPortType = function () { | ||
return "WrappedAuthenticatorPort_"; | ||
}; | ||
/** | ||
* Emulates the HTML 5 addEventListener interface. | ||
* @param {string} eventName | ||
* @param {function({data: Object})} handler | ||
*/ | ||
u2f.WrappedAuthenticatorPort_.prototype.addEventListener = function (eventName, handler) { | ||
var name = eventName.toLowerCase(); | ||
if (name == 'message' || name == 'onmessage') { | ||
this.port_.onMessage.addListener(function(message) { | ||
// Emulate a minimal MessageEvent object | ||
handler({'data': message}); | ||
}); | ||
if (name == 'message') { | ||
var self = this; | ||
/* Register a callback to that executes when | ||
* chrome injects the response. */ | ||
window.addEventListener( | ||
'message', self.onRequestUpdate_.bind(self, handler), false); | ||
} else { | ||
console.error('WrappedChromeRuntimePort only supports onMessage'); | ||
console.error('WrappedAuthenticatorPort only supports message'); | ||
} | ||
@@ -239,2 +460,68 @@ }; | ||
/** | ||
* Callback invoked when a response is received from the Authenticator. | ||
* @param function({data: Object}) callback | ||
* @param {Object} message message Object | ||
*/ | ||
u2f.WrappedAuthenticatorPort_.prototype.onRequestUpdate_ = | ||
function (callback, message) { | ||
var messageObject = JSON.parse(message.data); | ||
var intentUrl = messageObject['intentURL']; | ||
var errorCode = messageObject['errorCode']; | ||
var responseObject = null; | ||
if (messageObject.hasOwnProperty('data')) { | ||
responseObject = /** @type {Object} */ ( | ||
JSON.parse(messageObject['data'])); | ||
} | ||
callback({'data': responseObject}); | ||
}; | ||
/** | ||
* Base URL for intents to Authenticator. | ||
* @const | ||
* @private | ||
*/ | ||
u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ = | ||
'intent:#Intent;action=com.google.android.apps.authenticator.AUTHENTICATE'; | ||
/** | ||
* Wrap the iOS client app with a MessagePort interface. | ||
* @constructor | ||
* @private | ||
*/ | ||
u2f.WrappedIosPort_ = function () { | ||
}; | ||
/** | ||
* Launch the iOS client app request | ||
* @param {Object} message | ||
*/ | ||
u2f.WrappedIosPort_.prototype.postMessage = function (message) { | ||
var str = JSON.stringify(message); | ||
var url = "u2f://auth?" + encodeURI(str); | ||
location.replace(url); | ||
}; | ||
/** | ||
* Tells what type of port this is. | ||
* @return {String} port type | ||
*/ | ||
u2f.WrappedIosPort_.prototype.getPortType = function () { | ||
return "WrappedIosPort_"; | ||
}; | ||
/** | ||
* Emulates the HTML 5 addEventListener interface. | ||
* @param {string} eventName | ||
* @param {function({data: Object})} handler | ||
*/ | ||
u2f.WrappedIosPort_.prototype.addEventListener = function (eventName, handler) { | ||
var name = eventName.toLowerCase(); | ||
if (name !== 'message') { | ||
console.error('WrappedIosPort only supports message'); | ||
} | ||
}; | ||
/** | ||
* Sets up an embedded trampoline iframe, sourced from the extension. | ||
@@ -244,3 +531,3 @@ * @param {function(MessagePort)} callback | ||
*/ | ||
u2f.getIframePort_ = function(callback) { | ||
u2f.getIframePort_ = function (callback) { | ||
// Create the iframe | ||
@@ -256,3 +543,3 @@ var iframeOrigin = 'chrome-extension://' + u2f.EXTENSION_ID; | ||
var channel = new MessageChannel(); | ||
var ready = function(message) { | ||
var ready = function (message) { | ||
if (message.data == 'ready') { | ||
@@ -272,3 +559,3 @@ channel.port1.removeEventListener('message', ready); | ||
iframe.addEventListener('load', function() { | ||
iframe.addEventListener('load', function () { | ||
// Deliver the port to the iframe and initialize | ||
@@ -289,3 +576,3 @@ iframe.contentWindow.postMessage('init', iframeOrigin, [channel.port2]); | ||
// High-level JS API | ||
//High-level JS API | ||
@@ -307,3 +594,3 @@ /** | ||
* Callbacks waiting for a port | ||
* @type {Array.<function((MessagePort|u2f.WrappedChromeRuntimePort_))>} | ||
* @type {Array<function((MessagePort|u2f.WrappedChromeRuntimePort_))>} | ||
* @private | ||
@@ -333,3 +620,3 @@ */ | ||
*/ | ||
u2f.getPortSingleton_ = function(callback) { | ||
u2f.getPortSingleton_ = function (callback) { | ||
if (u2f.port_) { | ||
@@ -339,3 +626,4 @@ callback(null, u2f.port_); | ||
if (u2f.waitingForPort_.length == 0) { | ||
u2f.getMessagePort(function(err, port) { | ||
u2f.getMessagePort(function (err, port) { | ||
console.log(port); | ||
if (!err) { | ||
@@ -361,3 +649,3 @@ u2f.port_ = port; | ||
*/ | ||
u2f.responseHandler_ = function(message) { | ||
u2f.responseHandler_ = function (message) { | ||
var response = message.data; | ||
@@ -382,6 +670,9 @@ var reqId = response['requestId']; | ||
}); | ||
} | ||
}; | ||
/** | ||
* Dispatches an array of sign requests to available U2F tokens. | ||
* If the JS API version supported by the extension is unknown, it first sends a | ||
* message to the extension to find out the supported API version and then it sends | ||
* the sign request. | ||
* @param {Array.<u2f.SignRequest>} signRequests | ||
@@ -392,2 +683,30 @@ * @param {function((u2f.Error|u2f.SignResponse))} callback | ||
u2f.sign = function(signRequests, callback, opt_timeoutSeconds) { | ||
var pathArray = location.href.split('/'); | ||
var protocol = pathArray[0]; | ||
var host = pathArray[2]; | ||
var appId = protocol + '//' + host; | ||
var challenge = signRequests[0].challenge; | ||
if (js_api_version === undefined) { | ||
// Send a message to get the extension to JS API version, then send the actual sign request. | ||
u2f.getApiVersion( | ||
function (response) { | ||
js_api_version = response['js_api_version'] === undefined ? 0 : response['js_api_version']; | ||
console.log("Extension JS API Version: ", js_api_version); | ||
u2f.sendSignRequest(appId, challenge, signRequests, callback, opt_timeoutSeconds); | ||
}); | ||
} else { | ||
// We know the JS API version. Send the actual sign request in the supported API version. | ||
u2f.sendSignRequest(appId, challenge, signRequests, callback, opt_timeoutSeconds); | ||
} | ||
}; | ||
/** | ||
* Dispatches an array of sign requests to available U2F tokens. | ||
* @param {string=} appId | ||
* @param {string=} challenge | ||
* @param {Array<u2f.RegisteredKey>} registeredKeys | ||
* @param {function((u2f.Error|u2f.SignResponse))} callback | ||
* @param {number=} opt_timeoutSeconds | ||
*/ | ||
u2f.sendSignRequest = function (appId, challenge, registeredKeys, callback, opt_timeoutSeconds) { | ||
u2f.getPortSingleton_(function(err, port) { | ||
@@ -399,9 +718,5 @@ if (err) | ||
u2f.callbackMap_[reqId] = callback; | ||
var req = { | ||
type: u2f.MessageTypes.U2F_SIGN_REQUEST, | ||
signRequests: signRequests, | ||
timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ? | ||
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC), | ||
requestId: reqId | ||
}; | ||
var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ? | ||
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC); | ||
var req = u2f.formatSignRequest_(appId, challenge, registeredKeys, timeoutSeconds, reqId); | ||
port.postMessage(req); | ||
@@ -414,7 +729,37 @@ }); | ||
* requests identifies already registered tokens. | ||
* @param {Array.<u2f.RegisterRequest>} registerRequests | ||
* @param {Array.<u2f.SignRequest>} signRequests | ||
* If the JS API version supported by the extension is unknown, it first sends a | ||
* message to the extension to find out the supported API version and then it sends | ||
* the register request. | ||
* @param {string=} appId | ||
* @param {Array<u2f.RegisterRequest>} registerRequests | ||
* @param {Array<u2f.RegisteredKey>} registeredKeys | ||
* @param {function((u2f.Error|u2f.RegisterResponse))} callback | ||
* @param {number=} opt_timeoutSeconds | ||
*/ | ||
u2f.register = function (appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) { | ||
if (js_api_version === undefined) { | ||
// Send a message to get the extension to JS API version, then send the actual register request. | ||
u2f.getApiVersion( | ||
function (response) { | ||
js_api_version = response['js_api_version'] === undefined ? 0 : response['js_api_version']; | ||
console.log("Extension JS API Version: ", js_api_version); | ||
u2f.sendRegisterRequest(appId, registerRequests, registeredKeys, | ||
callback, opt_timeoutSeconds); | ||
}); | ||
} else { | ||
// We know the JS API version. Send the actual register request in the supported API version. | ||
u2f.sendRegisterRequest(appId, registerRequests, registeredKeys, | ||
callback, opt_timeoutSeconds); | ||
} | ||
}; | ||
/** | ||
* Dispatches register requests to available U2F tokens. An array of sign | ||
* requests identifies already registered tokens. | ||
* @param {string=} appId | ||
* @param {Array<u2f.RegisterRequest>} registerRequests | ||
* @param {Array<u2f.RegisteredKey>} registeredKeys | ||
* @param {function((u2f.Error|u2f.RegisterResponse))} callback | ||
* @param {number=} opt_timeoutSeconds | ||
*/ | ||
u2f.register = function(registerRequests, signRequests, | ||
@@ -429,5 +774,3 @@ callback, opt_timeoutSeconds) { | ||
var req = { | ||
type: u2f.MessageTypes.U2F_REGISTER_REQUEST, | ||
signRequests: signRequests, | ||
registerRequests: registerRequests, | ||
type: u2f.MessageTypes.U2F_GET_API_VERSION_REQUEST, | ||
timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ? | ||
@@ -440,1 +783,65 @@ opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC), | ||
}; | ||
/** | ||
* Dispatches register requests to available U2F tokens. An array of sign | ||
* requests identifies already registered tokens. | ||
* @param {string=} appId | ||
* @param {Array<u2f.RegisterRequest>} registerRequests | ||
* @param {Array<u2f.RegisteredKey>} registeredKeys | ||
* @param {function((u2f.Error|u2f.RegisterResponse))} callback | ||
* @param {number=} opt_timeoutSeconds | ||
*/ | ||
u2f.sendRegisterRequest = function (appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) { | ||
u2f.getPortSingleton_(function (port) { | ||
var reqId = ++u2f.reqCounter_; | ||
u2f.callbackMap_[reqId] = callback; | ||
var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ? | ||
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC); | ||
var req = u2f.formatRegisterRequest_( | ||
appId, registeredKeys, registerRequests, timeoutSeconds, reqId); | ||
port.postMessage(req); | ||
}); | ||
}; | ||
/** | ||
* Dispatches a message to the extension to find out the supported | ||
* JS API version. | ||
* If the user is on a mobile phone and is thus using Google Authenticator instead | ||
* of the Chrome extension, don't send the request and simply return 0. | ||
* @param {function((u2f.Error|u2f.GetJsApiVersionResponse))} callback | ||
* @param {number=} opt_timeoutSeconds | ||
*/ | ||
u2f.getApiVersion = function (callback, opt_timeoutSeconds) { | ||
u2f.getPortSingleton_(function (err, port) { | ||
if (err) { | ||
callback(-1); | ||
} | ||
// If we are using Android Google Authenticator or iOS client app, | ||
// do not fire an intent to ask which JS API version to use. | ||
if (port.getPortType) { | ||
var apiVersion; | ||
switch (port.getPortType()) { | ||
case 'WrappedIosPort_': | ||
case 'WrappedAuthenticatorPort_': | ||
apiVersion = 1.1; | ||
break; | ||
default: | ||
apiVersion = 0; | ||
break; | ||
} | ||
callback({'js_api_version': apiVersion}); | ||
return; | ||
} | ||
var reqId = ++u2f.reqCounter_; | ||
u2f.callbackMap_[reqId] = callback; | ||
var req = { | ||
type: u2f.MessageTypes.U2F_GET_API_VERSION_REQUEST, | ||
timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ? | ||
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC), | ||
requestId: reqId | ||
}; | ||
port.postMessage(req); | ||
}); | ||
}; |
@@ -128,3 +128,3 @@ 'use strict'; | ||
{ | ||
if ( disconnect && !backend.native ) | ||
if ( disconnect && !backend.native && backend.u2f.disconnect ) | ||
backend.u2f.disconnect( ); | ||
@@ -131,0 +131,0 @@ |
@@ -5,3 +5,3 @@ { | ||
"license": "MIT", | ||
"version": "0.2.7", | ||
"version": "0.3.0", | ||
"author": "Gustaf Räntilä <g.rantila@gmail.com>", | ||
@@ -30,10 +30,10 @@ "repository": { | ||
"devDependencies": { | ||
"@types/mocha": "^2.2.43", | ||
"already": "^0.6.0", | ||
"chai": "^4.1.2", | ||
"jsdom": "^11.2.0", | ||
"mocha": "^3.5.3", | ||
"pre-commit": "^1.2.2", | ||
"typescript": "^2.5.3" | ||
"@types/mocha": "2.x", | ||
"already": "1.x", | ||
"chai": "4.x", | ||
"jsdom": "11.x", | ||
"mocha": "5.x", | ||
"pre-commit": "1.x", | ||
"typescript": "2.7.x" | ||
} | ||
} |
'use strict'; | ||
declare var global: any; | ||
declare var require: any; | ||
import 'mocha'; | ||
@@ -215,3 +218,3 @@ import { expect } from 'chai'; | ||
{ | ||
return ( ): Promise< void > => | ||
return async ( ): Promise< void > => | ||
{ | ||
@@ -238,3 +241,3 @@ const dom = new JSDOM( | ||
return Try( ( ) => fn( api ) ) | ||
await Try( ( ) => fn( api ) ) | ||
.then( ...Finally( ( ) => | ||
@@ -241,0 +244,0 @@ { |
73854
15
1289