1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 15:53:27 +00:00

totp code management and countdown timer

This commit is contained in:
Kyle Spearrin
2017-07-11 14:05:04 -04:00
parent fccaa5f8de
commit 21feb653cb
11 changed files with 245 additions and 7 deletions

View File

@@ -45,4 +45,7 @@
})
.factory('lockService', function () {
return chrome.extension.getBackgroundPage().bg_lockService;
})
.factory('totpService', function () {
return chrome.extension.getBackgroundPage().bg_totpService;
});

View File

@@ -2,9 +2,10 @@ angular
.module('bit.vault')
.controller('vaultViewLoginController', function ($scope, $state, $stateParams, loginService, toastr, $q,
$analytics, i18nService, utilsService) {
$analytics, i18nService, utilsService, totpService, $timeout) {
$scope.i18n = i18nService;
var from = $stateParams.from;
var from = $stateParams.from,
totpInterval = null;
$scope.login = null;
loginService.get($stateParams.loginId, function (login) {
@@ -37,6 +38,19 @@ angular
else {
$scope.login.showLaunch = false;
}
if (model.totp) {
totpUpdateCode();
totpTick();
if (totpInterval) {
clearInterval(totpInterval);
}
totpInterval = setInterval(function () {
totpTick();
}, 1000);
}
});
});
@@ -89,4 +103,46 @@ angular
$analytics.eventTrack('Toggled Password');
$scope.showPassword = !$scope.showPassword;
};
$scope.$on("$destroy", function () {
if (totpInterval) {
clearInterval(totpInterval);
}
});
function totpUpdateCode() {
if (!$scope.login.totp) {
return;
}
totpService.getCode($scope.login.totp).then(function (code) {
$timeout(function () {
if (code) {
$scope.totpCodeFormatted = code.substring(0, 3) + ' ' + code.substring(3);
$scope.totpCode = code;
}
else {
$scope.totpCode = $scope.totpCodeFormatted = null;
if (totpInterval) {
clearInterval(totpInterval);
}
}
});
});
};
function totpTick() {
$timeout(function () {
var epoch = Math.round(new Date().getTime() / 1000.0);
var mod = (epoch % 30);
var sec = 30 - mod;
$scope.totpSec = sec;
$scope.totpDash = (2.62 * mod).toFixed(2);
$scope.totpLow = sec <= 7;
if (epoch % 30 == 0) {
totpUpdateCode();
}
});
};
});

View File

@@ -36,6 +36,10 @@
{{i18n.generatePassword}}
<i class="fa fa-chevron-right"></i>
</a>
<div class="list-section-item">
<label for="totp" class="item-label">Authenticator Key (TOTP)</label>
<input id="totp" type="text" name="Totp" ng-model="login.totp">
</div>
</div>
</div>
<div class="list-section">

View File

@@ -37,6 +37,10 @@
{{i18n.generatePassword}}
<i class="fa fa-chevron-right"></i>
</a>
<div class="list-section-item">
<label for="totp" class="item-label">Authenticator Key (TOTP)</label>
<input id="totp" type="text" name="Totp" ng-model="login.totp">
</div>
</div>
</div>
<div class="list-section">

View File

@@ -47,6 +47,24 @@
<span ng-show="!showPassword">{{login.maskedPassword}}</span>
<span id="password" ng-show="showPassword" class="monospaced">{{login.password}}</span>
</div>
<div class="list-section-item totp" ng-class="{'low': totpLow}" ng-if="login.totp && totpCode">
<a class="btn-list" href="" title="Copy TOTP" ngclipboard ngclipboard-error="clipboardError(e)"
ngclipboard-success="clipboardSuccess(e, 'Totp')" data-clipboard-text="{{totpCode}}">
<i class="fa fa-lg fa-clipboard"></i>
</a>
<span class="totp-countdown">
<span class="totp-sec">{{totpSec}}</span>
<svg>
<g>
<circle class="totp-circle inner" r="12.6" cy="16" cx="16"
style="stroke-dashoffset: {{totpDash}}px;"></circle>
<circle class="totp-circle outer" r="14" cy="16" cx="16"></circle>
</g>
</svg>
</span>
<span class="item-label">Verification Code (TOTP)</span>
<span id="totp" class="totp-code" ng-class="{'text-danger': totpLow}">{{totpCodeFormatted}}</span>
</div>
</div>
</div>
<div class="list-section" ng-if="login.notes">

View File

@@ -506,3 +506,58 @@
border: none;
}
}
.totp {
.totp-code {
font-family: @font-family-monospace;
font-size: 1.1em;
}
.totp-countdown {
margin: 3px 3px 0 0;
display: block;
user-select: none;
float: right;
.totp-sec {
font-size: 0.85em;
position: absolute;
line-height: 32px;
width: 32px;
text-align: center;
}
svg {
width: 32px;
height: 32px;
transform: rotate(-90deg);
}
.totp-circle {
stroke: @brand-primary;
fill: none;
&.inner {
stroke-width: 3;
stroke-dasharray: 78.6;
stroke-dashoffset: 0px;
}
&.outer {
stroke-width: 2;
stroke-dasharray: 88;
stroke-dashoffset: 0px;
}
}
}
&.low {
.totp-sec, .totp-code {
color: @brand-danger;
}
.totp-circle {
stroke: @brand-danger;
}
}
}