diff options
Diffstat (limited to 'app/assets/javascripts/u2f')
-rw-r--r-- | app/assets/javascripts/u2f/authenticate.js | 89 | ||||
-rw-r--r-- | app/assets/javascripts/u2f/authenticate.js.coffee | 75 | ||||
-rw-r--r-- | app/assets/javascripts/u2f/error.js | 27 | ||||
-rw-r--r-- | app/assets/javascripts/u2f/error.js.coffee | 13 | ||||
-rw-r--r-- | app/assets/javascripts/u2f/register.js | 87 | ||||
-rw-r--r-- | app/assets/javascripts/u2f/register.js.coffee | 63 | ||||
-rw-r--r-- | app/assets/javascripts/u2f/util.js | 13 | ||||
-rw-r--r-- | app/assets/javascripts/u2f/util.js.coffee | 3 |
8 files changed, 216 insertions, 154 deletions
diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js new file mode 100644 index 00000000000..9ba847fb0c2 --- /dev/null +++ b/app/assets/javascripts/u2f/authenticate.js @@ -0,0 +1,89 @@ +(function() { + var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + + this.U2FAuthenticate = (function() { + function U2FAuthenticate(container, u2fParams) { + this.container = container; + this.renderNotSupported = bind(this.renderNotSupported, this); + this.renderAuthenticated = bind(this.renderAuthenticated, this); + this.renderError = bind(this.renderError, this); + this.renderInProgress = bind(this.renderInProgress, this); + this.renderSetup = bind(this.renderSetup, this); + this.renderTemplate = bind(this.renderTemplate, this); + this.authenticate = bind(this.authenticate, this); + this.start = bind(this.start, this); + this.appId = u2fParams.app_id; + this.challenge = u2fParams.challenge; + this.signRequests = u2fParams.sign_requests.map(function(request) { + return _(request).omit('challenge'); + }); + } + + U2FAuthenticate.prototype.start = function() { + if (U2FUtil.isU2FSupported()) { + return this.renderSetup(); + } else { + return this.renderNotSupported(); + } + }; + + U2FAuthenticate.prototype.authenticate = function() { + return u2f.sign(this.appId, this.challenge, this.signRequests, (function(_this) { + return function(response) { + var error; + if (response.errorCode) { + error = new U2FError(response.errorCode); + return _this.renderError(error); + } else { + return _this.renderAuthenticated(JSON.stringify(response)); + } + }; + })(this), 10); + }; + + U2FAuthenticate.prototype.templates = { + "notSupported": "#js-authenticate-u2f-not-supported", + "setup": '#js-authenticate-u2f-setup', + "inProgress": '#js-authenticate-u2f-in-progress', + "error": '#js-authenticate-u2f-error', + "authenticated": '#js-authenticate-u2f-authenticated' + }; + + U2FAuthenticate.prototype.renderTemplate = function(name, params) { + var template, templateString; + templateString = $(this.templates[name]).html(); + template = _.template(templateString); + return this.container.html(template(params)); + }; + + U2FAuthenticate.prototype.renderSetup = function() { + this.renderTemplate('setup'); + return this.container.find('#js-login-u2f-device').on('click', this.renderInProgress); + }; + + U2FAuthenticate.prototype.renderInProgress = function() { + this.renderTemplate('inProgress'); + return this.authenticate(); + }; + + U2FAuthenticate.prototype.renderError = function(error) { + this.renderTemplate('error', { + error_message: error.message() + }); + return this.container.find('#js-u2f-try-again').on('click', this.renderSetup); + }; + + U2FAuthenticate.prototype.renderAuthenticated = function(deviceResponse) { + this.renderTemplate('authenticated'); + return this.container.find("#js-device-response").val(deviceResponse); + }; + + U2FAuthenticate.prototype.renderNotSupported = function() { + return this.renderTemplate('notSupported'); + }; + + return U2FAuthenticate; + + })(); + +}).call(this); diff --git a/app/assets/javascripts/u2f/authenticate.js.coffee b/app/assets/javascripts/u2f/authenticate.js.coffee deleted file mode 100644 index 918c0a560fd..00000000000 --- a/app/assets/javascripts/u2f/authenticate.js.coffee +++ /dev/null @@ -1,75 +0,0 @@ -# Authenticate U2F (universal 2nd factor) devices for users to authenticate with. -# -# State Flow #1: setup -> in_progress -> authenticated -> POST to server -# State Flow #2: setup -> in_progress -> error -> setup - -class @U2FAuthenticate - constructor: (@container, u2fParams) -> - @appId = u2fParams.app_id - @challenge = u2fParams.challenge - - # The U2F Javascript API v1.1 requires a single challenge, with - # _no challenges per-request_. The U2F Javascript API v1.0 requires a - # challenge per-request, which is done by copying the single challenge - # into every request. - # - # In either case, we don't need the per-request challenges that the server - # has generated, so we can remove them. - # - # Note: The server library fixes this behaviour in (unreleased) version 1.0.0. - # This can be removed once we upgrade. - # https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4 - @signRequests = u2fParams.sign_requests.map (request) -> _(request).omit('challenge') - - start: () => - if U2FUtil.isU2FSupported() - @renderSetup() - else - @renderNotSupported() - - authenticate: () => - u2f.sign(@appId, @challenge, @signRequests, (response) => - if response.errorCode - error = new U2FError(response.errorCode) - @renderError(error); - else - @renderAuthenticated(JSON.stringify(response)) - , 10) - - ############# - # Rendering # - ############# - - templates: { - "notSupported": "#js-authenticate-u2f-not-supported", - "setup": '#js-authenticate-u2f-setup', - "inProgress": '#js-authenticate-u2f-in-progress', - "error": '#js-authenticate-u2f-error', - "authenticated": '#js-authenticate-u2f-authenticated' - } - - renderTemplate: (name, params) => - templateString = $(@templates[name]).html() - template = _.template(templateString) - @container.html(template(params)) - - renderSetup: () => - @renderTemplate('setup') - @container.find('#js-login-u2f-device').on('click', @renderInProgress) - - renderInProgress: () => - @renderTemplate('inProgress') - @authenticate() - - renderError: (error) => - @renderTemplate('error', {error_message: error.message()}) - @container.find('#js-u2f-try-again').on('click', @renderSetup) - - renderAuthenticated: (deviceResponse) => - @renderTemplate('authenticated') - # Prefer to do this instead of interpolating using Underscore templates - # because of JSON escaping issues. - @container.find("#js-device-response").val(deviceResponse) - - renderNotSupported: () => - @renderTemplate('notSupported') diff --git a/app/assets/javascripts/u2f/error.js b/app/assets/javascripts/u2f/error.js new file mode 100644 index 00000000000..bc48c67c4f2 --- /dev/null +++ b/app/assets/javascripts/u2f/error.js @@ -0,0 +1,27 @@ +(function() { + var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + + this.U2FError = (function() { + function U2FError(errorCode) { + this.errorCode = errorCode; + this.message = bind(this.message, this); + this.httpsDisabled = window.location.protocol !== 'https:'; + console.error("U2F Error Code: " + this.errorCode); + } + + U2FError.prototype.message = function() { + switch (false) { + case !(this.errorCode === u2f.ErrorCodes.BAD_REQUEST && this.httpsDisabled): + return "U2F only works with HTTPS-enabled websites. Contact your administrator for more details."; + case this.errorCode !== u2f.ErrorCodes.DEVICE_INELIGIBLE: + return "This device has already been registered with us."; + default: + return "There was a problem communicating with your device."; + } + }; + + return U2FError; + + })(); + +}).call(this); diff --git a/app/assets/javascripts/u2f/error.js.coffee b/app/assets/javascripts/u2f/error.js.coffee deleted file mode 100644 index 1a2fc3e757f..00000000000 --- a/app/assets/javascripts/u2f/error.js.coffee +++ /dev/null @@ -1,13 +0,0 @@ -class @U2FError - constructor: (@errorCode) -> - @httpsDisabled = (window.location.protocol isnt 'https:') - console.error("U2F Error Code: #{@errorCode}") - - message: () => - switch - when (@errorCode is u2f.ErrorCodes.BAD_REQUEST and @httpsDisabled) - "U2F only works with HTTPS-enabled websites. Contact your administrator for more details." - when @errorCode is u2f.ErrorCodes.DEVICE_INELIGIBLE - "This device has already been registered with us." - else - "There was a problem communicating with your device." diff --git a/app/assets/javascripts/u2f/register.js b/app/assets/javascripts/u2f/register.js new file mode 100644 index 00000000000..c87e0840df3 --- /dev/null +++ b/app/assets/javascripts/u2f/register.js @@ -0,0 +1,87 @@ +(function() { + var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + + this.U2FRegister = (function() { + function U2FRegister(container, u2fParams) { + this.container = container; + this.renderNotSupported = bind(this.renderNotSupported, this); + this.renderRegistered = bind(this.renderRegistered, this); + this.renderError = bind(this.renderError, this); + this.renderInProgress = bind(this.renderInProgress, this); + this.renderSetup = bind(this.renderSetup, this); + this.renderTemplate = bind(this.renderTemplate, this); + this.register = bind(this.register, this); + this.start = bind(this.start, this); + this.appId = u2fParams.app_id; + this.registerRequests = u2fParams.register_requests; + this.signRequests = u2fParams.sign_requests; + } + + U2FRegister.prototype.start = function() { + if (U2FUtil.isU2FSupported()) { + return this.renderSetup(); + } else { + return this.renderNotSupported(); + } + }; + + U2FRegister.prototype.register = function() { + return u2f.register(this.appId, this.registerRequests, this.signRequests, (function(_this) { + return function(response) { + var error; + if (response.errorCode) { + error = new U2FError(response.errorCode); + return _this.renderError(error); + } else { + return _this.renderRegistered(JSON.stringify(response)); + } + }; + })(this), 10); + }; + + U2FRegister.prototype.templates = { + "notSupported": "#js-register-u2f-not-supported", + "setup": '#js-register-u2f-setup', + "inProgress": '#js-register-u2f-in-progress', + "error": '#js-register-u2f-error', + "registered": '#js-register-u2f-registered' + }; + + U2FRegister.prototype.renderTemplate = function(name, params) { + var template, templateString; + templateString = $(this.templates[name]).html(); + template = _.template(templateString); + return this.container.html(template(params)); + }; + + U2FRegister.prototype.renderSetup = function() { + this.renderTemplate('setup'); + return this.container.find('#js-setup-u2f-device').on('click', this.renderInProgress); + }; + + U2FRegister.prototype.renderInProgress = function() { + this.renderTemplate('inProgress'); + return this.register(); + }; + + U2FRegister.prototype.renderError = function(error) { + this.renderTemplate('error', { + error_message: error.message() + }); + return this.container.find('#js-u2f-try-again').on('click', this.renderSetup); + }; + + U2FRegister.prototype.renderRegistered = function(deviceResponse) { + this.renderTemplate('registered'); + return this.container.find("#js-device-response").val(deviceResponse); + }; + + U2FRegister.prototype.renderNotSupported = function() { + return this.renderTemplate('notSupported'); + }; + + return U2FRegister; + + })(); + +}).call(this); diff --git a/app/assets/javascripts/u2f/register.js.coffee b/app/assets/javascripts/u2f/register.js.coffee deleted file mode 100644 index 74472cfa120..00000000000 --- a/app/assets/javascripts/u2f/register.js.coffee +++ /dev/null @@ -1,63 +0,0 @@ -# Register U2F (universal 2nd factor) devices for users to authenticate with. -# -# State Flow #1: setup -> in_progress -> registered -> POST to server -# State Flow #2: setup -> in_progress -> error -> setup - -class @U2FRegister - constructor: (@container, u2fParams) -> - @appId = u2fParams.app_id - @registerRequests = u2fParams.register_requests - @signRequests = u2fParams.sign_requests - - start: () => - if U2FUtil.isU2FSupported() - @renderSetup() - else - @renderNotSupported() - - register: () => - u2f.register(@appId, @registerRequests, @signRequests, (response) => - if response.errorCode - error = new U2FError(response.errorCode) - @renderError(error); - else - @renderRegistered(JSON.stringify(response)) - , 10) - - ############# - # Rendering # - ############# - - templates: { - "notSupported": "#js-register-u2f-not-supported", - "setup": '#js-register-u2f-setup', - "inProgress": '#js-register-u2f-in-progress', - "error": '#js-register-u2f-error', - "registered": '#js-register-u2f-registered' - } - - renderTemplate: (name, params) => - templateString = $(@templates[name]).html() - template = _.template(templateString) - @container.html(template(params)) - - renderSetup: () => - @renderTemplate('setup') - @container.find('#js-setup-u2f-device').on('click', @renderInProgress) - - renderInProgress: () => - @renderTemplate('inProgress') - @register() - - renderError: (error) => - @renderTemplate('error', {error_message: error.message()}) - @container.find('#js-u2f-try-again').on('click', @renderSetup) - - renderRegistered: (deviceResponse) => - @renderTemplate('registered') - # Prefer to do this instead of interpolating using Underscore templates - # because of JSON escaping issues. - @container.find("#js-device-response").val(deviceResponse) - - renderNotSupported: () => - @renderTemplate('notSupported') diff --git a/app/assets/javascripts/u2f/util.js b/app/assets/javascripts/u2f/util.js new file mode 100644 index 00000000000..907e640161a --- /dev/null +++ b/app/assets/javascripts/u2f/util.js @@ -0,0 +1,13 @@ +(function() { + this.U2FUtil = (function() { + function U2FUtil() {} + + U2FUtil.isU2FSupported = function() { + return window.u2f; + }; + + return U2FUtil; + + })(); + +}).call(this); diff --git a/app/assets/javascripts/u2f/util.js.coffee b/app/assets/javascripts/u2f/util.js.coffee deleted file mode 100644 index 5ef324f609d..00000000000 --- a/app/assets/javascripts/u2f/util.js.coffee +++ /dev/null @@ -1,3 +0,0 @@ -class @U2FUtil - @isU2FSupported: -> - window.u2f |