Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add spinner to fix flash of password prompt when auto-decrypting #142

Merged
merged 2 commits into from
Nov 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -147,18 +147,18 @@ <h4>
</div>

<div class="form-group">
<label for="title">Passphrase input placeholder</label>
<label for="passphrase_placeholder">Passphrase input placeholder</label>
<input type="text" class="form-control" id="passphrase_placeholder"
placeholder="Default: 'Passphrase'">
</div>

<div class="form-group">
<label for="title">"Remember me" checkbox label</label>
<label for="remember_me">"Remember me" checkbox label</label>
<input type="text" class="form-control" id="remember_me" placeholder="Default: 'Remember me'">
</div>

<div class="form-group">
<label for="title">"Remember me" expiration in days</label>
<label for="remember_in_days">"Remember me" expiration in days</label>
<input type="number"
class="form-control"
id="remember_in_days"
Expand All @@ -171,7 +171,7 @@ <h4>
</div>

<div class="form-group">
<label for="title">Decrypt button label</label>
<label for="decrypt_button">Decrypt button label</label>
<input type="text" class="form-control" id="decrypt_button" placeholder="Default: 'DECRYPT'">
</div>

Expand Down
175 changes: 124 additions & 51 deletions lib/password_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@
}

.staticrypt-body {
height: 100%;
margin: 0;
}

.staticrypt-content {
height: 100%;
margin-bottom: 1em;
background: #76b852; /* fallback for old browsers */
background: -webkit-linear-gradient(right, #76b852, #8DC26F);
Expand Down Expand Up @@ -125,41 +131,82 @@
.hidden {
display: none !important;
}

.staticrypt-spinner-container {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}

.staticrypt-spinner {
display: inline-block;
width: 2rem;
height: 2rem;
vertical-align: text-bottom;
border: 0.25em solid gray;
border-right-color: transparent;
border-radius: 50%;
-webkit-animation: spinner-border .75s linear infinite;
animation: spinner-border .75s linear infinite;
animation-duration: 0.75s;
animation-timing-function: linear;
animation-delay: 0s;
animation-iteration-count: infinite;
animation-direction: normal;
animation-fill-mode: none;
animation-play-state: running;
animation-name: spinner-border;
}

@keyframes spinner-border {
100% {
transform: rotate(360deg);
}
}
</style>
</head>

<body class="staticrypt-body">
<div class="staticrypt-page">
<div class="staticrypt-form">
<div class="staticrypt-instructions">
<p class="staticrypt-title">{title}</p>
<p>{instructions}</p>
</div>

<hr class="staticrypt-hr">

<form id="staticrypt-form" action="#" method="post">
<input id="staticrypt-password"
type="password"
name="password"
placeholder="{passphrase_placeholder}"
autofocus/>
<div id="staticrypt_loading" class="staticrypt-spinner-container">
<div class="staticrypt-spinner"></div>
</div>

<label id="staticrypt-remember-label" class="staticrypt-remember hidden">
<input id="staticrypt-remember"
type="checkbox"
name="remember"/>
{remember_me}
</label>
<div id="staticrypt_content" class="staticrypt-content hidden">
<div class="staticrypt-page">
<div class="staticrypt-form">
<div class="staticrypt-instructions">
<p class="staticrypt-title">{title}</p>
<p>{instructions}</p>
</div>

<hr class="staticrypt-hr">

<form id="staticrypt-form" action="#" method="post">
<input id="staticrypt-password"
type="password"
name="password"
placeholder="{passphrase_placeholder}"
autofocus/>

<label id="staticrypt-remember-label" class="staticrypt-remember hidden">
<input id="staticrypt-remember"
type="checkbox"
name="remember"/>
{remember_me}
</label>

<input type="submit" class="staticrypt-decrypt-button" value="{decrypt_button}"/>
</form>
</div>

<input type="submit" class="staticrypt-decrypt-button" value="{decrypt_button}"/>
</form>
</div>

<footer class="staticrypt-footer">
<p class="pull-right">Created with <a href="https://robinmoisson.github.io/staticrypt">StatiCrypt</a></p>
</footer>
</div>
<footer class="staticrypt-footer">
<p class="pull-right">Created with <a href="https://robinmoisson.github.io/staticrypt">StatiCrypt</a></p>
</footer>


{crypto_tag}
Expand Down Expand Up @@ -205,41 +252,67 @@
localStorage.removeItem(rememberExpirationKey);
}

// try to automatically decrypt on load if there is a saved password
window.onload = function () {
if (isRememberEnabled) {
// show the remember me checkbox
document.getElementById('staticrypt-remember-label').classList.remove('hidden');
/**
* To be called on load: check if we want to try to decrypt and replace the HTML with the decrypted content, and
* try to do it if needed.
*
* @returns {boolean} true if we derypted and replaced the whole page, false otherwise
*/
function decryptOnLoadFromRememberMe() {
if (!isRememberEnabled) {
return false;
}

// if we are login out, clear the storage and terminate
var queryParams = new URLSearchParams(window.location.search);
// show the remember me checkbox
document.getElementById('staticrypt-remember-label').classList.remove('hidden');

if (queryParams.has("staticrypt_logout")) {
return clearLocalStorage();
}
// if we are login out, clear the storage and terminate
var queryParams = new URLSearchParams(window.location.search);

// if there is expiration configured, check if we're not beyond the expiration
if (rememberDurationInDays && rememberDurationInDays > 0) {
var expiration = localStorage.getItem(rememberExpirationKey),
isExpired = expiration && new Date().getTime() > parseInt(expiration);
if (queryParams.has("staticrypt_logout")) {
clearLocalStorage();
return false;
}

if (isExpired) {
return clearLocalStorage();
}
// if there is expiration configured, check if we're not beyond the expiration
if (rememberDurationInDays && rememberDurationInDays > 0) {
var expiration = localStorage.getItem(rememberExpirationKey),
isExpired = expiration && new Date().getTime() > parseInt(expiration);

if (isExpired) {
clearLocalStorage();
return false;
}
}

var hashedPassphrase = localStorage.getItem(rememberPassphraseKey);
var hashedPassphrase = localStorage.getItem(rememberPassphraseKey);

if (hashedPassphrase) {
// try to decrypt
var isDecryptionSuccessful = decryptAndReplaceHtml(hashedPassphrase);
if (hashedPassphrase) {
// try to decrypt
var isDecryptionSuccessful = decryptAndReplaceHtml(hashedPassphrase);

// if the decryption is unsuccessful the password might be wrong - silently clear the saved data and let
// the user fill the password form again
if (!isDecryptionSuccessful) {
return clearLocalStorage();
}
// if the decryption is unsuccessful the password might be wrong - silently clear the saved data and let
// the user fill the password form again
if (!isDecryptionSuccessful) {
clearLocalStorage();
return false;
}

return true;
}

return false;
}

// try to automatically decrypt on load if there is a saved password
window.onload = function () {
var hasDecrypted = decryptOnLoadFromRememberMe();

// if we didn't decrypt anything, show the password prompt. Otherwise the content has already been replaced, no
// need to do anything
if (!hasDecrypted) {
document.getElementById("staticrypt_loading").classList.add("hidden");
document.getElementById("staticrypt_content").classList.remove("hidden");
}
}

Expand Down