diff --git a/README.md b/README.md index faafbc5..102e6f9 100644 --- a/README.md +++ b/README.md @@ -58,10 +58,11 @@ Don't forget to migrate: rake db:migrate -Add the gem's JavaScript to you `application.js`: +### Default CSS - //= require devise-otp +To use the default CSS for devise-otp, just require the devise-otp.css file as usual in your application.css file (or equivalent): + *= require devise-otp ### Custom views @@ -77,9 +78,7 @@ The install generator also installs an english copy of a Devise OTP i18n file. T ### QR codes -By default, Devise OTP assumes that you use [Sprockets](https://github.com/rails/sprockets) to render assets and so will use the ([qrcode.js](/app/assets/javascripts/qrcode.js)) embeded library to render the QR code. - -If you need something more, have a look at [QR codes](/docs/QR_CODES.md) documentation file. +Devise OTP generates QR Codes directly as SVG's via the [rqrcode](https://github.com/whomwah/rqrcode), so there are no JavaScript (or Sprockets) dependencies. ## Configuration diff --git a/app/assets/javascripts/devise-otp.js b/app/assets/javascripts/devise-otp.js deleted file mode 100644 index 2c03f2d..0000000 --- a/app/assets/javascripts/devise-otp.js +++ /dev/null @@ -1 +0,0 @@ -//= require_tree . diff --git a/app/assets/javascripts/qrcode.js b/app/assets/javascripts/qrcode.js deleted file mode 100644 index 327e3b0..0000000 --- a/app/assets/javascripts/qrcode.js +++ /dev/null @@ -1,609 +0,0 @@ -/** - * @fileoverview - * - Using the 'QRCode for Javascript library' - * - Fixed dataset of 'QRCode for Javascript library' for support full-spec. - * - this library has no dependencies. - * - * @author davidshimjs - * @see http://www.d-project.com/ - * @see http://jeromeetienne.github.com/jquery-qrcode/ - */ -var QRCode; - -(function () { - //--------------------------------------------------------------------- - // QRCode for JavaScript - // - // Copyright (c) 2009 Kazuhiko Arase - // - // URL: http://www.d-project.com/ - // - // Licensed under the MIT license: - // http://www.opensource.org/licenses/mit-license.php - // - // The word "QR Code" is registered trademark of - // DENSO WAVE INCORPORATED - // http://www.denso-wave.com/qrcode/faqpatent-e.html - // - //--------------------------------------------------------------------- - function QR8bitByte(data) { - this.mode = QRMode.MODE_8BIT_BYTE; - this.data = data; - this.parsedData = []; - - // Added to support UTF-8 Characters - for (var i = 0, l = this.data.length; i < l; i++) { - var byteArray = []; - var code = this.data.charCodeAt(i); - - if (code > 0x10000) { - byteArray[0] = 0xF0 | ((code & 0x1C0000) >>> 18); - byteArray[1] = 0x80 | ((code & 0x3F000) >>> 12); - byteArray[2] = 0x80 | ((code & 0xFC0) >>> 6); - byteArray[3] = 0x80 | (code & 0x3F); - } else if (code > 0x800) { - byteArray[0] = 0xE0 | ((code & 0xF000) >>> 12); - byteArray[1] = 0x80 | ((code & 0xFC0) >>> 6); - byteArray[2] = 0x80 | (code & 0x3F); - } else if (code > 0x80) { - byteArray[0] = 0xC0 | ((code & 0x7C0) >>> 6); - byteArray[1] = 0x80 | (code & 0x3F); - } else { - byteArray[0] = code; - } - - this.parsedData.push(byteArray); - } - - this.parsedData = Array.prototype.concat.apply([], this.parsedData); - - if (this.parsedData.length != this.data.length) { - this.parsedData.unshift(191); - this.parsedData.unshift(187); - this.parsedData.unshift(239); - } - } - - QR8bitByte.prototype = { - getLength: function (buffer) { - return this.parsedData.length; - }, - write: function (buffer) { - for (var i = 0, l = this.parsedData.length; i < l; i++) { - buffer.put(this.parsedData[i], 8); - } - } - }; - - function QRCodeModel(typeNumber, errorCorrectLevel) { - this.typeNumber = typeNumber; - this.errorCorrectLevel = errorCorrectLevel; - this.modules = null; - this.moduleCount = 0; - this.dataCache = null; - this.dataList = []; - } - - QRCodeModel.prototype={addData:function(data){var newData=new QR8bitByte(data);this.dataList.push(newData);this.dataCache=null;},isDark:function(row,col){if(row<0||this.moduleCount<=row||col<0||this.moduleCount<=col){throw new Error(row+","+col);} - return this.modules[row][col];},getModuleCount:function(){return this.moduleCount;},make:function(){this.makeImpl(false,this.getBestMaskPattern());},makeImpl:function(test,maskPattern){this.moduleCount=this.typeNumber*4+17;this.modules=new Array(this.moduleCount);for(var row=0;row=7){this.setupTypeNumber(test);} - if(this.dataCache==null){this.dataCache=QRCodeModel.createData(this.typeNumber,this.errorCorrectLevel,this.dataList);} - this.mapData(this.dataCache,maskPattern);},setupPositionProbePattern:function(row,col){for(var r=-1;r<=7;r++){if(row+r<=-1||this.moduleCount<=row+r)continue;for(var c=-1;c<=7;c++){if(col+c<=-1||this.moduleCount<=col+c)continue;if((0<=r&&r<=6&&(c==0||c==6))||(0<=c&&c<=6&&(r==0||r==6))||(2<=r&&r<=4&&2<=c&&c<=4)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}},getBestMaskPattern:function(){var minLostPoint=0;var pattern=0;for(var i=0;i<8;i++){this.makeImpl(true,i);var lostPoint=QRUtil.getLostPoint(this);if(i==0||minLostPoint>lostPoint){minLostPoint=lostPoint;pattern=i;}} - return pattern;},createMovieClip:function(target_mc,instance_name,depth){var qr_mc=target_mc.createEmptyMovieClip(instance_name,depth);var cs=1;this.make();for(var row=0;row>i)&1)==1);this.modules[Math.floor(i/3)][i%3+this.moduleCount-8-3]=mod;} - for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[i%3+this.moduleCount-8-3][Math.floor(i/3)]=mod;}},setupTypeInfo:function(test,maskPattern){var data=(this.errorCorrectLevel<<3)|maskPattern;var bits=QRUtil.getBCHTypeInfo(data);for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<6){this.modules[i][8]=mod;}else if(i<8){this.modules[i+1][8]=mod;}else{this.modules[this.moduleCount-15+i][8]=mod;}} - for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<8){this.modules[8][this.moduleCount-i-1]=mod;}else if(i<9){this.modules[8][15-i-1+1]=mod;}else{this.modules[8][15-i-1]=mod;}} - this.modules[this.moduleCount-8][8]=(!test);},mapData:function(data,maskPattern){var inc=-1;var row=this.moduleCount-1;var bitIndex=7;var byteIndex=0;for(var col=this.moduleCount-1;col>0;col-=2){if(col==6)col--;while(true){for(var c=0;c<2;c++){if(this.modules[row][col-c]==null){var dark=false;if(byteIndex>>bitIndex)&1)==1);} - var mask=QRUtil.getMask(maskPattern,row,col-c);if(mask){dark=!dark;} - this.modules[row][col-c]=dark;bitIndex--;if(bitIndex==-1){byteIndex++;bitIndex=7;}}} - row+=inc;if(row<0||this.moduleCount<=row){row-=inc;inc=-inc;break;}}}}};QRCodeModel.PAD0=0xEC;QRCodeModel.PAD1=0x11;QRCodeModel.createData=function(typeNumber,errorCorrectLevel,dataList){var rsBlocks=QRRSBlock.getRSBlocks(typeNumber,errorCorrectLevel);var buffer=new QRBitBuffer();for(var i=0;itotalDataCount*8){throw new Error("code length overflow. (" - +buffer.getLengthInBits() - +">" - +totalDataCount*8 - +")");} - if(buffer.getLengthInBits()+4<=totalDataCount*8){buffer.put(0,4);} - while(buffer.getLengthInBits()%8!=0){buffer.putBit(false);} - while(true){if(buffer.getLengthInBits()>=totalDataCount*8){break;} - buffer.put(QRCodeModel.PAD0,8);if(buffer.getLengthInBits()>=totalDataCount*8){break;} - buffer.put(QRCodeModel.PAD1,8);} - return QRCodeModel.createBytes(buffer,rsBlocks);};QRCodeModel.createBytes=function(buffer,rsBlocks){var offset=0;var maxDcCount=0;var maxEcCount=0;var dcdata=new Array(rsBlocks.length);var ecdata=new Array(rsBlocks.length);for(var r=0;r=0)?modPoly.get(modIndex):0;}} - var totalCodeCount=0;for(var i=0;i=0){d^=(QRUtil.G15<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)));} - return((data<<10)|d)^QRUtil.G15_MASK;},getBCHTypeNumber:function(data){var d=data<<12;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)>=0){d^=(QRUtil.G18<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)));} - return(data<<12)|d;},getBCHDigit:function(data){var digit=0;while(data!=0){digit++;data>>>=1;} - return digit;},getPatternPosition:function(typeNumber){return QRUtil.PATTERN_POSITION_TABLE[typeNumber-1];},getMask:function(maskPattern,i,j){switch(maskPattern){case QRMaskPattern.PATTERN000:return(i+j)%2==0;case QRMaskPattern.PATTERN001:return i%2==0;case QRMaskPattern.PATTERN010:return j%3==0;case QRMaskPattern.PATTERN011:return(i+j)%3==0;case QRMaskPattern.PATTERN100:return(Math.floor(i/2)+Math.floor(j/3))%2==0;case QRMaskPattern.PATTERN101:return(i*j)%2+(i*j)%3==0;case QRMaskPattern.PATTERN110:return((i*j)%2+(i*j)%3)%2==0;case QRMaskPattern.PATTERN111:return((i*j)%3+(i+j)%2)%2==0;default:throw new Error("bad maskPattern:"+maskPattern);}},getErrorCorrectPolynomial:function(errorCorrectLength){var a=new QRPolynomial([1],0);for(var i=0;i5){lostPoint+=(3+sameCount-5);}}} - for(var row=0;row=256){n-=255;} - return QRMath.EXP_TABLE[n];},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)};for(var i=0;i<8;i++){QRMath.EXP_TABLE[i]=1<>>(7-index%8))&1)==1;},put:function(num,length){for(var i=0;i>>(length-i-1))&1)==1);}},getLengthInBits:function(){return this.length;},putBit:function(bit){var bufIndex=Math.floor(this.length/8);if(this.buffer.length<=bufIndex){this.buffer.push(0);} - if(bit){this.buffer[bufIndex]|=(0x80>>>(this.length%8));} - this.length++;}};var QRCodeLimitLength=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]]; - - function _isSupportCanvas() { - return typeof CanvasRenderingContext2D != "undefined"; - } - - // android 2.x doesn't support Data-URI spec - function _getAndroid() { - var android = false; - var sAgent = navigator.userAgent; - - if (/android/i.test(sAgent)) { // android - android = true; - aMat = sAgent.toString().match(/android ([0-9]\.[0-9])/i); - - if (aMat && aMat[1]) { - android = parseFloat(aMat[1]); - } - } - - return android; - } - - var svgDrawer = (function() { - - var Drawing = function (el, htOption) { - this._el = el; - this._htOption = htOption; - }; - - Drawing.prototype.draw = function (oQRCode) { - var _htOption = this._htOption; - var _el = this._el; - var nCount = oQRCode.getModuleCount(); - var nWidth = Math.floor(_htOption.width / nCount); - var nHeight = Math.floor(_htOption.height / nCount); - - this.clear(); - - function makeSVG(tag, attrs) { - var el = document.createElementNS('http://www.w3.org/2000/svg', tag); - for (var k in attrs) - if (attrs.hasOwnProperty(k)) el.setAttribute(k, attrs[k]); - return el; - } - - var svg = makeSVG("svg" , {'viewBox': '0 0 ' + String(nCount) + " " + String(nCount), 'width': '100%', 'height': '100%', 'fill': _htOption.colorLight}); - svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink"); - _el.appendChild(svg); - - svg.appendChild(makeSVG("rect", {"fill": _htOption.colorDark, "width": "1", "height": "1", "id": "template"})); - - for (var row = 0; row < nCount; row++) { - for (var col = 0; col < nCount; col++) { - if (oQRCode.isDark(row, col)) { - var child = makeSVG("use", {"x": String(row), "y": String(col)}); - child.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#template") - svg.appendChild(child); - } - } - } - }; - Drawing.prototype.clear = function () { - while (this._el.hasChildNodes()) - this._el.removeChild(this._el.lastChild); - }; - return Drawing; - })(); - - var useSVG = document.documentElement.tagName.toLowerCase() === "svg"; - - // Drawing in DOM by using Table tag - var Drawing = useSVG ? svgDrawer : !_isSupportCanvas() ? (function () { - var Drawing = function (el, htOption) { - this._el = el; - this._htOption = htOption; - }; - - /** - * Draw the QRCode - * - * @param {QRCode} oQRCode - */ - Drawing.prototype.draw = function (oQRCode) { - var _htOption = this._htOption; - var _el = this._el; - var nCount = oQRCode.getModuleCount(); - var nWidth = Math.floor(_htOption.width / nCount); - var nHeight = Math.floor(_htOption.height / nCount); - var aHTML = ['']; - - for (var row = 0; row < nCount; row++) { - aHTML.push(''); - - for (var col = 0; col < nCount; col++) { - aHTML.push(''); - } - - aHTML.push(''); - } - - aHTML.push('
'); - _el.innerHTML = aHTML.join(''); - - // Fix the margin values as real size. - var elTable = _el.childNodes[0]; - var nLeftMarginTable = (_htOption.width - elTable.offsetWidth) / 2; - var nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2; - - if (nLeftMarginTable > 0 && nTopMarginTable > 0) { - elTable.style.margin = nTopMarginTable + "px " + nLeftMarginTable + "px"; - } - }; - - /** - * Clear the QRCode - */ - Drawing.prototype.clear = function () { - this._el.innerHTML = ''; - }; - - return Drawing; - })() : (function () { // Drawing in Canvas - function _onMakeImage() { - this._elImage.src = this._elCanvas.toDataURL("image/png"); - this._elImage.style.display = "block"; - this._elCanvas.style.display = "none"; - } - - // Android 2.1 bug workaround - // http://code.google.com/p/android/issues/detail?id=5141 - if (this._android && this._android <= 2.1) { - var factor = 1 / window.devicePixelRatio; - var drawImage = CanvasRenderingContext2D.prototype.drawImage; - CanvasRenderingContext2D.prototype.drawImage = function (image, sx, sy, sw, sh, dx, dy, dw, dh) { - if (("nodeName" in image) && /img/i.test(image.nodeName)) { - for (var i = arguments.length - 1; i >= 1; i--) { - arguments[i] = arguments[i] * factor; - } - } else if (typeof dw == "undefined") { - arguments[1] *= factor; - arguments[2] *= factor; - arguments[3] *= factor; - arguments[4] *= factor; - } - - drawImage.apply(this, arguments); - }; - } - - /** - * Check whether the user's browser supports Data URI or not - * - * @private - * @param {Function} fSuccess Occurs if it supports Data URI - * @param {Function} fFail Occurs if it doesn't support Data URI - */ - function _safeSetDataURI(fSuccess, fFail) { - var self = this; - self._fFail = fFail; - self._fSuccess = fSuccess; - - // Check it just once - if (self._bSupportDataURI === null) { - var el = document.createElement("img"); - var fOnError = function() { - self._bSupportDataURI = false; - - if (self._fFail) { - _fFail.call(self); - } - }; - var fOnSuccess = function() { - self._bSupportDataURI = true; - - if (self._fSuccess) { - self._fSuccess.call(self); - } - }; - - el.onabort = fOnError; - el.onerror = fOnError; - el.onload = fOnSuccess; - el.src = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; // the Image contains 1px data. - return; - } else if (self._bSupportDataURI === true && self._fSuccess) { - self._fSuccess.call(self); - } else if (self._bSupportDataURI === false && self._fFail) { - self._fFail.call(self); - } - }; - - /** - * Drawing QRCode by using canvas - * - * @constructor - * @param {HTMLElement} el - * @param {Object} htOption QRCode Options - */ - var Drawing = function (el, htOption) { - this._bIsPainted = false; - this._android = _getAndroid(); - - this._htOption = htOption; - this._elCanvas = document.createElement("canvas"); - this._elCanvas.width = htOption.width; - this._elCanvas.height = htOption.height; - el.appendChild(this._elCanvas); - this._el = el; - this._oContext = this._elCanvas.getContext("2d"); - this._bIsPainted = false; - this._elImage = document.createElement("img"); - this._elImage.alt = "Scan me!"; - this._elImage.style.display = "none"; - this._el.appendChild(this._elImage); - this._bSupportDataURI = null; - }; - - /** - * Draw the QRCode - * - * @param {QRCode} oQRCode - */ - Drawing.prototype.draw = function (oQRCode) { - var _elImage = this._elImage; - var _oContext = this._oContext; - var _htOption = this._htOption; - - var nCount = oQRCode.getModuleCount(); - var nWidth = _htOption.width / nCount; - var nHeight = _htOption.height / nCount; - var nRoundedWidth = Math.round(nWidth); - var nRoundedHeight = Math.round(nHeight); - - _elImage.style.display = "none"; - this.clear(); - - for (var row = 0; row < nCount; row++) { - for (var col = 0; col < nCount; col++) { - var bIsDark = oQRCode.isDark(row, col); - var nLeft = col * nWidth; - var nTop = row * nHeight; - _oContext.strokeStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight; - _oContext.lineWidth = 1; - _oContext.fillStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight; - _oContext.fillRect(nLeft, nTop, nWidth, nHeight); - - // 안티 앨리어싱 방지 처리 - _oContext.strokeRect( - Math.floor(nLeft) + 0.5, - Math.floor(nTop) + 0.5, - nRoundedWidth, - nRoundedHeight - ); - - _oContext.strokeRect( - Math.ceil(nLeft) - 0.5, - Math.ceil(nTop) - 0.5, - nRoundedWidth, - nRoundedHeight - ); - } - } - - this._bIsPainted = true; - }; - - /** - * Make the image from Canvas if the browser supports Data URI. - */ - Drawing.prototype.makeImage = function () { - if (this._bIsPainted) { - _safeSetDataURI.call(this, _onMakeImage); - } - }; - - /** - * Return whether the QRCode is painted or not - * - * @return {Boolean} - */ - Drawing.prototype.isPainted = function () { - return this._bIsPainted; - }; - - /** - * Clear the QRCode - */ - Drawing.prototype.clear = function () { - this._oContext.clearRect(0, 0, this._elCanvas.width, this._elCanvas.height); - this._bIsPainted = false; - }; - - /** - * @private - * @param {Number} nNumber - */ - Drawing.prototype.round = function (nNumber) { - if (!nNumber) { - return nNumber; - } - - return Math.floor(nNumber * 1000) / 1000; - }; - - return Drawing; - })(); - - /** - * Get the type by string length - * - * @private - * @param {String} sText - * @param {Number} nCorrectLevel - * @return {Number} type - */ - function _getTypeNumber(sText, nCorrectLevel) { - var nType = 1; - var length = _getUTF8Length(sText); - - for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) { - var nLimit = 0; - - switch (nCorrectLevel) { - case QRErrorCorrectLevel.L : - nLimit = QRCodeLimitLength[i][0]; - break; - case QRErrorCorrectLevel.M : - nLimit = QRCodeLimitLength[i][1]; - break; - case QRErrorCorrectLevel.Q : - nLimit = QRCodeLimitLength[i][2]; - break; - case QRErrorCorrectLevel.H : - nLimit = QRCodeLimitLength[i][3]; - break; - } - - if (length <= nLimit) { - break; - } else { - nType++; - } - } - - if (nType > QRCodeLimitLength.length) { - throw new Error("Too long data"); - } - - return nType; - } - - function _getUTF8Length(sText) { - var replacedText = encodeURI(sText).toString().replace(/\%[0-9a-fA-F]{2}/g, 'a'); - return replacedText.length + (replacedText.length != sText ? 3 : 0); - } - - /** - * @class QRCode - * @constructor - * @example - * new QRCode(document.getElementById("test"), "http://jindo.dev.naver.com/collie"); - * - * @example - * var oQRCode = new QRCode("test", { - * text : "http://naver.com", - * width : 128, - * height : 128 - * }); - * - * oQRCode.clear(); // Clear the QRCode. - * oQRCode.makeCode("http://map.naver.com"); // Re-create the QRCode. - * - * @param {HTMLElement|String} el target element or 'id' attribute of element. - * @param {Object|String} vOption - * @param {String} vOption.text QRCode link data - * @param {Number} [vOption.width=256] - * @param {Number} [vOption.height=256] - * @param {String} [vOption.colorDark="#000000"] - * @param {String} [vOption.colorLight="#ffffff"] - * @param {QRCode.CorrectLevel} [vOption.correctLevel=QRCode.CorrectLevel.H] [L|M|Q|H] - */ - QRCode = function (el, vOption) { - this._htOption = { - width : 256, - height : 256, - typeNumber : 4, - colorDark : "#000000", - colorLight : "#ffffff", - correctLevel : QRErrorCorrectLevel.H - }; - - if (typeof vOption === 'string') { - vOption = { - text : vOption - }; - } - - // Overwrites options - if (vOption) { - for (var i in vOption) { - this._htOption[i] = vOption[i]; - } - } - - if (typeof el == "string") { - el = document.getElementById(el); - } - - this._android = _getAndroid(); - this._el = el; - this._oQRCode = null; - this._oDrawing = new Drawing(this._el, this._htOption); - - if (this._htOption.text) { - this.makeCode(this._htOption.text); - } - }; - - /** - * Make the QRCode - * - * @param {String} sText link data - */ - QRCode.prototype.makeCode = function (sText) { - this._oQRCode = new QRCodeModel(_getTypeNumber(sText, this._htOption.correctLevel), this._htOption.correctLevel); - this._oQRCode.addData(sText); - this._oQRCode.make(); - this._el.title = sText; - this._oDrawing.draw(this._oQRCode); - this.makeImage(); - }; - - /** - * Make the Image from Canvas element - * - It occurs automatically - * - Android below 3 doesn't support Data-URI spec. - * - * @private - */ - QRCode.prototype.makeImage = function () { - if (typeof this._oDrawing.makeImage == "function" && (!this._android || this._android >= 3)) { - this._oDrawing.makeImage(); - } - }; - - /** - * Clear the QRCode - */ - QRCode.prototype.clear = function () { - this._oDrawing.clear(); - }; - - /** - * @name QRCode.CorrectLevel - */ - QRCode.CorrectLevel = QRErrorCorrectLevel; -})(); \ No newline at end of file diff --git a/app/assets/stylesheets/devise-otp.css b/app/assets/stylesheets/devise-otp.css new file mode 100644 index 0000000..f6e1554 --- /dev/null +++ b/app/assets/stylesheets/devise-otp.css @@ -0,0 +1,4 @@ +.qrcode-container { + max-width: 300px; + margin: 0 auto; +} diff --git a/devise-otp.gemspec b/devise-otp.gemspec index 6564d5b..ee01ac5 100644 --- a/devise-otp.gemspec +++ b/devise-otp.gemspec @@ -17,4 +17,5 @@ Gem::Specification.new do |gem| gem.add_dependency "rails", ">= 7.0", "< 8.0" gem.add_dependency "devise", ">= 4.8.0", "< 5.0" gem.add_dependency "rotp", ">= 2.0.0" + gem.add_dependency "rqrcode", "~> 2.0" end diff --git a/docs/QR_CODES.md b/docs/QR_CODES.md deleted file mode 100644 index c73c880..0000000 --- a/docs/QR_CODES.md +++ /dev/null @@ -1,9 +0,0 @@ -# QR code rendering - -By default, Devise OTP assumes that you use [Sprockets](https://github.com/rails/sprockets) to render assets and so will use the ([qrcode.js](/app/assets/javascripts/qrcode.js)) embeded library to render the QR code. - -To do that, add the the following line to your `application.js` file: - - //= require devise-otp - -You can change this behavior by overriding the `otp_authenticator_token_image` method in your view helper. \ No newline at end of file diff --git a/lib/devise_otp_authenticatable/controllers/helpers.rb b/lib/devise_otp_authenticatable/controllers/helpers.rb index 0c5f90f..9b7b56a 100644 --- a/lib/devise_otp_authenticatable/controllers/helpers.rb +++ b/lib/devise_otp_authenticatable/controllers/helpers.rb @@ -1,3 +1,5 @@ +require "rqrcode" + module DeviseOtpAuthenticatable module Controllers module Helpers @@ -119,33 +121,11 @@ def otp_reset_persistence_for(resource) # returns the URL for the QR Code to initialize the Authenticator device # def otp_authenticator_token_image(resource) - otp_authenticator_token_image_js(resource.otp_provisioning_uri) - end - - private - - def otp_authenticator_token_image_js(otp_url) content_tag(:div, class: "qrcode-container") do - content_tag(:div, id: "qrcode", class: "qrcode") do - javascript_tag(%[ - new QRCode("qrcode", { - text: "#{otp_url}", - width: 256, - height: 256, - colorDark : "#000000", - colorLight : "#ffffff", - correctLevel : QRCode.CorrectLevel.H - }); - ]) - end + raw RQRCode::QRCode.new(resource.otp_provisioning_uri).as_svg(:module_size => 5, :viewbox => true, :use_path => true) end end - def otp_authenticator_token_image_google(otp_url) - otp_url = Rack::Utils.escape(otp_url) - url = "https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=#{otp_url}" - image_tag(url, alt: "OTP Url QRCode") - end end end end diff --git a/test/dummy/app/assets/javascripts/application.js b/test/dummy/app/assets/javascripts/application.js index 970666e..15ebed9 100644 --- a/test/dummy/app/assets/javascripts/application.js +++ b/test/dummy/app/assets/javascripts/application.js @@ -11,4 +11,3 @@ // GO AFTER THE REQUIRES BELOW. // //= require_tree . -//= require devise-otp diff --git a/test/dummy/app/assets/stylesheets/application.css b/test/dummy/app/assets/stylesheets/application.css index 3192ec8..e852dae 100644 --- a/test/dummy/app/assets/stylesheets/application.css +++ b/test/dummy/app/assets/stylesheets/application.css @@ -8,6 +8,7 @@ * You're free to add application-wide styles to this file and they'll appear at the top of the * compiled file, but it's generally better to create a new file per style scope. * + *= require devise-otp *= require_self *= require_tree . */