|
@@ -8,354 +8,301 @@
|
8
|
8
|
|
9
|
9
|
'use strict';
|
10
|
10
|
|
11
|
|
-// Namespace for the U2F api.
|
12
|
|
-var u2f = u2f || {};
|
13
|
|
-
|
14
|
|
-// The U2F extension id
|
15
|
|
-u2f.EXTENSION_ID = 'kmendfapggjehodndflmmgagdbamhnfd';
|
16
|
|
-
|
17
|
|
-// Message types for messsages to/from the extension
|
18
|
|
-u2f.MessageTypes = {
|
19
|
|
- 'U2F_REGISTER_REQUEST': 'u2f_register_request',
|
20
|
|
- 'U2F_SIGN_REQUEST': 'u2f_sign_request',
|
21
|
|
- 'U2F_REGISTER_RESPONSE': 'u2f_register_response',
|
22
|
|
- 'U2F_SIGN_RESPONSE': 'u2f_sign_response'
|
23
|
|
-};
|
|
11
|
+if (!u2f) {
|
|
12
|
+ // Namespace for the U2F api.
|
|
13
|
+ var u2f = u2f || {};
|
24
|
14
|
|
25
|
|
-// Response status codes
|
26
|
|
-u2f.ErrorCodes = {
|
27
|
|
- 'OK': 0,
|
28
|
|
- 'OTHER_ERROR': 1,
|
29
|
|
- 'BAD_REQUEST': 2,
|
30
|
|
- 'CONFIGURATION_UNSUPPORTED': 3,
|
31
|
|
- 'DEVICE_INELIGIBLE': 4,
|
32
|
|
- 'TIMEOUT': 5
|
33
|
|
-};
|
|
15
|
+ // FIDO U2F Javascript API Version
|
|
16
|
+ var js_api_version;
|
|
17
|
+
|
|
18
|
+ // The U2F extension id
|
|
19
|
+ u2f.EXTENSION_ID = 'kmendfapggjehodndflmmgagdbamhnfd';
|
|
20
|
+
|
|
21
|
+ // Message types for messsages to/from the extension
|
|
22
|
+ u2f.MessageTypes = {
|
|
23
|
+ 'U2F_REGISTER_REQUEST': 'u2f_register_request',
|
|
24
|
+ 'U2F_REGISTER_RESPONSE': 'u2f_register_response',
|
|
25
|
+ 'U2F_SIGN_REQUEST': 'u2f_sign_request',
|
|
26
|
+ 'U2F_SIGN_RESPONSE': 'u2f_sign_response',
|
|
27
|
+ 'U2F_GET_API_VERSION_REQUEST': 'u2f_get_api_version_request',
|
|
28
|
+ 'U2F_GET_API_VERSION_RESPONSE': 'u2f_get_api_version_response'
|
|
29
|
+ };
|
|
30
|
+
|
|
31
|
+ // A message for registration requests
|
|
32
|
+ u2f.U2fRequest;
|
34
|
33
|
|
35
|
|
-// A message type for registration requests
|
36
|
|
-u2f.Request;
|
|
34
|
+ // A message for registration responses
|
|
35
|
+ u2f.U2fResponse;
|
37
|
36
|
|
38
|
|
-// A message for registration responses
|
39
|
|
-u2f.Response;
|
|
37
|
+ // An error object for responses
|
|
38
|
+ u2f.Error;
|
40
|
39
|
|
41
|
|
-// An error object for responses
|
42
|
|
-u2f.Error;
|
|
40
|
+ // Data object for a single sign request.
|
|
41
|
+ u2f.Transport;
|
43
|
42
|
|
44
|
|
-// Data object for a single sign request.
|
45
|
|
-u2f.SignRequest;
|
|
43
|
+ // Data object for a single sign request.
|
|
44
|
+ u2f.Transports;
|
46
|
45
|
|
47
|
|
-// Data object for a sign response.
|
48
|
|
-u2f.SignResponse;
|
|
46
|
+ // Data object for a single sign request.
|
|
47
|
+ u2f.SignRequest;
|
49
|
48
|
|
50
|
|
-// Data object for a registration request.
|
51
|
|
-u2f.RegisterRequest;
|
|
49
|
+ // Data object for a sign response.
|
|
50
|
+ u2f.SignResponse;
|
52
|
51
|
|
53
|
|
-// Data object for a registration response.
|
54
|
|
-u2f.RegisterResponse;
|
|
52
|
+ // Data object for a registration request.
|
|
53
|
+ u2f.RegisterRequest;
|
55
|
54
|
|
|
55
|
+ // Data object for a registration response.
|
|
56
|
+ u2f.RegisterResponse;
|
56
|
57
|
|
57
|
|
-// Low level MessagePort API support
|
|
58
|
+ // Data object for a registered key.
|
|
59
|
+ u2f.RegisteredKey;
|
58
|
60
|
|
59
|
|
-// Sets up a MessagePort to the U2F extension using the available mechanisms.
|
60
|
|
-u2f.getMessagePort = function(callback) {
|
|
61
|
+ // Data object for a get API register response.
|
|
62
|
+ u2f.GetJsApiVersionResponse;
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+ //Low level MessagePort API support
|
|
66
|
+
|
|
67
|
+ // Sets up a MessagePort to the U2F extension using the
|
|
68
|
+ u2f.getMessagePort = function(callback) {
|
61
|
69
|
if (typeof chrome != 'undefined' && chrome.runtime) {
|
62
|
|
- // The actual message here does not matter, but we need to get a reply
|
63
|
|
- // for the callback to run. Thus, send an empty signature request
|
64
|
|
- // in order to get a failure response.
|
65
|
|
- var msg = {
|
66
|
|
- type: u2f.MessageTypes.U2F_SIGN_REQUEST,
|
67
|
|
- signRequests: []
|
68
|
|
- };
|
69
|
|
- chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function() {
|
70
|
|
- if (!chrome.runtime.lastError) {
|
71
|
|
- // We are on a whitelisted origin and can talk directly
|
72
|
|
- // with the extension.
|
73
|
|
- u2f.getChromeRuntimePort_(callback);
|
74
|
|
- } else {
|
75
|
|
- // chrome.runtime was available, but we couldn't message
|
76
|
|
- // the extension directly, use iframe
|
77
|
|
- u2f.getIframePort_(callback);
|
78
|
|
- }
|
79
|
|
- });
|
|
70
|
+ // The actual message here does not matter, but we need to get a reply
|
|
71
|
+ // for the callback to run. Thus, send an empty signature request
|
|
72
|
+ // in order to get a failure response.
|
|
73
|
+ var msg = {
|
|
74
|
+ type: u2f.MessageTypes.U2F_SIGN_REQUEST,
|
|
75
|
+ signRequests: []
|
|
76
|
+ };
|
|
77
|
+ chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function() {
|
|
78
|
+ if (!chrome.runtime.lastError) {
|
|
79
|
+ // We are on a whitelisted origin and can talk directly
|
|
80
|
+ // with the extension.
|
|
81
|
+ u2f.getChromeRuntimePort_(callback);
|
|
82
|
+ } else {
|
|
83
|
+ // chrome.runtime was available, but we couldn't message
|
|
84
|
+ // the extension directly, use iframe
|
|
85
|
+ u2f.getIframePort_(callback);
|
|
86
|
+ }
|
|
87
|
+ });
|
80
|
88
|
} else if (u2f.isAndroidChrome_()) {
|
81
|
|
- u2f.getAuthenticatorPort_(callback);
|
|
89
|
+ u2f.getAuthenticatorPort_(callback);
|
|
90
|
+ } else if (u2f.isIosChrome_()) {
|
|
91
|
+ u2f.getIosPort_(callback);
|
82
|
92
|
} else {
|
83
|
|
- // chrome.runtime was not available at all, which is normal
|
84
|
|
- // when this origin doesn't have access to any extensions.
|
85
|
|
- u2f.getIframePort_(callback);
|
|
93
|
+ // chrome.runtime was not available at all, which is normal
|
|
94
|
+ // when this origin doesn't have access to any extensions.
|
|
95
|
+ u2f.getIframePort_(callback);
|
86
|
96
|
}
|
87
|
|
-};
|
|
97
|
+ };
|
88
|
98
|
|
89
|
|
-// Detect chrome running on android based on the browser's useragent.
|
90
|
|
-u2f.isAndroidChrome_ = function() {
|
|
99
|
+ // Detect chrome running on android based on the browser's useragent.
|
|
100
|
+ u2f.isAndroidChrome_ = function() {
|
91
|
101
|
var userAgent = navigator.userAgent;
|
92
|
102
|
return userAgent.indexOf('Chrome') != -1 &&
|
93
|
|
- userAgent.indexOf('Android') != -1;
|
94
|
|
-};
|
|
103
|
+ userAgent.indexOf('Android') != -1;
|
|
104
|
+ };
|
|
105
|
+
|
|
106
|
+ // Detect chrome running on iOS based on the browser's platform.
|
|
107
|
+ u2f.isIosChrome_ = function() {
|
|
108
|
+ return ["iPhone", "iPad", "iPod"].indexOf(navigator.platform) > -1;
|
|
109
|
+ };
|
95
|
110
|
|
96
|
|
-// Connects directly to the extension via chrome.runtime.connect
|
97
|
|
-u2f.getChromeRuntimePort_ = function(callback) {
|
|
111
|
+ // Connects directly to the extension via chrome.runtime.connect.
|
|
112
|
+ u2f.getChromeRuntimePort_ = function(callback) {
|
98
|
113
|
var port = chrome.runtime.connect(u2f.EXTENSION_ID,
|
99
|
114
|
{'includeTlsChannelId': true});
|
100
|
115
|
setTimeout(function() {
|
101
|
|
- callback(new u2f.WrappedChromeRuntimePort_(port));
|
|
116
|
+ callback(new u2f.WrappedChromeRuntimePort_(port));
|
102
|
117
|
}, 0);
|
103
|
|
-};
|
|
118
|
+ };
|
104
|
119
|
|
105
|
|
-// Return a 'port' abstraction to the Authenticator app.
|
106
|
|
-u2f.getAuthenticatorPort_ = function(callback) {
|
|
120
|
+ // Return a 'port' abstraction to the Authenticator app.
|
|
121
|
+ u2f.getAuthenticatorPort_ = function(callback) {
|
107
|
122
|
setTimeout(function() {
|
108
|
|
- callback(new u2f.WrappedAuthenticatorPort_());
|
|
123
|
+ callback(new u2f.WrappedAuthenticatorPort_());
|
109
|
124
|
}, 0);
|
110
|
|
-};
|
|
125
|
+ };
|
111
|
126
|
|
112
|
|
-// A wrapper for chrome.runtime.Port that is compatible with MessagePort.
|
113
|
|
-u2f.WrappedChromeRuntimePort_ = function(port) {
|
114
|
|
- this.port_ = port;
|
115
|
|
-};
|
|
127
|
+ // Return a 'port' abstraction to the iOS client app.
|
|
128
|
+ u2f.getIosPort_ = function(callback) {
|
|
129
|
+ setTimeout(function() {
|
|
130
|
+ callback(new u2f.WrappedIosPort_());
|
|
131
|
+ }, 0);
|
|
132
|
+ };
|
116
|
133
|
|
117
|
|
-// Format a return a sign request.
|
118
|
|
-u2f.WrappedChromeRuntimePort_.prototype.formatSignRequest_ =
|
119
|
|
- function(signRequests, timeoutSeconds, reqId) {
|
120
|
|
- return {
|
121
|
|
- type: u2f.MessageTypes.U2F_SIGN_REQUEST,
|
122
|
|
- signRequests: signRequests,
|
123
|
|
- timeoutSeconds: timeoutSeconds,
|
124
|
|
- requestId: reqId
|
|
134
|
+ // A wrapper for chrome.runtime.Port that is compatible with MessagePort.
|
|
135
|
+ u2f.WrappedChromeRuntimePort_ = function(port) {
|
|
136
|
+ this.port_ = port;
|
|
137
|
+ };
|
|
138
|
+
|
|
139
|
+ // Format and return a sign request compliant with the JS API version supported by the extension.
|
|
140
|
+ u2f.formatSignRequest_ =
|
|
141
|
+ function(appId, challenge, registeredKeys, timeoutSeconds, reqId) {
|
|
142
|
+ if (js_api_version === undefined || js_api_version < 1.1) {
|
|
143
|
+ // Adapt request to the 1.0 JS API
|
|
144
|
+ var signRequests = [];
|
|
145
|
+ for (var i = 0; i < registeredKeys.length; i++) {
|
|
146
|
+ signRequests[i] = {
|
|
147
|
+ version: registeredKeys[i].version,
|
|
148
|
+ challenge: challenge,
|
|
149
|
+ keyHandle: registeredKeys[i].keyHandle,
|
|
150
|
+ appId: appId
|
125
|
151
|
};
|
|
152
|
+ }
|
|
153
|
+ return {
|
|
154
|
+ type: u2f.MessageTypes.U2F_SIGN_REQUEST,
|
|
155
|
+ signRequests: signRequests,
|
|
156
|
+ timeoutSeconds: timeoutSeconds,
|
|
157
|
+ requestId: reqId
|
|
158
|
+ };
|
|
159
|
+ }
|
|
160
|
+ // JS 1.1 API
|
|
161
|
+ return {
|
|
162
|
+ type: u2f.MessageTypes.U2F_SIGN_REQUEST,
|
|
163
|
+ appId: appId,
|
|
164
|
+ challenge: challenge,
|
|
165
|
+ registeredKeys: registeredKeys,
|
|
166
|
+ timeoutSeconds: timeoutSeconds,
|
|
167
|
+ requestId: reqId
|
126
|
168
|
};
|
127
|
|
-
|
128
|
|
-// Format a return a register request.
|
129
|
|
-u2f.WrappedChromeRuntimePort_.prototype.formatRegisterRequest_ =
|
130
|
|
- function(signRequests, registerRequests, timeoutSeconds, reqId) {
|
131
|
|
- return {
|
132
|
|
- type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
|
133
|
|
- signRequests: signRequests,
|
134
|
|
- registerRequests: registerRequests,
|
135
|
|
- timeoutSeconds: timeoutSeconds,
|
136
|
|
- requestId: reqId
|
|
169
|
+ };
|
|
170
|
+
|
|
171
|
+ // Format and return a register request compliant with the JS API version supported by the extension.
|
|
172
|
+ u2f.formatRegisterRequest_ =
|
|
173
|
+ function(appId, registeredKeys, registerRequests, timeoutSeconds, reqId) {
|
|
174
|
+ if (js_api_version === undefined || js_api_version < 1.1) {
|
|
175
|
+ // Adapt request to the 1.0 JS API
|
|
176
|
+ for (var i = 0; i < registerRequests.length; i++) {
|
|
177
|
+ registerRequests[i].appId = appId;
|
|
178
|
+ }
|
|
179
|
+ var signRequests = [];
|
|
180
|
+ for (var i = 0; i < registeredKeys.length; i++) {
|
|
181
|
+ signRequests[i] = {
|
|
182
|
+ version: registeredKeys[i].version,
|
|
183
|
+ challenge: registerRequests[0],
|
|
184
|
+ keyHandle: registeredKeys[i].keyHandle,
|
|
185
|
+ appId: appId
|
137
|
186
|
};
|
|
187
|
+ }
|
|
188
|
+ return {
|
|
189
|
+ type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
|
|
190
|
+ signRequests: signRequests,
|
|
191
|
+ registerRequests: registerRequests,
|
|
192
|
+ timeoutSeconds: timeoutSeconds,
|
|
193
|
+ requestId: reqId
|
|
194
|
+ };
|
|
195
|
+ }
|
|
196
|
+ // JS 1.1 API
|
|
197
|
+ return {
|
|
198
|
+ type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
|
|
199
|
+ appId: appId,
|
|
200
|
+ registerRequests: registerRequests,
|
|
201
|
+ registeredKeys: registeredKeys,
|
|
202
|
+ timeoutSeconds: timeoutSeconds,
|
|
203
|
+ requestId: reqId
|
138
|
204
|
};
|
|
205
|
+ };
|
139
|
206
|
|
140
|
|
-// Posts a message on the underlying channel.
|
141
|
|
-u2f.WrappedChromeRuntimePort_.prototype.postMessage = function(message) {
|
142
|
|
- this.port_.postMessage(message);
|
143
|
|
-};
|
144
|
207
|
|
145
|
|
-// Emulates the HTML 5 addEventListener interface. Works only for the
|
146
|
|
-// onmessage event, which is hooked up to the chrome.runtime.Port.onMessage.
|
147
|
|
-u2f.WrappedChromeRuntimePort_.prototype.addEventListener =
|
148
|
|
- function(eventName, handler) {
|
149
|
|
- var name = eventName.toLowerCase();
|
150
|
|
- if (name == 'message' || name == 'onmessage') {
|
151
|
|
- this.port_.onMessage.addListener(function(message) {
|
152
|
|
- // Emulate a minimal MessageEvent object
|
153
|
|
- handler({'data': message});
|
154
|
|
- });
|
155
|
|
- } else {
|
156
|
|
- console.error('WrappedChromeRuntimePort only supports onMessage');
|
157
|
|
- }
|
158
|
|
- };
|
|
208
|
+ // Posts a message on the underlying channel.
|
|
209
|
+ u2f.WrappedChromeRuntimePort_.prototype.postMessage = function(message) {
|
|
210
|
+ this.port_.postMessage(message);
|
|
211
|
+ };
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+ // Emulates the HTML 5 addEventListener interface. Works only for the onmessage event, which is hooked up to the chrome.runtime.Port.onMessage.
|
|
215
|
+ u2f.WrappedChromeRuntimePort_.prototype.addEventListener =
|
|
216
|
+ function(eventName, handler) {
|
|
217
|
+ var name = eventName.toLowerCase();
|
|
218
|
+ if (name == 'message' || name == 'onmessage') {
|
|
219
|
+ this.port_.onMessage.addListener(function(message) {
|
|
220
|
+ // Emulate a minimal MessageEvent object
|
|
221
|
+ handler({'data': message});
|
|
222
|
+ });
|
|
223
|
+ } else {
|
|
224
|
+ console.error('WrappedChromeRuntimePort only supports onMessage');
|
|
225
|
+ }
|
|
226
|
+ };
|
159
|
227
|
|
160
|
|
-// Wrap the Authenticator app with a MessagePort interface.
|
161
|
|
-u2f.WrappedAuthenticatorPort_ = function() {
|
|
228
|
+ // Wrap the Authenticator app with a MessagePort interface.
|
|
229
|
+ u2f.WrappedAuthenticatorPort_ = function() {
|
162
|
230
|
this.requestId_ = -1;
|
163
|
231
|
this.requestObject_ = null;
|
164
|
|
-}
|
165
|
|
-
|
166
|
|
-// Launch the Authenticator intent.
|
167
|
|
-u2f.WrappedAuthenticatorPort_.prototype.postMessage = function(message) {
|
168
|
|
- var intentLocation = /** @type {string} */ (message);
|
169
|
|
- document.location = intentLocation;
|
170
|
|
-};
|
171
|
|
-
|
172
|
|
-// Emulates the HTML5 addEventListener interface.
|
173
|
|
-u2f.WrappedAuthenticatorPort_.prototype.addEventListener =
|
174
|
|
- function(eventName, handler) {
|
175
|
|
- var name = eventName.toLowerCase();
|
176
|
|
- if (name == 'message') {
|
177
|
|
- var self = this;
|
178
|
|
- /* Register a callback to that executes when
|
179
|
|
- * chrome injects the response. */
|
180
|
|
- window.addEventListener(
|
181
|
|
- 'message', self.onRequestUpdate_.bind(self, handler), false);
|
182
|
|
- } else {
|
183
|
|
- console.error('WrappedAuthenticatorPort only supports message');
|
184
|
|
- }
|
185
|
|
- };
|
186
|
|
-
|
187
|
|
-// Callback invoked when a response is received from the Authenticator.
|
188
|
|
-u2f.WrappedAuthenticatorPort_.prototype.onRequestUpdate_ =
|
189
|
|
- function(callback, message) {
|
190
|
|
- var messageObject = JSON.parse(message.data);
|
191
|
|
- var intentUrl = messageObject['intentURL'];
|
192
|
|
-
|
193
|
|
- var errorCode = messageObject['errorCode'];
|
194
|
|
- var responseObject = null;
|
195
|
|
- if (messageObject.hasOwnProperty('data')) {
|
196
|
|
- responseObject = /** @type {Object} */ (
|
197
|
|
- JSON.parse(messageObject['data']));
|
198
|
|
- responseObject['requestId'] = this.requestId_;
|
199
|
|
- }
|
200
|
|
-
|
201
|
|
- /* Sign responses from the authenticator do not conform to U2F,
|
202
|
|
- * convert to U2F here. */
|
203
|
|
- responseObject = this.doResponseFixups_(responseObject);
|
204
|
|
- callback({'data': responseObject});
|
205
|
|
- };
|
|
232
|
+ }
|
206
|
233
|
|
207
|
|
-// Fixup the response provided by the Authenticator to conform with the U2F spec.
|
208
|
|
-u2f.WrappedAuthenticatorPort_.prototype.doResponseFixups_ =
|
209
|
|
- function(responseObject) {
|
210
|
|
- if (responseObject.hasOwnProperty('responseData')) {
|
211
|
|
- return responseObject;
|
212
|
|
- } else if (this.requestObject_['type'] != u2f.MessageTypes.U2F_SIGN_REQUEST) {
|
213
|
|
- // Only sign responses require fixups. If this is not a response
|
214
|
|
- // to a sign request, then an internal error has occurred.
|
215
|
|
- return {
|
216
|
|
- 'type': u2f.MessageTypes.U2F_REGISTER_RESPONSE,
|
217
|
|
- 'responseData': {
|
218
|
|
- 'errorCode': u2f.ErrorCodes.OTHER_ERROR,
|
219
|
|
- 'errorMessage': 'Internal error: invalid response from Authenticator'
|
220
|
|
- }
|
221
|
|
- };
|
222
|
|
- }
|
|
234
|
+ // Launch the Authenticator intent.
|
|
235
|
+ u2f.WrappedAuthenticatorPort_.prototype.postMessage = function(message) {
|
|
236
|
+ var intentUrl =
|
|
237
|
+ u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ +
|
|
238
|
+ ';S.request=' + encodeURIComponent(JSON.stringify(message)) +
|
|
239
|
+ ';end';
|
|
240
|
+ document.location = intentUrl;
|
|
241
|
+ };
|
|
242
|
+
|
|
243
|
+ // Tells what type of port this is.
|
|
244
|
+ u2f.WrappedAuthenticatorPort_.prototype.getPortType = function() {
|
|
245
|
+ return "WrappedAuthenticatorPort_";
|
|
246
|
+ };
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+ // Emulates the HTML 5 addEventListener interface.
|
|
250
|
+ u2f.WrappedAuthenticatorPort_.prototype.addEventListener = function(eventName, handler) {
|
|
251
|
+ var name = eventName.toLowerCase();
|
|
252
|
+ if (name == 'message') {
|
|
253
|
+ var self = this;
|
|
254
|
+ // Register a callback to that executes when chrome injects the response.
|
|
255
|
+ window.addEventListener('message', self.onRequestUpdate_.bind(self, handler), false);
|
|
256
|
+ } else {
|
|
257
|
+ console.error('WrappedAuthenticatorPort only supports message');
|
|
258
|
+ }
|
|
259
|
+ };
|
|
260
|
+
|
|
261
|
+ // Callback invoked when a response is received from the Authenticator.
|
|
262
|
+ u2f.WrappedAuthenticatorPort_.prototype.onRequestUpdate_ =
|
|
263
|
+ function(callback, message) {
|
|
264
|
+ var messageObject = JSON.parse(message.data);
|
|
265
|
+ var intentUrl = messageObject['intentURL'];
|
|
266
|
+
|
|
267
|
+ var errorCode = messageObject['errorCode'];
|
|
268
|
+ var responseObject = null;
|
|
269
|
+ if (messageObject.hasOwnProperty('data')) {
|
|
270
|
+ responseObject = /** @type {Object} */ (
|
|
271
|
+ JSON.parse(messageObject['data']));
|
|
272
|
+ }
|
223
|
273
|
|
224
|
|
- /* Non-conformant sign response, do fixups. */
|
225
|
|
- var encodedChallengeObject = responseObject['challenge'];
|
226
|
|
- if (typeof encodedChallengeObject !== 'undefined') {
|
227
|
|
- var challengeObject = JSON.parse(atob(encodedChallengeObject));
|
228
|
|
- var serverChallenge = challengeObject['challenge'];
|
229
|
|
- var challengesList = this.requestObject_['signData'];
|
230
|
|
- var requestChallengeObject = null;
|
231
|
|
- for (var i = 0; i < challengesList.length; i++) {
|
232
|
|
- var challengeObject = challengesList[i];
|
233
|
|
- if (challengeObject['keyHandle'] == responseObject['keyHandle']) {
|
234
|
|
- requestChallengeObject = challengeObject;
|
235
|
|
- break;
|
236
|
|
- }
|
237
|
|
- }
|
238
|
|
- }
|
239
|
|
- var responseData = {
|
240
|
|
- 'errorCode': responseObject['resultCode'],
|
241
|
|
- 'keyHandle': responseObject['keyHandle'],
|
242
|
|
- 'signatureData': responseObject['signature'],
|
243
|
|
- 'clientData': encodedChallengeObject
|
244
|
|
- };
|
245
|
|
- return {
|
246
|
|
- 'type': u2f.MessageTypes.U2F_SIGN_RESPONSE,
|
247
|
|
- 'responseData': responseData,
|
248
|
|
- 'requestId': responseObject['requestId']
|
249
|
|
- }
|
250
|
|
- };
|
|
274
|
+ callback({'data': responseObject});
|
|
275
|
+ };
|
251
|
276
|
|
252
|
|
-// Base URL for intents to Authenticator.
|
253
|
|
-u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ =
|
|
277
|
+ // Base URL for intents to Authenticator.
|
|
278
|
+ u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ =
|
254
|
279
|
'intent:#Intent;action=com.google.android.apps.authenticator.AUTHENTICATE';
|
255
|
280
|
|
256
|
|
-// Format a return a sign request.
|
257
|
|
-u2f.WrappedAuthenticatorPort_.prototype.formatSignRequest_ =
|
258
|
|
- function(signRequests, timeoutSeconds, reqId) {
|
259
|
|
- if (!signRequests || signRequests.length == 0) {
|
260
|
|
- return null;
|
261
|
|
- }
|
262
|
|
- /* TODO(fixme): stash away requestId, as the authenticator app does
|
263
|
|
- * not return it for sign responses. */
|
264
|
|
- this.requestId_ = reqId;
|
265
|
|
- /* TODO(fixme): stash away the signRequests, to deal with the legacy
|
266
|
|
- * response format returned by the Authenticator app. */
|
267
|
|
- this.requestObject_ = {
|
268
|
|
- 'type': u2f.MessageTypes.U2F_SIGN_REQUEST,
|
269
|
|
- 'signData': signRequests,
|
270
|
|
- 'requestId': reqId,
|
271
|
|
- 'timeout': timeoutSeconds
|
272
|
|
- };
|
273
|
|
-
|
274
|
|
- var appId = signRequests[0]['appId'];
|
275
|
|
- var intentUrl =
|
276
|
|
- u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ +
|
277
|
|
- ';S.appId=' + encodeURIComponent(appId) +
|
278
|
|
- ';S.eventId=' + reqId +
|
279
|
|
- ';S.challenges=' +
|
280
|
|
- encodeURIComponent(
|
281
|
|
- JSON.stringify(this.getBrowserDataList_(signRequests))) + ';end';
|
282
|
|
- return intentUrl;
|
283
|
|
- };
|
284
|
|
-
|
285
|
|
-// Get the browser data objects from the challenge list
|
286
|
|
-u2f.WrappedAuthenticatorPort_
|
287
|
|
- .prototype.getBrowserDataList_ = function(challenges) {
|
288
|
|
- return challenges
|
289
|
|
- .map(function(challenge) {
|
290
|
|
- var browserData = {
|
291
|
|
- 'typ': 'navigator.id.getAssertion',
|
292
|
|
- 'challenge': challenge['challenge']
|
293
|
|
- };
|
294
|
|
- var challengeObject = {
|
295
|
|
- 'challenge' : browserData,
|
296
|
|
- 'keyHandle' : challenge['keyHandle']
|
297
|
|
- };
|
298
|
|
- return challengeObject;
|
299
|
|
- });
|
300
|
|
-};
|
301
|
|
-
|
302
|
|
-// Format a return a register request.
|
303
|
|
-u2f.WrappedAuthenticatorPort_.prototype.formatRegisterRequest_ =
|
304
|
|
- function(signRequests, enrollChallenges, timeoutSeconds, reqId) {
|
305
|
|
- if (!enrollChallenges || enrollChallenges.length == 0) {
|
306
|
|
- return null;
|
307
|
|
- }
|
308
|
|
- // Assume the appId is the same for all enroll challenges.
|
309
|
|
- var appId = enrollChallenges[0]['appId'];
|
310
|
|
- var registerRequests = [];
|
311
|
|
- for (var i = 0; i < enrollChallenges.length; i++) {
|
312
|
|
- var registerRequest = {
|
313
|
|
- 'challenge': enrollChallenges[i]['challenge'],
|
314
|
|
- 'version': enrollChallenges[i]['version']
|
315
|
|
- };
|
316
|
|
- if (enrollChallenges[i]['appId'] != appId) {
|
317
|
|
- // Only include the appId when it differs from the first appId.
|
318
|
|
- registerRequest['appId'] = enrollChallenges[i]['appId'];
|
319
|
|
- }
|
320
|
|
- registerRequests.push(registerRequest);
|
321
|
|
- }
|
322
|
|
- var registeredKeys = [];
|
323
|
|
- if (signRequests) {
|
324
|
|
- for (i = 0; i < signRequests.length; i++) {
|
325
|
|
- var key = {
|
326
|
|
- 'keyHandle': signRequests[i]['keyHandle'],
|
327
|
|
- 'version': signRequests[i]['version']
|
328
|
|
- };
|
329
|
|
- // Only include the appId when it differs from the appId that's
|
330
|
|
- // being registered now.
|
331
|
|
- if (signRequests[i]['appId'] != appId) {
|
332
|
|
- key['appId'] = signRequests[i]['appId'];
|
333
|
|
- }
|
334
|
|
- registeredKeys.push(key);
|
335
|
|
- }
|
336
|
|
- }
|
337
|
|
- var request = {
|
338
|
|
- 'type': u2f.MessageTypes.U2F_REGISTER_REQUEST,
|
339
|
|
- 'appId': appId,
|
340
|
|
- 'registerRequests': registerRequests,
|
341
|
|
- 'registeredKeys': registeredKeys,
|
342
|
|
- 'requestId': reqId,
|
343
|
|
- 'timeoutSeconds': timeoutSeconds
|
344
|
|
- };
|
345
|
|
- var intentUrl =
|
346
|
|
- u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ +
|
347
|
|
- ';S.request=' + encodeURIComponent(JSON.stringify(request)) +
|
348
|
|
- ';end';
|
349
|
|
- /* TODO(fixme): stash away requestId, this is is not necessary for
|
350
|
|
- * register requests, but here to keep parity with sign.
|
351
|
|
- */
|
352
|
|
- this.requestId_ = reqId;
|
353
|
|
- return intentUrl;
|
354
|
|
- };
|
355
|
|
-
|
|
281
|
+ // Wrap the iOS client app with a MessagePort interface.
|
|
282
|
+ u2f.WrappedIosPort_ = function() {};
|
|
283
|
+
|
|
284
|
+ // Launch the iOS client app request
|
|
285
|
+ u2f.WrappedIosPort_.prototype.postMessage = function(message) {
|
|
286
|
+ var str = JSON.stringify(message);
|
|
287
|
+ var url = "u2f://auth?" + encodeURI(str);
|
|
288
|
+ location.replace(url);
|
|
289
|
+ };
|
|
290
|
+
|
|
291
|
+ // Tells what type of port this is.
|
|
292
|
+ u2f.WrappedIosPort_.prototype.getPortType = function() {
|
|
293
|
+ return "WrappedIosPort_";
|
|
294
|
+ };
|
|
295
|
+
|
|
296
|
+ // Emulates the HTML 5 addEventListener interface.
|
|
297
|
+ u2f.WrappedIosPort_.prototype.addEventListener = function(eventName, handler) {
|
|
298
|
+ var name = eventName.toLowerCase();
|
|
299
|
+ if (name !== 'message') {
|
|
300
|
+ console.error('WrappedIosPort only supports message');
|
|
301
|
+ }
|
|
302
|
+ };
|
356
|
303
|
|
357
|
|
-// Sets up an embedded trampoline iframe, sourced from the extension.
|
358
|
|
-u2f.getIframePort_ = function(callback) {
|
|
304
|
+ // Sets up an embedded trampoline iframe, sourced from the extension.
|
|
305
|
+ u2f.getIframePort_ = function(callback) {
|
359
|
306
|
// Create the iframe
|
360
|
307
|
var iframeOrigin = 'chrome-extension://' + u2f.EXTENSION_ID;
|
361
|
308
|
var iframe = document.createElement('iframe');
|
|
@@ -365,108 +312,193 @@ u2f.getIframePort_ = function(callback) {
|
365
|
312
|
|
366
|
313
|
var channel = new MessageChannel();
|
367
|
314
|
var ready = function(message) {
|
368
|
|
- if (message.data == 'ready') {
|
369
|
|
- channel.port1.removeEventListener('message', ready);
|
370
|
|
- callback(channel.port1);
|
371
|
|
- } else {
|
372
|
|
- console.error('First event on iframe port was not "ready"');
|
373
|
|
- }
|
|
315
|
+ if (message.data == 'ready') {
|
|
316
|
+ channel.port1.removeEventListener('message', ready);
|
|
317
|
+ callback(channel.port1);
|
|
318
|
+ } else {
|
|
319
|
+ console.error('First event on iframe port was not "ready"');
|
|
320
|
+ }
|
374
|
321
|
};
|
375
|
322
|
channel.port1.addEventListener('message', ready);
|
376
|
323
|
channel.port1.start();
|
377
|
324
|
|
378
|
325
|
iframe.addEventListener('load', function() {
|
379
|
|
- // Deliver the port to the iframe and initialize
|
380
|
|
- iframe.contentWindow.postMessage('init', iframeOrigin, [channel.port2]);
|
|
326
|
+ // Deliver the port to the iframe and initialize
|
|
327
|
+ iframe.contentWindow.postMessage('init', iframeOrigin, [channel.port2]);
|
381
|
328
|
});
|
382
|
|
-};
|
|
329
|
+ };
|
383
|
330
|
|
384
|
331
|
|
385
|
|
-// High-level JS API
|
|
332
|
+ //High-level JS API
|
386
|
333
|
|
387
|
|
-// Default extension response timeout in seconds.
|
388
|
|
-u2f.EXTENSION_TIMEOUT_SEC = 30;
|
|
334
|
+ // Default extension response timeout in seconds.
|
|
335
|
+ u2f.EXTENSION_TIMEOUT_SEC = 30;
|
389
|
336
|
|
390
|
|
-// A singleton instance for a MessagePort to the extension.
|
391
|
|
-u2f.port_ = null;
|
|
337
|
+ // A singleton instance for a MessagePort to the extension.
|
|
338
|
+ u2f.port_ = null;
|
392
|
339
|
|
393
|
|
-// Callbacks waiting for a port
|
394
|
|
-u2f.waitingForPort_ = [];
|
|
340
|
+ // Callbacks waiting for a port
|
|
341
|
+ u2f.waitingForPort_ = [];
|
395
|
342
|
|
396
|
|
-// A counter for requestIds.
|
397
|
|
-u2f.reqCounter_ = 0;
|
|
343
|
+ // A counter for requestIds.
|
|
344
|
+ u2f.reqCounter_ = 0;
|
398
|
345
|
|
399
|
|
-// A map from requestIds to client callbacks
|
400
|
|
-u2f.callbackMap_ = {};
|
|
346
|
+ // A map from requestIds to client callbacks
|
|
347
|
+ u2f.callbackMap_ = {};
|
401
|
348
|
|
402
|
|
-// Creates or retrieves the MessagePort singleton to use.
|
403
|
|
-u2f.getPortSingleton_ = function(callback) {
|
|
349
|
+ // Creates or retrieves the MessagePort singleton to use.
|
|
350
|
+ u2f.getPortSingleton_ = function(callback) {
|
404
|
351
|
if (u2f.port_) {
|
405
|
|
- callback(u2f.port_);
|
|
352
|
+ callback(u2f.port_);
|
406
|
353
|
} else {
|
407
|
|
- if (u2f.waitingForPort_.length == 0) {
|
408
|
|
- u2f.getMessagePort(function(port) {
|
409
|
|
- u2f.port_ = port;
|
410
|
|
- u2f.port_.addEventListener('message',
|
411
|
|
- /** @type {function(Event)} */ (u2f.responseHandler_));
|
412
|
|
-
|
413
|
|
- // Careful, here be async callbacks. Maybe.
|
414
|
|
- while (u2f.waitingForPort_.length)
|
415
|
|
- u2f.waitingForPort_.shift()(u2f.port_);
|
416
|
|
- });
|
417
|
|
- }
|
418
|
|
- u2f.waitingForPort_.push(callback);
|
|
354
|
+ if (u2f.waitingForPort_.length == 0) {
|
|
355
|
+ u2f.getMessagePort(function(port) {
|
|
356
|
+ u2f.port_ = port;
|
|
357
|
+ u2f.port_.addEventListener('message', (u2f.responseHandler_));
|
|
358
|
+ // Careful, here be async callbacks. Maybe.
|
|
359
|
+ while (u2f.waitingForPort_.length)
|
|
360
|
+ u2f.waitingForPort_.shift()(u2f.port_);
|
|
361
|
+ });
|
|
362
|
+ }
|
|
363
|
+ u2f.waitingForPort_.push(callback);
|
419
|
364
|
}
|
420
|
|
-};
|
|
365
|
+ };
|
421
|
366
|
|
422
|
|
-// Handles response messages from the extension.
|
423
|
|
-u2f.responseHandler_ = function(message) {
|
|
367
|
+ // Handles response messages from the extension.
|
|
368
|
+ u2f.responseHandler_ = function(message) {
|
424
|
369
|
var response = message.data;
|
425
|
370
|
var reqId = response['requestId'];
|
426
|
371
|
if (!reqId || !u2f.callbackMap_[reqId]) {
|
427
|
|
- console.error('Unknown or missing requestId in response.');
|
428
|
|
- return;
|
|
372
|
+ console.error('Unknown or missing requestId in response.');
|
|
373
|
+ return;
|
429
|
374
|
}
|
430
|
375
|
var cb = u2f.callbackMap_[reqId];
|
431
|
376
|
delete u2f.callbackMap_[reqId];
|
432
|
377
|
cb(response['responseData']);
|
433
|
|
-};
|
|
378
|
+ };
|
|
379
|
+
|
|
380
|
+ // Dispatches an array of sign requests to available U2F tokens.
|
|
381
|
+ // If the JS API version supported by the extension is unknown, it first sends a
|
|
382
|
+ // message to the extension to find out the supported API version and then it sends
|
|
383
|
+ // the sign request.
|
|
384
|
+ u2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
|
|
385
|
+ if (js_api_version === undefined) {
|
|
386
|
+ // Send a message to get the extension to JS API version, then send the actual sign request.
|
|
387
|
+ u2f.getApiVersion(
|
|
388
|
+ function (response) {
|
|
389
|
+ js_api_version = response['js_api_version'] === undefined ? 0 : response['js_api_version'];
|
|
390
|
+ console.log("Extension JS API Version: ", js_api_version);
|
|
391
|
+ u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds);
|
|
392
|
+ });
|
|
393
|
+ } else {
|
|
394
|
+ // We know the JS API version. Send the actual sign request in the supported API version.
|
|
395
|
+ u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds);
|
|
396
|
+ }
|
|
397
|
+ };
|
434
|
398
|
|
435
|
|
-// Dispatches an array of sign requests to available U2F tokens.
|
436
|
|
-u2f.sign = function(signRequests, callback, opt_timeoutSeconds) {
|
|
399
|
+ // Dispatches an array of sign requests to available U2F tokens.
|
|
400
|
+ u2f.sendSignRequest = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
|
437
|
401
|
u2f.getPortSingleton_(function(port) {
|
438
|
|
- var reqId = ++u2f.reqCounter_;
|
439
|
|
- u2f.callbackMap_[reqId] = callback;
|
440
|
|
- var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
|
441
|
|
- opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC);
|
442
|
|
- var req = port.formatSignRequest_(signRequests, timeoutSeconds, reqId);
|
443
|
|
- port.postMessage(req);
|
|
402
|
+ var reqId = ++u2f.reqCounter_;
|
|
403
|
+ u2f.callbackMap_[reqId] = callback;
|
|
404
|
+ var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
|
|
405
|
+ opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC);
|
|
406
|
+ var req = u2f.formatSignRequest_(appId, challenge, registeredKeys, timeoutSeconds, reqId);
|
|
407
|
+ port.postMessage(req);
|
444
|
408
|
});
|
445
|
|
-};
|
|
409
|
+ };
|
|
410
|
+
|
|
411
|
+ // Dispatches register requests to available U2F tokens. An array of sign
|
|
412
|
+ // requests identifies already registered tokens.
|
|
413
|
+ // If the JS API version supported by the extension is unknown, it first sends a
|
|
414
|
+ // message to the extension to find out the supported API version and then it sends
|
|
415
|
+ // the register request.
|
|
416
|
+ u2f.register = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {
|
|
417
|
+ if (js_api_version === undefined) {
|
|
418
|
+ // Send a message to get the extension to JS API version, then send the actual register request.
|
|
419
|
+ u2f.getApiVersion(
|
|
420
|
+ function (response) {
|
|
421
|
+ js_api_version = response['js_api_version'] === undefined ? 0: response['js_api_version'];
|
|
422
|
+ console.log("Extension JS API Version: ", js_api_version);
|
|
423
|
+ u2f.sendRegisterRequest(appId, registerRequests, registeredKeys,
|
|
424
|
+ callback, opt_timeoutSeconds);
|
|
425
|
+ });
|
|
426
|
+ } else {
|
|
427
|
+ // We know the JS API version. Send the actual register request in the supported API version.
|
|
428
|
+ u2f.sendRegisterRequest(appId, registerRequests, registeredKeys,
|
|
429
|
+ callback, opt_timeoutSeconds);
|
|
430
|
+ }
|
|
431
|
+ };
|
446
|
432
|
|
447
|
|
-// Dispatches register requests to available U2F tokens. An array of sign
|
448
|
|
-// requests identifies already registered tokens.
|
449
|
|
-u2f.register = function(registerRequests, signRequests,
|
450
|
|
- callback, opt_timeoutSeconds) {
|
|
433
|
+ // Dispatches register requests to available U2F tokens. An array of sign
|
|
434
|
+ // requests identifies already registered tokens.
|
|
435
|
+ u2f.sendRegisterRequest = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {
|
451
|
436
|
u2f.getPortSingleton_(function(port) {
|
452
|
|
- var reqId = ++u2f.reqCounter_;
|
453
|
|
- u2f.callbackMap_[reqId] = callback;
|
454
|
|
- var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
|
455
|
|
- opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC);
|
456
|
|
- var req = port.formatRegisterRequest_(
|
457
|
|
- signRequests, registerRequests, timeoutSeconds, reqId);
|
458
|
|
- port.postMessage(req);
|
|
437
|
+ var reqId = ++u2f.reqCounter_;
|
|
438
|
+ u2f.callbackMap_[reqId] = callback;
|
|
439
|
+ var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
|
|
440
|
+ opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC);
|
|
441
|
+ var req = u2f.formatRegisterRequest_(
|
|
442
|
+ appId, registeredKeys, registerRequests, timeoutSeconds, reqId);
|
|
443
|
+ port.postMessage(req);
|
459
|
444
|
});
|
|
445
|
+ };
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+ // Dispatches a message to the extension to find out the supported JS API version.
|
|
449
|
+ // If the user is on a mobile phone and is thus using Google Authenticator instead
|
|
450
|
+ // of the Chrome extension, don't send the request and simply return 0.
|
|
451
|
+ u2f.getApiVersion = function(callback, opt_timeoutSeconds) {
|
|
452
|
+ u2f.getPortSingleton_(function(port) {
|
|
453
|
+ // If we are using Android Google Authenticator or iOS client app,
|
|
454
|
+ // do not fire an intent to ask which JS API version to use.
|
|
455
|
+ if (port.getPortType) {
|
|
456
|
+ var apiVersion;
|
|
457
|
+ switch (port.getPortType()) {
|
|
458
|
+ case 'WrappedIosPort_':
|
|
459
|
+ case 'WrappedAuthenticatorPort_':
|
|
460
|
+ apiVersion = 1.1;
|
|
461
|
+ break;
|
|
462
|
+
|
|
463
|
+ default:
|
|
464
|
+ apiVersion = 0;
|
|
465
|
+ break;
|
|
466
|
+ }
|
|
467
|
+ callback({ 'js_api_version': apiVersion });
|
|
468
|
+ return;
|
|
469
|
+ }
|
|
470
|
+ var reqId = ++u2f.reqCounter_;
|
|
471
|
+ u2f.callbackMap_[reqId] = callback;
|
|
472
|
+ var req = {
|
|
473
|
+ type: u2f.MessageTypes.U2F_GET_API_VERSION_REQUEST,
|
|
474
|
+ timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ?
|
|
475
|
+ opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC),
|
|
476
|
+ requestId: reqId
|
|
477
|
+ };
|
|
478
|
+ port.postMessage(req);
|
|
479
|
+ });
|
|
480
|
+ };
|
|
481
|
+}
|
|
482
|
+
|
|
483
|
+// Response status codes
|
|
484
|
+u2f.ErrorCodes = {
|
|
485
|
+ 'OK': 0,
|
|
486
|
+ 'OTHER_ERROR': 1,
|
|
487
|
+ 'BAD_REQUEST': 2,
|
|
488
|
+ 'CONFIGURATION_UNSUPPORTED': 3,
|
|
489
|
+ 'DEVICE_INELIGIBLE': 4,
|
|
490
|
+ 'TIMEOUT': 5
|
460
|
491
|
};
|
461
|
492
|
|
|
493
|
+
|
462
|
494
|
// Everything below is Gazelle-specific and not part of the u2f API
|
463
|
495
|
|
464
|
|
-function initU2F() {
|
|
496
|
+$(function() {
|
465
|
497
|
if (document.querySelector('#u2f_register_form') && document.querySelector('[name="u2f-request"]')) {
|
466
|
498
|
var req = JSON.parse($('[name="u2f-request"]').raw().value);
|
467
|
499
|
var sigs = JSON.parse($('[name="u2f-sigs"]').raw().value);
|
468
|
500
|
if (req) {
|
469
|
|
- u2f.register([req], sigs, function(data) {
|
|
501
|
+ u2f.register(req.appID, [req], sigs, function(data) {
|
470
|
502
|
if (data.errorCode) {
|
471
|
503
|
console.log('U2F Register Error: ' + Object.keys(u2f.ErrorCodes).find(k => u2f.ErrorCodes[k] == data.errorCode));
|
472
|
504
|
return;
|
|
@@ -479,9 +511,9 @@ function initU2F() {
|
479
|
511
|
}
|
480
|
512
|
|
481
|
513
|
if (document.querySelector('#u2f_sign_form') && document.querySelector('[name="u2f-request"]')) {
|
482
|
|
- var req = JSON.parse($('[name="u2f-request"]').raw().value);
|
|
514
|
+ var req = JSON.parse($('[name="u2f-request"]').raw().value)[0];
|
483
|
515
|
if (req) {
|
484
|
|
- u2f.sign(req, function(data) {
|
|
516
|
+ u2f.sign(req.appId, req.challenge, [{version:req.version, keyHandle:req.keyHandle}], function(data) {
|
485
|
517
|
if (data.errorCode) {
|
486
|
518
|
console.log('U2F Sign Error: ' + Object.keys(u2f.ErrorCodes).find(k => u2f.ErrorCodes[k] == data.errorCode));
|
487
|
519
|
return;
|
|
@@ -491,10 +523,4 @@ function initU2F() {
|
491
|
523
|
}, 3600)
|
492
|
524
|
}
|
493
|
525
|
}
|
494
|
|
-}
|
495
|
|
-
|
496
|
|
-if (document.readyState != 'loading') {
|
497
|
|
- initU2F();
|
498
|
|
-} else {
|
499
|
|
- document.addEventListener("DOMContentLoaded", initU2F);
|
500
|
|
-}
|
|
526
|
+})
|