now functional
This commit is contained in:
parent
30d47d62a6
commit
e999d1e37a
122
countdown.js
122
countdown.js
|
@ -1,11 +1,113 @@
|
||||||
var countdown = $("#countdown").countdown360({
|
(function (exports) {
|
||||||
radius: 30,
|
'use strict';
|
||||||
seconds: 30,
|
|
||||||
fontColor: '#000',
|
$('body').addClass('in');
|
||||||
autostart: false,
|
|
||||||
onComplete: function() {
|
var Authenticator = exports.Authenticator;
|
||||||
console.log('done')
|
|
||||||
|
function parseQuery(search) {
|
||||||
|
|
||||||
|
var args = search.substring(1).split('&');
|
||||||
|
|
||||||
|
var argsParsed = {};
|
||||||
|
|
||||||
|
var i, arg, kvp, key, value;
|
||||||
|
|
||||||
|
for (i=0; i < args.length; i++) {
|
||||||
|
|
||||||
|
arg = args[i];
|
||||||
|
|
||||||
|
if (-1 === arg.indexOf('=')) {
|
||||||
|
|
||||||
|
argsParsed[decodeURIComponent(arg).trim()] = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
kvp = arg.split('=');
|
||||||
|
|
||||||
|
key = decodeURIComponent(kvp[0]).trim();
|
||||||
|
|
||||||
|
value = decodeURIComponent(kvp[1]).trim();
|
||||||
|
|
||||||
|
argsParsed[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return argsParsed;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
countdown.start();
|
/*
|
||||||
console.log('countdown360 ', countdown);
|
function parseQuery(search) {
|
||||||
|
var args = search.substring(1).split('&');
|
||||||
|
var argsParsed = {};
|
||||||
|
var i;
|
||||||
|
|
||||||
|
console.log('parse args', args);
|
||||||
|
for (i = 0; i < args.length; i++) {
|
||||||
|
var arg = args[i];
|
||||||
|
|
||||||
|
if (-1 === arg.indexOf('=')) {
|
||||||
|
argsParsed[decodeURIComponent(arg).trim()] = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var kvp = arg.split('=');
|
||||||
|
argsParsed[decodeURIComponent(kvp[0]).trim()] = decodeURIComponent(kvp[1]).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return argsParsed;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
var countdown = $(".js-countdown").countdown360({
|
||||||
|
radius: 30,
|
||||||
|
seconds: 30,
|
||||||
|
fontColor: '#000',
|
||||||
|
autostart: false,
|
||||||
|
onComplete: function() {
|
||||||
|
console.log('done');
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO change to token start time, regardless of the time the app began
|
||||||
|
countdown.start(new Date());
|
||||||
|
console.log('countdown360 ', countdown);
|
||||||
|
console.log(document.location.search);
|
||||||
|
|
||||||
|
var otpauth = parseQuery(document.location.search).otpuri;
|
||||||
|
var otplink = document.createElement('a');
|
||||||
|
var otp;
|
||||||
|
var meta;
|
||||||
|
var issuer;
|
||||||
|
var accountName;
|
||||||
|
|
||||||
|
otplink.href = otpauth;
|
||||||
|
otp = parseQuery(otplink.search);
|
||||||
|
|
||||||
|
meta = otplink.pathname.replace(/.*\/totp\//, '').split(':');
|
||||||
|
// TODO throw if otp.issuer !== decodeURI(meta[0])
|
||||||
|
if (meta.length > 1) {
|
||||||
|
issuer = otp.issuer || decodeURI(meta[0]);
|
||||||
|
accountName = decodeURI(meta[1]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
issuer = otp.issuer;
|
||||||
|
accountName = decodeURI(meta[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('otpuri', otpauth);
|
||||||
|
console.log('otplink', otplink);
|
||||||
|
console.log('otplink.search', otplink.search);
|
||||||
|
console.log('otp', otp);
|
||||||
|
|
||||||
|
$('.js-issuer').text(issuer);
|
||||||
|
$('.js-account-name').text(accountName);
|
||||||
|
Authenticator.generateToken(otp.secret).then(function (token) {
|
||||||
|
$('.js-token').text(token.replace(/(\d{3})/g, '$1 ').trim());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
|
}(window));
|
||||||
|
|
|
@ -55,15 +55,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
start: function () {
|
start: function (date) {
|
||||||
this.startedAt = new Date();
|
this.updateInterval = 25;
|
||||||
|
this.startedAt = date || new Date();
|
||||||
this._drawCountdownShape(Math.PI*3.5, true);
|
this._drawCountdownShape(Math.PI*3.5, true);
|
||||||
this._drawCountdownLabel(0);
|
this._drawCountdownLabel(0);
|
||||||
this.interval = setInterval(jQuery.proxy(this._draw, this), 1000);
|
this._interval = setInterval(jQuery.proxy(this._draw, this), this.updateInterval);
|
||||||
},
|
},
|
||||||
|
|
||||||
stop: function (cb) {
|
stop: function (cb) {
|
||||||
clearInterval(this.interval);
|
clearInterval(this._interval);
|
||||||
if (cb) { cb(); }
|
if (cb) { cb(); }
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -135,8 +136,14 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
_draw: function () {
|
_draw: function () {
|
||||||
var secondsElapsed = Math.round((new Date().getTime() - this.startedAt.getTime())/1000),
|
var secondsElapsed = Math.round((new Date().getTime() - this.startedAt.getTime())/1000);
|
||||||
endAngle = (Math.PI*3.5) - (((Math.PI*2)/this.settings.seconds) * secondsElapsed);
|
var milisecondsElapsed = Math.round((Date.now() - this.startedAt.getTime()));
|
||||||
|
var whole = (Math.PI*2)/(this.settings.seconds * 1000);
|
||||||
|
var parts = milisecondsElapsed;
|
||||||
|
var endAngle = (Math.PI*3.5)
|
||||||
|
- ( (whole) * parts);
|
||||||
|
//console.log('endAngle', endAngle);
|
||||||
|
|
||||||
this._clearRect();
|
this._clearRect();
|
||||||
this._drawCountdownShape(Math.PI*3.5, false);
|
this._drawCountdownShape(Math.PI*3.5, false);
|
||||||
if (secondsElapsed < this.settings.seconds) {
|
if (secondsElapsed < this.settings.seconds) {
|
||||||
|
|
63
phone.html
63
phone.html
|
@ -1,11 +1,52 @@
|
||||||
<link rel="stylesheet" type="text/css" href="style.css">
|
<!DOCTYPE html>
|
||||||
<script src="jquery-2.0.0.min.js" type="text/javascript"></script>
|
<html>
|
||||||
<script src="jquery.countdown.js" type="text/javascript"></script>
|
<head>
|
||||||
<div class="phone-screen">
|
<title>Authenticator - Daplie, Inc</title>
|
||||||
<div class="logo"></div>
|
<meta charset="UTF-8">
|
||||||
<div class="token-issuer uppercase">your token is:</div>
|
<!--meta name="viewport" content="width=device-width, user-scalable=no" /-->
|
||||||
<div class="token-phone"> 123 456 </div>
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src 'self' https://chart.googleapis.com/ data:; child-src 'self'; object-src 'none'">
|
||||||
<div id="countdown" class="countdown"></div>
|
<link rel="stylesheet" type="text/css" href="style.css">
|
||||||
<script src="countdown.js" type="text/javascript"></script>
|
</head>
|
||||||
<div class="account-name">john@example.com</div>
|
<body class="fade">
|
||||||
</div>
|
<div class="phone-screen">
|
||||||
|
<div class="logo"></div>
|
||||||
|
<div class="token-issuer uppercase">your <span class="js-issuer">Company</span> token is:</div>
|
||||||
|
<div class="token-phone"> <span class="js-token">--- ---</span> </div>
|
||||||
|
<div class="js-countdown countdown"></div>
|
||||||
|
<div class="js-account-name account-name">123@abc.xyz</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- extremely lightweight shim for hex conversion -->
|
||||||
|
<script src="bower_components/unibabel/index.js"></script>
|
||||||
|
<script src="bower_components/unibabel/unibabel.hex.js"></script>
|
||||||
|
|
||||||
|
<!-- base32 conversion (and binary string for forge) (works standalone from the above) -->
|
||||||
|
<script src="bower_components/unibabel/unibabel.base32.js"></script>
|
||||||
|
|
||||||
|
<!-- forge.hmac -->
|
||||||
|
<script src="bower_components/forge/js/util.js"></script>
|
||||||
|
<script src="bower_components/forge/js/sha1.js"></script>
|
||||||
|
<script src="bower_components/forge/js/hmac.js"></script>
|
||||||
|
|
||||||
|
<!-- forge.random.getBytes -->
|
||||||
|
<script src="bower_components/forge/js/sha256.js"></script>
|
||||||
|
<script src="bower_components/forge/js/cipher.js"></script>
|
||||||
|
<script src="bower_components/forge/js/cipherModes.js"></script>
|
||||||
|
<script src="bower_components/forge/js/aes.js"></script>
|
||||||
|
<script src="bower_components/forge/js/prng.js"></script>
|
||||||
|
<script src="bower_components/forge/js/random.js"></script>
|
||||||
|
|
||||||
|
<!-- botp.totp -->
|
||||||
|
<script src="bower_components/botp/sha1-hmac.js"></script>
|
||||||
|
<script src="bower_components/botp/index.js"></script>
|
||||||
|
|
||||||
|
<!-- Authenticator -->
|
||||||
|
<script src="authenticator.js"></script>
|
||||||
|
|
||||||
|
<script src="jquery-2.0.0.min.js" type="text/javascript"></script>
|
||||||
|
<script src="jquery.countdown.js" type="text/javascript"></script>
|
||||||
|
|
||||||
|
<!-- The Magic -->
|
||||||
|
<script src="countdown.js" type="text/javascript"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
18
style.css
18
style.css
|
@ -14,7 +14,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
margin-top: 18%;
|
margin-top: 170px;
|
||||||
padding: 0 0 0 15%;
|
padding: 0 0 0 15%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,13 +45,13 @@
|
||||||
|
|
||||||
.token-label {
|
.token-label {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 1.0556;
|
line-height: 1.0556;
|
||||||
font-weight: 200;
|
font-weight: 200;
|
||||||
letter-spacing: .023em;
|
letter-spacing: .023em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-family: "Myriad Set Pro","Helvetica Neue","Helvetica","Arial",sans-serif;
|
font-family: "Myriad Set Pro","Helvetica Neue","Helvetica","Arial",sans-serif;
|
||||||
margin-left: -100px;
|
margin-left: -100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
|
@ -82,7 +82,7 @@ input {
|
||||||
}
|
}
|
||||||
|
|
||||||
.right {
|
.right {
|
||||||
margin-top:25%;
|
margin-top:265px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
13
test.html
13
test.html
|
@ -3,7 +3,8 @@
|
||||||
<head>
|
<head>
|
||||||
<title>Authenticator - Daplie, Inc</title>
|
<title>Authenticator - Daplie, Inc</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, user-scalable=no" />
|
<!--meta name="viewport" content="width=device-width, user-scalable=no" /-->
|
||||||
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src 'self' https://chart.googleapis.com/ data:; child-src 'self'; object-src 'none'">
|
||||||
<link rel="stylesheet" type="text/css" href="bootstrap-v3.3.5.min.css">
|
<link rel="stylesheet" type="text/css" href="bootstrap-v3.3.5.min.css">
|
||||||
<link rel="stylesheet" type="text/css" href="style.css">
|
<link rel="stylesheet" type="text/css" href="style.css">
|
||||||
</head>
|
</head>
|
||||||
|
@ -25,10 +26,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-xs-6 right">
|
<div class="col-xs-6 right">
|
||||||
|
<img class="iPhone" src="iPhoneMockup.png" />
|
||||||
<div class="iframe-container">
|
<div class="iframe-container">
|
||||||
<iframe class="iframe" src="phone.html"></iframe>
|
<iframe class="js-otp-iframe iframe" src="phone.html"></iframe>
|
||||||
</div>
|
</div>
|
||||||
<img class="iPhone" src="iPhoneMockup.png" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,7 +56,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-3 control-label">Key:</label>
|
<label class="col-sm-3 control-label">Key:</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input type="text" class="js-key base-key-input wide" value="xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx">
|
<input type="text" class="js-key base-key-input wide">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -64,9 +65,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-3 control-label">URL:</label>
|
<label class="col-sm-3 control-label">URI:</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<p class="js-otpauth">otpauth://totp/company:user?secret=xxxx</p>
|
<p class="js-otpauth">otpauth://totp/company:user?secret=xxxx&issuer=company</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
36
test.js
36
test.js
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var defaultKey = 'hxdm vjec jjws rb3h wizr 4ifu gftm xboz';
|
||||||
var key;
|
var key;
|
||||||
var Authenticator = exports.Authenticator;
|
var Authenticator = exports.Authenticator;
|
||||||
var $ = function (x) {
|
var $ = function (x) {
|
||||||
|
@ -16,7 +17,19 @@ var $ = function (x) {
|
||||||
|
|
||||||
function generate(ke) {
|
function generate(ke) {
|
||||||
Authenticator.generateKey().then(function (k) {
|
Authenticator.generateKey().then(function (k) {
|
||||||
key = ke || k;
|
var $keyEl = $('.js-key');
|
||||||
|
if (ke) {
|
||||||
|
key = ke;
|
||||||
|
}
|
||||||
|
else if ($keyEl.value) {
|
||||||
|
key = $keyEl.value;
|
||||||
|
$keyEl.placeholder = key;
|
||||||
|
$keyEl.value = '';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
key = k;
|
||||||
|
$keyEl.placeholder = key;
|
||||||
|
}
|
||||||
|
|
||||||
var companyName = $('.js-company-name').value;
|
var companyName = $('.js-company-name').value;
|
||||||
var userAccount = $('.js-user-account').value;
|
var userAccount = $('.js-user-account').value;
|
||||||
|
@ -25,25 +38,13 @@ function generate(ke) {
|
||||||
+ encodeURI(companyName) + ':' + encodeURI(userAccount)
|
+ encodeURI(companyName) + ':' + encodeURI(userAccount)
|
||||||
+ '?secret=' + key.replace(/\s+/g, '').toUpperCase()
|
+ '?secret=' + key.replace(/\s+/g, '').toUpperCase()
|
||||||
;
|
;
|
||||||
/*
|
|
||||||
var otpauth = encodeURI('otpauth://totp/'
|
|
||||||
+ companyName + ':' + userAccount
|
|
||||||
+ '?secret=') + key.replace(/\s+/g, '').toUpperCase()
|
|
||||||
;
|
|
||||||
*/
|
|
||||||
// TODO figure out the right escaping
|
|
||||||
/*
|
|
||||||
var otpauth = 'otpauth://totp/'
|
|
||||||
+ companyName + ':' + userAccount
|
|
||||||
+ '?secret=' + key.replace(/\s+/g, '').toUpperCase()
|
|
||||||
;
|
|
||||||
*/
|
|
||||||
// obviously don't use this in production, but it's not so bad for the demo
|
// obviously don't use this in production, but it's not so bad for the demo
|
||||||
|
// (hmm... no one has ever said those words and regretted them... TODO XXX generate QR locally)
|
||||||
var src = 'https://chart.googleapis.com/chart?chs=166x166&chld=L|0&cht=qr&chl=' + encodeURIComponent(otpauth);
|
var src = 'https://chart.googleapis.com/chart?chs=166x166&chld=L|0&cht=qr&chl=' + encodeURIComponent(otpauth);
|
||||||
|
|
||||||
$('.js-otpauth').innerHTML = otpauth; // safe to inject because I created it
|
$('.js-otpauth').innerHTML = otpauth; // only safe to inject because I created it
|
||||||
$('.js-key').innerHTML = key; // safe to inject because I created it
|
|
||||||
$('img.js-qrcode').src = src;
|
$('img.js-qrcode').src = src;
|
||||||
|
$('.js-otp-iframe').src = 'phone.html?otpuri=' + encodeURIComponent(otpauth);
|
||||||
|
|
||||||
Authenticator.generateToken(key).then(function (token) {
|
Authenticator.generateToken(key).then(function (token) {
|
||||||
console.log('token', token);
|
console.log('token', token);
|
||||||
|
@ -84,7 +85,8 @@ $('.js-generate').addEventListener('click', function () {
|
||||||
|
|
||||||
$('.js-company-name').value = 'ACME Co';
|
$('.js-company-name').value = 'ACME Co';
|
||||||
$('.js-user-account').value = 'john@example.com';
|
$('.js-user-account').value = 'john@example.com';
|
||||||
generate('hxdm vjec jjws rb3h wizr 4ifu gftm xboz');
|
$('.js-key').placeholder = defaultKey;
|
||||||
|
generate(defaultKey);
|
||||||
|
|
||||||
}(
|
}(
|
||||||
'undefined' !== typeof window ? window : module.exports
|
'undefined' !== typeof window ? window : module.exports
|
||||||
|
|
Loading…
Reference in New Issue