').addClass(this.$element.prop("checked")?this._onstyle:this._offstyle+" off").addClass(b).addClass(this.options.style);this.$element.wrap(g),a.extend(this,{$toggle:this.$element.parent(),$toggleOn:c,$toggleOff:d,$toggleGroup:f}),this.$toggle.append(f);var h=this.options.width||Math.max(c.outerWidth(),d.outerWidth())+e.outerWidth()/2,i=this.options.height||Math.max(c.outerHeight(),d.outerHeight());c.addClass("toggle-on"),d.addClass("toggle-off"),this.$toggle.css({width:h,height:i}),this.options.height&&(c.css("line-height",c.height()+"px"),d.css("line-height",d.height()+"px")),this.update(!0),this.trigger(!0)},c.prototype.toggle=function(){this.$element.prop("checked")?this.off():this.on()},c.prototype.on=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._offstyle+" off").addClass(this._onstyle),this.$element.prop("checked",!0),void(a||this.trigger()))},c.prototype.off=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._onstyle).addClass(this._offstyle+" off"),this.$element.prop("checked",!1),void(a||this.trigger()))},c.prototype.enable=function(){this.$toggle.removeAttr("disabled"),this.$element.prop("disabled",!1)},c.prototype.disable=function(){this.$toggle.attr("disabled","disabled"),this.$element.prop("disabled",!0)},c.prototype.update=function(a){this.$element.prop("disabled")?this.disable():this.enable(),this.$element.prop("checked")?this.on(a):this.off(a)},c.prototype.trigger=function(b){this.$element.off("change.bs.toggle"),b||this.$element.change(),this.$element.on("change.bs.toggle",a.proxy(function(){this.update()},this))},c.prototype.destroy=function(){this.$element.off("change.bs.toggle"),this.$toggleGroup.remove(),this.$element.removeData("bs.toggle"),this.$element.unwrap()};var d=a.fn.bootstrapToggle;a.fn.bootstrapToggle=b,a.fn.bootstrapToggle.Constructor=c,a.fn.toggle.noConflict=function(){return a.fn.bootstrapToggle=d,this},a(function(){a("input[type=checkbox][data-toggle^=toggle]").bootstrapToggle()}),a(document).on("click.bs.toggle","div[data-toggle^=toggle]",function(b){var c=a(this).find("input[type=checkbox]");c.bootstrapToggle("toggle"),b.preventDefault()})}(jQuery);
+//# sourceMappingURL=bootstrap-toggle.min.js.map
\ No newline at end of file
diff --git a/example/static/mfa/js/cbor.js b/example/static/mfa/js/cbor.js
new file mode 100644
index 0000000..3e1f300
--- /dev/null
+++ b/example/static/mfa/js/cbor.js
@@ -0,0 +1,406 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014-2016 Patrick Gansterer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+(function(global, undefined) { "use strict";
+var POW_2_24 = 5.960464477539063e-8,
+ POW_2_32 = 4294967296,
+ POW_2_53 = 9007199254740992;
+
+function encode(value) {
+ var data = new ArrayBuffer(256);
+ var dataView = new DataView(data);
+ var lastLength;
+ var offset = 0;
+
+ function prepareWrite(length) {
+ var newByteLength = data.byteLength;
+ var requiredLength = offset + length;
+ while (newByteLength < requiredLength)
+ newByteLength <<= 1;
+ if (newByteLength !== data.byteLength) {
+ var oldDataView = dataView;
+ data = new ArrayBuffer(newByteLength);
+ dataView = new DataView(data);
+ var uint32count = (offset + 3) >> 2;
+ for (var i = 0; i < uint32count; ++i)
+ dataView.setUint32(i << 2, oldDataView.getUint32(i << 2));
+ }
+
+ lastLength = length;
+ return dataView;
+ }
+ function commitWrite() {
+ offset += lastLength;
+ }
+ function writeFloat64(value) {
+ commitWrite(prepareWrite(8).setFloat64(offset, value));
+ }
+ function writeUint8(value) {
+ commitWrite(prepareWrite(1).setUint8(offset, value));
+ }
+ function writeUint8Array(value) {
+ var dataView = prepareWrite(value.length);
+ for (var i = 0; i < value.length; ++i)
+ dataView.setUint8(offset + i, value[i]);
+ commitWrite();
+ }
+ function writeUint16(value) {
+ commitWrite(prepareWrite(2).setUint16(offset, value));
+ }
+ function writeUint32(value) {
+ commitWrite(prepareWrite(4).setUint32(offset, value));
+ }
+ function writeUint64(value) {
+ var low = value % POW_2_32;
+ var high = (value - low) / POW_2_32;
+ var dataView = prepareWrite(8);
+ dataView.setUint32(offset, high);
+ dataView.setUint32(offset + 4, low);
+ commitWrite();
+ }
+ function writeTypeAndLength(type, length) {
+ if (length < 24) {
+ writeUint8(type << 5 | length);
+ } else if (length < 0x100) {
+ writeUint8(type << 5 | 24);
+ writeUint8(length);
+ } else if (length < 0x10000) {
+ writeUint8(type << 5 | 25);
+ writeUint16(length);
+ } else if (length < 0x100000000) {
+ writeUint8(type << 5 | 26);
+ writeUint32(length);
+ } else {
+ writeUint8(type << 5 | 27);
+ writeUint64(length);
+ }
+ }
+
+ function encodeItem(value) {
+ var i;
+
+ if (value === false)
+ return writeUint8(0xf4);
+ if (value === true)
+ return writeUint8(0xf5);
+ if (value === null)
+ return writeUint8(0xf6);
+ if (value === undefined)
+ return writeUint8(0xf7);
+
+ switch (typeof value) {
+ case "number":
+ if (Math.floor(value) === value) {
+ if (0 <= value && value <= POW_2_53)
+ return writeTypeAndLength(0, value);
+ if (-POW_2_53 <= value && value < 0)
+ return writeTypeAndLength(1, -(value + 1));
+ }
+ writeUint8(0xfb);
+ return writeFloat64(value);
+
+ case "string":
+ var utf8data = [];
+ for (i = 0; i < value.length; ++i) {
+ var charCode = value.charCodeAt(i);
+ if (charCode < 0x80) {
+ utf8data.push(charCode);
+ } else if (charCode < 0x800) {
+ utf8data.push(0xc0 | charCode >> 6);
+ utf8data.push(0x80 | charCode & 0x3f);
+ } else if (charCode < 0xd800) {
+ utf8data.push(0xe0 | charCode >> 12);
+ utf8data.push(0x80 | (charCode >> 6) & 0x3f);
+ utf8data.push(0x80 | charCode & 0x3f);
+ } else {
+ charCode = (charCode & 0x3ff) << 10;
+ charCode |= value.charCodeAt(++i) & 0x3ff;
+ charCode += 0x10000;
+
+ utf8data.push(0xf0 | charCode >> 18);
+ utf8data.push(0x80 | (charCode >> 12) & 0x3f);
+ utf8data.push(0x80 | (charCode >> 6) & 0x3f);
+ utf8data.push(0x80 | charCode & 0x3f);
+ }
+ }
+
+ writeTypeAndLength(3, utf8data.length);
+ return writeUint8Array(utf8data);
+
+ default:
+ var length;
+ if (Array.isArray(value)) {
+ length = value.length;
+ writeTypeAndLength(4, length);
+ for (i = 0; i < length; ++i)
+ encodeItem(value[i]);
+ } else if (value instanceof Uint8Array) {
+ writeTypeAndLength(2, value.length);
+ writeUint8Array(value);
+ } else {
+ var keys = Object.keys(value);
+ length = keys.length;
+ writeTypeAndLength(5, length);
+ for (i = 0; i < length; ++i) {
+ var key = keys[i];
+ encodeItem(key);
+ encodeItem(value[key]);
+ }
+ }
+ }
+ }
+
+ encodeItem(value);
+
+ if ("slice" in data)
+ return data.slice(0, offset);
+
+ var ret = new ArrayBuffer(offset);
+ var retView = new DataView(ret);
+ for (var i = 0; i < offset; ++i)
+ retView.setUint8(i, dataView.getUint8(i));
+ return ret;
+}
+
+function decode(data, tagger, simpleValue) {
+ var dataView = new DataView(data);
+ var offset = 0;
+
+ if (typeof tagger !== "function")
+ tagger = function(value) { return value; };
+ if (typeof simpleValue !== "function")
+ simpleValue = function() { return undefined; };
+
+ function commitRead(length, value) {
+ offset += length;
+ return value;
+ }
+ function readArrayBuffer(length) {
+ return commitRead(length, new Uint8Array(data, offset, length));
+ }
+ function readFloat16() {
+ var tempArrayBuffer = new ArrayBuffer(4);
+ var tempDataView = new DataView(tempArrayBuffer);
+ var value = readUint16();
+
+ var sign = value & 0x8000;
+ var exponent = value & 0x7c00;
+ var fraction = value & 0x03ff;
+
+ if (exponent === 0x7c00)
+ exponent = 0xff << 10;
+ else if (exponent !== 0)
+ exponent += (127 - 15) << 10;
+ else if (fraction !== 0)
+ return (sign ? -1 : 1) * fraction * POW_2_24;
+
+ tempDataView.setUint32(0, sign << 16 | exponent << 13 | fraction << 13);
+ return tempDataView.getFloat32(0);
+ }
+ function readFloat32() {
+ return commitRead(4, dataView.getFloat32(offset));
+ }
+ function readFloat64() {
+ return commitRead(8, dataView.getFloat64(offset));
+ }
+ function readUint8() {
+ return commitRead(1, dataView.getUint8(offset));
+ }
+ function readUint16() {
+ return commitRead(2, dataView.getUint16(offset));
+ }
+ function readUint32() {
+ return commitRead(4, dataView.getUint32(offset));
+ }
+ function readUint64() {
+ return readUint32() * POW_2_32 + readUint32();
+ }
+ function readBreak() {
+ if (dataView.getUint8(offset) !== 0xff)
+ return false;
+ offset += 1;
+ return true;
+ }
+ function readLength(additionalInformation) {
+ if (additionalInformation < 24)
+ return additionalInformation;
+ if (additionalInformation === 24)
+ return readUint8();
+ if (additionalInformation === 25)
+ return readUint16();
+ if (additionalInformation === 26)
+ return readUint32();
+ if (additionalInformation === 27)
+ return readUint64();
+ if (additionalInformation === 31)
+ return -1;
+ throw "Invalid length encoding";
+ }
+ function readIndefiniteStringLength(majorType) {
+ var initialByte = readUint8();
+ if (initialByte === 0xff)
+ return -1;
+ var length = readLength(initialByte & 0x1f);
+ if (length < 0 || (initialByte >> 5) !== majorType)
+ throw "Invalid indefinite length element";
+ return length;
+ }
+
+ function appendUtf16Data(utf16data, length) {
+ for (var i = 0; i < length; ++i) {
+ var value = readUint8();
+ if (value & 0x80) {
+ if (value < 0xe0) {
+ value = (value & 0x1f) << 6
+ | (readUint8() & 0x3f);
+ length -= 1;
+ } else if (value < 0xf0) {
+ value = (value & 0x0f) << 12
+ | (readUint8() & 0x3f) << 6
+ | (readUint8() & 0x3f);
+ length -= 2;
+ } else {
+ value = (value & 0x0f) << 18
+ | (readUint8() & 0x3f) << 12
+ | (readUint8() & 0x3f) << 6
+ | (readUint8() & 0x3f);
+ length -= 3;
+ }
+ }
+
+ if (value < 0x10000) {
+ utf16data.push(value);
+ } else {
+ value -= 0x10000;
+ utf16data.push(0xd800 | (value >> 10));
+ utf16data.push(0xdc00 | (value & 0x3ff));
+ }
+ }
+ }
+
+ function decodeItem() {
+ var initialByte = readUint8();
+ var majorType = initialByte >> 5;
+ var additionalInformation = initialByte & 0x1f;
+ var i;
+ var length;
+
+ if (majorType === 7) {
+ switch (additionalInformation) {
+ case 25:
+ return readFloat16();
+ case 26:
+ return readFloat32();
+ case 27:
+ return readFloat64();
+ }
+ }
+
+ length = readLength(additionalInformation);
+ if (length < 0 && (majorType < 2 || 6 < majorType))
+ throw "Invalid length";
+
+ switch (majorType) {
+ case 0:
+ return length;
+ case 1:
+ return -1 - length;
+ case 2:
+ if (length < 0) {
+ var elements = [];
+ var fullArrayLength = 0;
+ while ((length = readIndefiniteStringLength(majorType)) >= 0) {
+ fullArrayLength += length;
+ elements.push(readArrayBuffer(length));
+ }
+ var fullArray = new Uint8Array(fullArrayLength);
+ var fullArrayOffset = 0;
+ for (i = 0; i < elements.length; ++i) {
+ fullArray.set(elements[i], fullArrayOffset);
+ fullArrayOffset += elements[i].length;
+ }
+ return fullArray;
+ }
+ return readArrayBuffer(length);
+ case 3:
+ var utf16data = [];
+ if (length < 0) {
+ while ((length = readIndefiniteStringLength(majorType)) >= 0)
+ appendUtf16Data(utf16data, length);
+ } else
+ appendUtf16Data(utf16data, length);
+ return String.fromCharCode.apply(null, utf16data);
+ case 4:
+ var retArray;
+ if (length < 0) {
+ retArray = [];
+ while (!readBreak())
+ retArray.push(decodeItem());
+ } else {
+ retArray = new Array(length);
+ for (i = 0; i < length; ++i)
+ retArray[i] = decodeItem();
+ }
+ return retArray;
+ case 5:
+ var retObject = {};
+ for (i = 0; i < length || length < 0 && !readBreak(); ++i) {
+ var key = decodeItem();
+ retObject[key] = decodeItem();
+ }
+ return retObject;
+ case 6:
+ return tagger(decodeItem(), length);
+ case 7:
+ switch (length) {
+ case 20:
+ return false;
+ case 21:
+ return true;
+ case 22:
+ return null;
+ case 23:
+ return undefined;
+ default:
+ return simpleValue(length);
+ }
+ }
+ }
+
+ var ret = decodeItem();
+ if (offset !== data.byteLength)
+ throw "Remaining bytes";
+ return ret;
+}
+
+var obj = { encode: encode, decode: decode };
+
+if (typeof define === "function" && define.amd)
+ define("cbor/cbor", obj);
+else if (typeof module !== "undefined" && module.exports)
+ module.exports = obj;
+else if (!global.CBOR)
+ global.CBOR = obj;
+
+})(this);
diff --git a/example/static/mfa/js/qrious.min.js b/example/static/mfa/js/qrious.min.js
new file mode 100644
index 0000000..5735ea6
--- /dev/null
+++ b/example/static/mfa/js/qrious.min.js
@@ -0,0 +1,6 @@
+/*! QRious v4.0.2 | (C) 2017 Alasdair Mercer | GPL v3 License
+Based on jsqrencode | (C) 2010 tz@execpc.com | GPL v3 License
+*/
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.QRious=e()}(this,function(){"use strict";function t(t,e){var n;return"function"==typeof Object.create?n=Object.create(t):(s.prototype=t,n=new s,s.prototype=null),e&&i(!0,n,e),n}function e(e,n,s,r){var o=this;return"string"!=typeof e&&(r=s,s=n,n=e,e=null),"function"!=typeof n&&(r=s,s=n,n=function(){return o.apply(this,arguments)}),i(!1,n,o,r),n.prototype=t(o.prototype,s),n.prototype.constructor=n,n.class_=e||o.class_,n.super_=o,n}function i(t,e,i){for(var n,s,a=0,h=(i=o.call(arguments,2)).length;a>1&1,n=0;n0;e--)n[e]=n[e]?n[e-1]^_.EXPONENT[v._modN(_.LOG[n[e]]+t)]:n[e-1];n[0]=_.EXPONENT[v._modN(_.LOG[n[0]]+t)]}for(t=0;t<=i;t++)n[t]=_.LOG[n[t]]},_checkBadness:function(){var t,e,i,n,s,r=0,o=this._badness,a=this.buffer,h=this.width;for(s=0;sh*h;)u-=h*h,c++;for(r+=c*v.N4,n=0;n=o-2&&(t=o-2,s>9&&t--);var a=t;if(s>9){for(r[a+2]=0,r[a+3]=0;a--;)e=r[a],r[a+3]|=255&e<<4,r[a+2]=e>>4;r[2]|=255&t<<4,r[1]=t>>4,r[0]=64|t>>12}else{for(r[a+1]=0,r[a+2]=0;a--;)e=r[a],r[a+2]|=255&e<<4,r[a+1]=e>>4;r[1]|=255&t<<4,r[0]=64|t>>4}for(a=t+3-(s<10);a=5&&(i+=v.N1+n[e]-5);for(e=3;et||3*n[e-3]>=4*n[e]||3*n[e+3]>=4*n[e])&&(i+=v.N3);return i},_finish:function(){this._stringBuffer=this.buffer.slice();var t,e,i=0,n=3e4;for(e=0;e<8&&(this._applyMask(e),(t=this._checkBadness())>=1)1&n&&(s[r-1-e+8*r]=1,e<6?s[8+r*e]=1:s[8+r*(e+1)]=1);for(e=0;e<7;e++,n>>=1)1&n&&(s[8+r*(r-7+e)]=1,e?s[6-e+8*r]=1:s[7+8*r]=1)},_interleaveBlocks:function(){var t,e,i=this._dataBlock,n=this._ecc,s=this._eccBlock,r=0,o=this._calculateMaxLength(),a=this._neccBlock1,h=this._neccBlock2,f=this._stringBuffer;for(t=0;t1)for(t=u.BLOCK[n],i=s-7;;){for(e=s-7;e>t-3&&(this._addAlignment(e,i),!(e6)for(t=d.BLOCK[r-7],e=17,i=0;i<6;i++)for(n=0;n<3;n++,e--)1&(e>11?r>>e-12:t>>e)?(s[5-i+o*(2-n+o-11)]=1,s[2-n+o-11+o*(5-i)]=1):(this._setMask(5-i,2-n+o-11),this._setMask(2-n+o-11,5-i))},_isMasked:function(t,e){var i=v._getMaskBit(t,e);return 1===this._mask[i]},_pack:function(){var t,e,i,n=1,s=1,r=this.width,o=r-1,a=r-1,h=(this._dataBlock+this._eccBlock)*(this._neccBlock1+this._neccBlock2)+this._neccBlock2;for(e=0;ee&&(i=t,t=e,e=i),i=e,i+=e*e,i>>=1,i+=t},_modN:function(t){for(;t>=255;)t=((t-=255)>>8)+(255&t);return t},N1:3,N2:3,N3:40,N4:10}),p=v,m=f.extend({draw:function(){this.element.src=this.qrious.toDataURL()},reset:function(){this.element.src=""},resize:function(){var t=this.element;t.width=t.height=this.qrious.size}}),g=h.extend(function(t,e,i,n){this.name=t,this.modifiable=Boolean(e),this.defaultValue=i,this._valueTransformer=n},{transform:function(t){var e=this._valueTransformer;return"function"==typeof e?e(t,this):t}}),k=h.extend(null,{abs:function(t){return null!=t?Math.abs(t):null},hasOwn:function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},noop:function(){},toUpperCase:function(t){return null!=t?t.toUpperCase():null}}),w=h.extend(function(t){this.options={},t.forEach(function(t){this.options[t.name]=t},this)},{exists:function(t){return null!=this.options[t]},get:function(t,e){return w._get(this.options[t],e)},getAll:function(t){var e,i=this.options,n={};for(e in i)k.hasOwn(i,e)&&(n[e]=w._get(i[e],t));return n},init:function(t,e,i){"function"!=typeof i&&(i=k.noop);var n,s;for(n in this.options)k.hasOwn(this.options,n)&&(s=this.options[n],w._set(s,s.defaultValue,e),w._createAccessor(s,e,i));this._setAll(t,e,!0)},set:function(t,e,i){return this._set(t,e,i)},setAll:function(t,e){return this._setAll(t,e)},_set:function(t,e,i,n){var s=this.options[t];if(!s)throw new Error("Invalid option: "+t);if(!s.modifiable&&!n)throw new Error("Option cannot be modified: "+t);return w._set(s,e,i)},_setAll:function(t,e,i){if(!t)return!1;var n,s=!1;for(n in t)k.hasOwn(t,n)&&this._set(n,t[n],e,i)&&(s=!0);return s}},{_createAccessor:function(t,e,i){var n={get:function(){return w._get(t,e)}};t.modifiable&&(n.set=function(n){w._set(t,n,e)&&i(n,t)}),Object.defineProperty(e,t.name,n)},_get:function(t,e){return e["_"+t.name]},_set:function(t,e,i){var n="_"+t.name,s=i[n],r=t.transform(null!=e?e:t.defaultValue);return i[n]=r,r!==s}}),M=w,b=h.extend(function(){this._services={}},{getService:function(t){var e=this._services[t];if(!e)throw new Error("Service is not being managed with name: "+t);return e},setService:function(t,e){if(this._services[t])throw new Error("Service is already managed with name: "+t);e&&(this._services[t]=e)}}),B=new M([new g("background",!0,"white"),new g("backgroundAlpha",!0,1,k.abs),new g("element"),new g("foreground",!0,"black"),new g("foregroundAlpha",!0,1,k.abs),new g("level",!0,"L",k.toUpperCase),new g("mime",!0,"image/png"),new g("padding",!0,null,k.abs),new g("size",!0,100,k.abs),new g("value",!0,"")]),y=new b,O=h.extend(function(t){B.init(t,this,this.update.bind(this));var e=B.get("element",this),i=y.getService("element"),n=e&&i.isCanvas(e)?e:i.createCanvas(),s=e&&i.isImage(e)?e:i.createImage();this._canvasRenderer=new c(this,n,!0),this._imageRenderer=new m(this,s,s===e),this.update()},{get:function(){return B.getAll(this)},set:function(t){B.setAll(t,this)&&this.update()},toDataURL:function(t){return this.canvas.toDataURL(t||this.mime)},update:function(){var t=new p({level:this.level,value:this.value});this._canvasRenderer.render(t),this._imageRenderer.render(t)}},{use:function(t){y.setService(t.getName(),t)}});Object.defineProperties(O.prototype,{canvas:{get:function(){return this._canvasRenderer.getElement()}},image:{get:function(){return this._imageRenderer.getElement()}}});var A=O,L=h.extend({getName:function(){}}).extend({createCanvas:function(){},createImage:function(){},getName:function(){return"element"},isCanvas:function(t){},isImage:function(t){}}).extend({createCanvas:function(){return document.createElement("canvas")},createImage:function(){return document.createElement("img")},isCanvas:function(t){return t instanceof HTMLCanvasElement},isImage:function(t){return t instanceof HTMLImageElement}});return A.use(new L),A});
+
+//# sourceMappingURL=qrious.min.js.map
\ No newline at end of file
diff --git a/example/static/mfa/js/u2f-api.js b/example/static/mfa/js/u2f-api.js
new file mode 100644
index 0000000..b11c83c
--- /dev/null
+++ b/example/static/mfa/js/u2f-api.js
@@ -0,0 +1,751 @@
+//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';
+
+
+/**
+ * Namespace for the U2F api.
+ * @type {Object}
+ */
+var u2f = u2f || {};
+
+/**
+ * FIDO U2F Javascript API Version
+ * @number
+ */
+var js_api_version;
+
+/**
+ * The U2F extension id
+ * @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';
+
+
+/**
+ * Message types for messsages to/from the extension
+ * @const
+ * @enum {string}
+ */
+u2f.MessageTypes = {
+ 'U2F_REGISTER_REQUEST': 'u2f_register_request',
+ 'U2F_REGISTER_RESPONSE': 'u2f_register_response',
+ 'U2F_SIGN_REQUEST': 'u2f_sign_request',
+ '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'
+};
+
+
+/**
+ * Response status codes
+ * @const
+ * @enum {number}
+ */
+u2f.ErrorCodes = {
+ 'OK': 0,
+ 'OTHER_ERROR': 1,
+ 'BAD_REQUEST': 2,
+ 'CONFIGURATION_UNSUPPORTED': 3,
+ 'DEVICE_INELIGIBLE': 4,
+ 'TIMEOUT': 5
+};
+
+
+/**
+ * A message for registration requests
+ * @typedef {{
+ * type: u2f.MessageTypes,
+ * appId: ?string,
+ * timeoutSeconds: ?number,
+ * requestId: ?number
+ * }}
+ */
+u2f.U2fRequest;
+
+
+/**
+ * A message for registration responses
+ * @typedef {{
+ * type: u2f.MessageTypes,
+ * responseData: (u2f.Error | u2f.RegisterResponse | u2f.SignResponse),
+ * requestId: ?number
+ * }}
+ */
+u2f.U2fResponse;
+
+
+/**
+ * An error object for responses
+ * @typedef {{
+ * errorCode: u2f.ErrorCodes,
+ * errorMessage: ?string
+ * }}
+ */
+u2f.Error;
+
+/**
+ * Data object for a single sign request.
+ * @typedef {enum {BLUETOOTH_RADIO, BLUETOOTH_LOW_ENERGY, USB, NFC}}
+ */
+u2f.Transport;
+
+
+/**
+ * Data object for a single sign request.
+ * @typedef {Array}
+ */
+u2f.Transports;
+
+/**
+ * Data object for a single sign request.
+ * @typedef {{
+ * version: string,
+ * challenge: string,
+ * keyHandle: string,
+ * appId: string
+ * }}
+ */
+u2f.SignRequest;
+
+
+/**
+ * Data object for a sign response.
+ * @typedef {{
+ * keyHandle: string,
+ * signatureData: string,
+ * clientData: string
+ * }}
+ */
+u2f.SignResponse;
+
+
+/**
+ * Data object for a registration request.
+ * @typedef {{
+ * version: string,
+ * challenge: string
+ * }}
+ */
+u2f.RegisterRequest;
+
+
+/**
+ * Data object for a registration response.
+ * @typedef {{
+ * version: string,
+ * keyHandle: string,
+ * transports: Transports,
+ * appId: string
+ * }}
+ */
+u2f.RegisterResponse;
+
+
+/**
+ * Data object for a registered key.
+ * @typedef {{
+ * version: string,
+ * keyHandle: string,
+ * transports: ?Transports,
+ * appId: ?string
+ * }}
+ */
+u2f.RegisteredKey;
+
+
+/**
+ * Data object for a get API register response.
+ * @typedef {{
+ * js_api_version: number
+ * }}
+ */
+u2f.GetJsApiVersionResponse;
+
+
+//Low level MessagePort API support
+
+/**
+ * Sets up a MessagePort to the U2F extension using the
+ * available mechanisms.
+ * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback
+ */
+u2f.getMessagePort = function(callback) {
+ if (typeof chrome != 'undefined' && chrome.runtime) {
+ // The actual message here does not matter, but we need to get a reply
+ // for the callback to run. Thus, send an empty signature request
+ // in order to get a failure response.
+ var msg = {
+ type: u2f.MessageTypes.U2F_SIGN_REQUEST,
+ signRequests: []
+ };
+ chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function() {
+ if (!chrome.runtime.lastError) {
+ // We are on a whitelisted origin and can talk directly
+ // with the extension.
+ u2f.getChromeRuntimePort_(callback);
+ } else {
+ // chrome.runtime was available, but we couldn't message
+ // the extension directly, use iframe
+ u2f.getIframePort_(callback);
+ }
+ });
+ } else if (u2f.isAndroidChrome_()) {
+ u2f.getAuthenticatorPort_(callback);
+ } else if (u2f.isIosChrome_()) {
+ u2f.getIosPort_(callback);
+ } else {
+ // chrome.runtime was not available at all, which is normal
+ // when this origin doesn't have access to any extensions.
+ u2f.getIframePort_(callback);
+ }
+};
+
+/**
+ * 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) {
+ var port = chrome.runtime.connect(u2f.EXTENSION_ID,
+ {'includeTlsChannelId': true});
+ setTimeout(function() {
+ callback(new u2f.WrappedChromeRuntimePort_(port));
+ }, 0);
+};
+
+/**
+ * Return a 'port' abstraction to the Authenticator app.
+ * @param {function(u2f.WrappedAuthenticatorPort_)} callback
+ * @private
+ */
+u2f.getAuthenticatorPort_ = function(callback) {
+ setTimeout(function() {
+ callback(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(new u2f.WrappedIosPort_());
+ }, 0);
+};
+
+/**
+ * A wrapper for chrome.runtime.Port that is compatible with MessagePort.
+ * @param {Port} port
+ * @constructor
+ * @private
+ */
+u2f.WrappedChromeRuntimePort_ = function(port) {
+ this.port_ = port;
+};
+
+/**
+ * Format and return a sign request compliant with the JS API version supported by the extension.
+ * @param {Array} 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} signRequests
+ * @param {Array} 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) {
+ this.port_.postMessage(message);
+};
+
+
+/**
+ * Emulates the HTML 5 addEventListener interface. Works only for the
+ * onmessage event, which is hooked up to the chrome.runtime.Port.onMessage.
+ * @param {string} eventName
+ * @param {function({data: Object})} handler
+ */
+u2f.WrappedChromeRuntimePort_.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});
+ });
+ } 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') {
+ 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('WrappedAuthenticatorPort only supports message');
+ }
+};
+
+/**
+ * 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.
+ * @param {function(MessagePort)} callback
+ * @private
+ */
+u2f.getIframePort_ = function(callback) {
+ // Create the iframe
+ var iframeOrigin = 'chrome-extension://' + u2f.EXTENSION_ID;
+ var iframe = document.createElement('iframe');
+ iframe.src = iframeOrigin + '/u2f-comms.html';
+ iframe.setAttribute('style', 'display:none');
+ document.body.appendChild(iframe);
+
+ var channel = new MessageChannel();
+ var ready = function(message) {
+ if (message.data == 'ready') {
+ channel.port1.removeEventListener('message', ready);
+ callback(channel.port1);
+ } else {
+ console.error('First event on iframe port was not "ready"');
+ }
+ };
+ channel.port1.addEventListener('message', ready);
+ channel.port1.start();
+
+ iframe.addEventListener('load', function() {
+ // Deliver the port to the iframe and initialize
+ iframe.contentWindow.postMessage('init', iframeOrigin, [channel.port2]);
+ });
+};
+
+
+//High-level JS API
+
+/**
+ * Default extension response timeout in seconds.
+ * @const
+ */
+u2f.EXTENSION_TIMEOUT_SEC = 30;
+
+/**
+ * A singleton instance for a MessagePort to the extension.
+ * @type {MessagePort|u2f.WrappedChromeRuntimePort_}
+ * @private
+ */
+u2f.port_ = null;
+
+/**
+ * Callbacks waiting for a port
+ * @type {Array}
+ * @private
+ */
+u2f.waitingForPort_ = [];
+
+/**
+ * A counter for requestIds.
+ * @type {number}
+ * @private
+ */
+u2f.reqCounter_ = 0;
+
+/**
+ * A map from requestIds to client callbacks
+ * @type {Object.}
+ * @private
+ */
+u2f.callbackMap_ = {};
+
+/**
+ * Creates or retrieves the MessagePort singleton to use.
+ * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback
+ * @private
+ */
+u2f.getPortSingleton_ = function(callback) {
+ if (u2f.port_) {
+ callback(u2f.port_);
+ } else {
+ if (u2f.waitingForPort_.length == 0) {
+ u2f.getMessagePort(function(port) {
+ u2f.port_ = port;
+ u2f.port_.addEventListener('message',
+ /** @type {function(Event)} */ (u2f.responseHandler_));
+
+ // Careful, here be async callbacks. Maybe.
+ while (u2f.waitingForPort_.length)
+ u2f.waitingForPort_.shift()(u2f.port_);
+ });
+ }
+ u2f.waitingForPort_.push(callback);
+ }
+};
+
+/**
+ * Handles response messages from the extension.
+ * @param {MessageEvent.} message
+ * @private
+ */
+u2f.responseHandler_ = function(message) {
+ var response = message.data;
+ var reqId = response['requestId'];
+ if (!reqId || !u2f.callbackMap_[reqId]) {
+ console.error('Unknown or missing requestId in response.');
+ return;
+ }
+ var cb = u2f.callbackMap_[reqId];
+ delete u2f.callbackMap_[reqId];
+ cb(response['responseData']);
+};
+
+/**
+ * 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 {string=} appId
+ * @param {string=} challenge
+ * @param {Array} registeredKeys
+ * @param {function((u2f.Error|u2f.SignResponse))} callback
+ * @param {number=} opt_timeoutSeconds
+ */
+u2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
+ 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, registeredKeys, callback, opt_timeoutSeconds);
+ });
+ } else {
+ // We know the JS API version. Send the actual sign request in the supported API version.
+ u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds);
+ }
+};
+
+/**
+ * Dispatches an array of sign requests to available U2F tokens.
+ * @param {string=} appId
+ * @param {string=} challenge
+ * @param {Array} registeredKeys
+ * @param {function((u2f.Error|u2f.SignResponse))} callback
+ * @param {number=} opt_timeoutSeconds
+ */
+u2f.sendSignRequest = function(appId, challenge, 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.formatSignRequest_(appId, challenge, registeredKeys, timeoutSeconds, reqId);
+ port.postMessage(req);
+ });
+};
+
+/**
+ * Dispatches register requests to available U2F tokens. An array of sign
+ * requests identifies already registered 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 register request.
+ * @param {string=} appId
+ * @param {Array} registerRequests
+ * @param {Array} registeredKeys
+ * @param {function((u2f.Error|u2f.RegisterResponse))} callback
+ * @param {number=} opt_timeoutSeconds
+ */
+u2f.register = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {
+ console.log("appid",appId)
+ console.log("registerRequests",registerRequests)
+ console.log("registeredKeys",registeredKeys)
+ 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} registerRequests
+ * @param {Array} 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(port) {
+ // 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);
+ });
+};
diff --git a/mfa/Email.py b/mfa/Email.py
index b1bc931..bc638fa 100644
--- a/mfa/Email.py
+++ b/mfa/Email.py
@@ -13,7 +13,7 @@ def sendEmail(request,username,secret):
kwargs = {key: username}
user = User.objects.get(**kwargs)
res=render(request,"mfa_email_token_template.html",{"request":request,"user":user,'otp':secret})
- return send([user.email],"OTP", res.content)
+ return send([user.email],"OTP", str(res.content))
def start(request):
context = csrf(request)
@@ -25,7 +25,10 @@ def start(request):
uk.enabled=1
uk.save()
from django.http import HttpResponseRedirect
- from django.core.urlresolvers import reverse
+ try:
+ from django.core.urlresolvers import reverse
+ except:
+ from django.urls import reverse
return HttpResponseRedirect(reverse('mfa_home'))
context["invalid"] = True
else:
diff --git a/mfa/totp.py b/mfa/totp.py
index b98fc61..826b6a0 100644
--- a/mfa/totp.py
+++ b/mfa/totp.py
@@ -1,5 +1,5 @@
from django.shortcuts import render
-#from django.http import HttpResponse
+from django.http import HttpResponse
from .models import *
from django.template.context_processors import csrf
import simplejson
diff --git a/mfa/urls.py b/mfa/urls.py
index 6a8e827..37a8dcf 100644
--- a/mfa/urls.py
+++ b/mfa/urls.py
@@ -1,49 +1,53 @@
-from django.conf.urls import url
-
from . import views,totp,U2F,TrustedDevice,helpers,FIDO2,Email
-app_name='mfa'
+#app_name='mfa'
+
+try:
+ from django.urls import re_path as url
+except:
+ from django.conf.urls import url
urlpatterns = [
-url(r'totp/start/', totp.start , name="start_new_otop"),
-url(r'totp/getToken', totp.getToken , name="get_new_otop"),
-url(r'totp/verify', totp.verify, name="verify_otop"),
-url(r'totp/auth', totp.auth, name="totp_auth"),
-url(r'totp/recheck', totp.recheck, name="totp_recheck"),
+ url(r'totp/start/', totp.start , name="start_new_otop"),
+ url(r'totp/getToken', totp.getToken , name="get_new_otop"),
+ url(r'totp/verify', totp.verify, name="verify_otop"),
+ url(r'totp/auth', totp.auth, name="totp_auth"),
+ url(r'totp/recheck', totp.recheck, name="totp_recheck"),
-url(r'email/start/', Email.start , name="start_email"),
-url(r'email/auth/', Email.auth , name="email_auth"),
+ url(r'email/start/', Email.start , name="start_email"),
+ url(r'email/auth/', Email.auth , name="email_auth"),
-url(r'u2f/$', U2F.start, name="start_u2f"),
-url(r'u2f/bind', U2F.bind, name="bind_u2f"),
-url(r'u2f/auth', U2F.auth, name="u2f_auth"),
-url(r'u2f/process_recheck', U2F.process_recheck, name="u2f_recheck"),
-url(r'u2f/verify', U2F.verify, name="u2f_verify"),
+ url(r'u2f/$', U2F.start, name="start_u2f"),
+ url(r'u2f/bind', U2F.bind, name="bind_u2f"),
+ url(r'u2f/auth', U2F.auth, name="u2f_auth"),
+ url(r'u2f/process_recheck', U2F.process_recheck, name="u2f_recheck"),
+ url(r'u2f/verify', U2F.verify, name="u2f_verify"),
-url(r'fido2/$', FIDO2.start, name="start_fido2"),
-url(r'fido2/auth', FIDO2.auth, name="fido2_auth"),
-url(r'fido2/begin_auth', FIDO2.authenticate_begin, name="fido2_begin_auth"),
-url(r'fido2/complete_auth', FIDO2.authenticate_complete, name="fido2_complete_auth"),
-url(r'fido2/begin_reg', FIDO2.begin_registeration, name="fido2_begin_reg"),
-url(r'fido2/complete_reg', FIDO2.complete_reg, name="fido2_complete_reg"),
-url(r'u2f/bind', U2F.bind, name="bind_u2f"),
-url(r'u2f/auth', U2F.auth, name="u2f_auth"),
-url(r'u2f/process_recheck', U2F.process_recheck, name="u2f_recheck"),
-url(r'u2f/verify', U2F.verify, name="u2f_verify"),
+ url(r'fido2/$', FIDO2.start, name="start_fido2"),
+ url(r'fido2/auth', FIDO2.auth, name="fido2_auth"),
+ url(r'fido2/begin_auth', FIDO2.authenticate_begin, name="fido2_begin_auth"),
+ url(r'fido2/complete_auth', FIDO2.authenticate_complete, name="fido2_complete_auth"),
+ url(r'fido2/begin_reg', FIDO2.begin_registeration, name="fido2_begin_reg"),
+ url(r'fido2/complete_reg', FIDO2.complete_reg, name="fido2_complete_reg"),
+ url(r'u2f/bind', U2F.bind, name="bind_u2f"),
+ url(r'u2f/auth', U2F.auth, name="u2f_auth"),
+ url(r'u2f/process_recheck', U2F.process_recheck, name="u2f_recheck"),
+ url(r'u2f/verify', U2F.verify, name="u2f_verify"),
-url(r'td/$', TrustedDevice.start, name="start_td"),
-url(r'td/add', TrustedDevice.add, name="add_td"),
-url(r'td/send_link', TrustedDevice.send_email, name="td_sendemail"),
-url(r'td/get-ua', TrustedDevice.getUserAgent, name="td_get_useragent"),
-url(r'td/trust', TrustedDevice.trust_device, name="td_trust_device"),
-url(r'u2f/checkTrusted', TrustedDevice.checkTrusted, name="td_checkTrusted"),
-url(r'u2f/secure_device', TrustedDevice.getCookie, name="td_securedevice"),
+ url(r'td/$', TrustedDevice.start, name="start_td"),
+ url(r'td/add', TrustedDevice.add, name="add_td"),
+ url(r'td/send_link', TrustedDevice.send_email, name="td_sendemail"),
+ url(r'td/get-ua', TrustedDevice.getUserAgent, name="td_get_useragent"),
+ url(r'td/trust', TrustedDevice.trust_device, name="td_trust_device"),
+ url(r'u2f/checkTrusted', TrustedDevice.checkTrusted, name="td_checkTrusted"),
+ url(r'u2f/secure_device', TrustedDevice.getCookie, name="td_securedevice"),
-url(r'^$', views.index, name="mfa_home"),
-url(r'goto/(.*)', views.goto, name="mfa_goto"),
-url(r'selct_method', views.show_methods, name="mfa_methods_list"),
-url(r'recheck', helpers.recheck, name="mfa_recheck"),
-url(r'toggleKey', views.toggleKey, name="toggle_key"),
-url(r'delete', views.delKey, name="mfa_delKey"),
-url(r'reset', views.reset_cookie, name="mfa_reset_cookie"),
+ url(r'^$', views.index, name="mfa_home"),
+ url(r'goto/(.*)', views.goto, name="mfa_goto"),
+ url(r'selct_method', views.show_methods, name="mfa_methods_list"),
+ url(r'recheck', helpers.recheck, name="mfa_recheck"),
+ url(r'toggleKey', views.toggleKey, name="toggle_key"),
+ url(r'delete', views.delKey, name="mfa_delKey"),
+ url(r'reset', views.reset_cookie, name="mfa_reset_cookie"),
- ]
\ No newline at end of file
+ ]
+# print(urlpatterns)
\ No newline at end of file
diff --git a/mfa/views.py b/mfa/views.py
index 4788a2b..2f63c8f 100644
--- a/mfa/views.py
+++ b/mfa/views.py
@@ -7,6 +7,7 @@ except:
from django.core.urlresolvers import reverse
from django.template.context_processors import csrf
from django.template.context import RequestContext
+from django.http import HttpResponseRedirect
from django.conf import settings
from . import TrustedDevice
from user_agents import parse
@@ -42,7 +43,7 @@ def show_methods(request):
return render(request,"select_mfa_method.html", {})
def reset_cookie(request):
- response=HttpResponseRedirect(settings.BASE_URL)
+ response=HttpResponseRedirect(settings.LOGIN_URL)
response.delete_cookie("base_username")
return response
def login(request):
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..3322acb
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,11 @@
+django >= 1.7
+ jsonfield
+ simplejson
+ pyotp
+ python-u2flib-server
+ ua-parser
+ user-agents
+ python-jose
+ fido2 == 0.7
+ jsonLookup
+