2FA: Ability to see original QR code again

face-detect
Noah Petherbridge 2023-11-15 18:01:34 -08:00
parent ebd63bcb0d
commit f0373285eb
3 changed files with 174 additions and 100 deletions

View File

@ -70,6 +70,9 @@ func Setup2FA() http.HandlerFunc {
} }
} }
// Are they (re)viewing their original QR code?
var isPairingSecondDevice bool
// POST form actions. // POST form actions.
if r.Method == http.MethodPost { if r.Method == http.MethodPost {
var intent = r.PostFormValue("intent") var intent = r.PostFormValue("intent")
@ -84,8 +87,15 @@ func Setup2FA() http.HandlerFunc {
// Valid? // Valid?
if !valid { if !valid {
session.FlashError(w, r, "The passcode you submitted didn't seem correct. Try a new six-digit code.") session.FlashError(w, r, "The passcode you submitted didn't seem correct. Try a new six-digit code.")
templates.Redirect(w, r.URL.Path)
return // If they were reconfiguring a second device, go back to the re-setup screen.
if tf.Enabled {
isPairingSecondDevice = true
break
} else {
templates.Redirect(w, r.URL.Path)
return
}
} }
// OK! // OK!
@ -120,12 +130,24 @@ func Setup2FA() http.HandlerFunc {
session.Flash(w, r, "Your 2FA settings have been cleared and disabled.") session.Flash(w, r, "Your 2FA settings have been cleared and disabled.")
} }
} }
case "resetup":
// View the original QR code to set up a new device.
var password = r.PostFormValue("password")
if err := currentUser.CheckPassword(password); err != nil {
session.FlashError(w, r, "Couldn't access your 2FA QR code: the password you entered is incorrect.")
} else {
session.Flash(w, r, "Password accepted. Your 2FA QR code and setup steps will be displayed below.")
isPairingSecondDevice = true
}
default: default:
session.FlashError(w, r, "Unknown intent: %s", intent) session.FlashError(w, r, "Unknown intent: %s", intent)
} }
templates.Redirect(w, r.URL.Path) // All POST requests redirect away except resetup.
return if !isPairingSecondDevice {
templates.Redirect(w, r.URL.Path)
return
}
} }
// Generate the QR code. // Generate the QR code.
@ -135,9 +157,10 @@ func Setup2FA() http.HandlerFunc {
} }
var vars = map[string]interface{}{ var vars = map[string]interface{}{
"TwoFactor": tf, "TwoFactor": tf,
"Key": key, "Key": key,
"QRCode": qrCode, "QRCode": qrCode,
"IsPairingSecondDevice": isPairingSecondDevice,
} }
if err := tmpl.Execute(w, r, vars); err != nil { if err := tmpl.Execute(w, r, vars); err != nil {

View File

@ -134,7 +134,7 @@
{{end}} {{end}}
<!-- New Feature: Two Factor Auth --> <!-- New Feature: Two Factor Auth -->
{{if not .TwoFactorEnabled}} <!--
<div class="card block"> <div class="card block">
<header class="card-header has-background-success-dark"> <header class="card-header has-background-success-dark">
<p class="card-header-title has-text-light"> <p class="card-header-title has-text-light">
@ -162,7 +162,7 @@
</p> </p>
</div> </div>
</div> </div>
{{end}} -->
<div class="card block"> <div class="card block">
<header class="card-header has-background-link"> <header class="card-header has-background-link">

View File

@ -38,7 +38,13 @@
the time-limited six-digit code to log in. the time-limited six-digit code to log in.
</p> </p>
<h4>Backup Codes</h4> <!-- Are they reconfiguring their 2FA app (viewing the barcode)? -->
{{if .IsPairingSecondDevice}}
{{template "2fa-setup" .}}
<hr>
{{end}}
<h4 class="mt-4">Backup Codes</h4>
<p> <p>
In case you lose access to your Authenticator App, please print off or write down these In case you lose access to your Authenticator App, please print off or write down these
@ -72,7 +78,12 @@
</button> </button>
</form> </form>
<h4 class="has-text-danger mt-4">Disable Two-Factor Auth</h4> <hr>
<h4 class="has-text-danger mt-4">
<i class="fa fa-exclamation-triangle mr-1"></i>
Disable Two-Factor Auth
</h4>
<p> <p>
If you wish to <strong>disable</strong> two-factor authentication for your account, please enter If you wish to <strong>disable</strong> two-factor authentication for your account, please enter
@ -95,6 +106,43 @@
Disable Two-Factor Authentication Disable Two-Factor Authentication
</button> </button>
</form> </form>
<hr>
<h4 class="has-text-warning mt-4">
<i class="fa fa-qrcode mr-1"></i>
Set Up Another Device
</h4>
<p>
If you wish to <strong>set up another authenticator device</strong> and view your original
QR code, you may do so by entering your current account password below. This may be useful
if you have bought a new phone or want to migrate your authenticator to a different device,
so that you may access the original QR code and configure the new authenticator.
</p>
<p>
<strong>Note:</strong> this will not change your 2FA security key or backup codes. If you
have lost your old authenticator, it will be more secure to <strong>disable and then set up
2FA from scratch</strong>, which will generate a new secret key and backup codes.
</p>
<form action="{{.Request.URL.Path}}" method="POST">
{{InputCSRF}}
<input type="hidden" name="intent" value="resetup">
<div class="field">
<label class="label" for="password">Your account password:</label>
<input type="password" class="input"
name="password"
placeholder="Password"
required>
</div>
<button type="submit" class="button is-warning">
View my original 2FA QR code
</button>
</form>
</div> </div>
</div> </div>
</div> </div>
@ -144,95 +192,7 @@
setting up your Authenticator App. setting up your Authenticator App.
</p> </p>
<h2>Set up your Authenticator App</h2> {{template "2fa-setup" .}}
<p>
To set up Two-Factor Auth, you'll need to download and install a compatible
Authenticator App on your device. Some suggestions for apps that are compatible
with {{PrettyTitle}} are as follows:
</p>
<ul>
<li>
<strong>Google Authenticator:</strong>
for <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2" target="_blank">
<i class="fab fa-android"></i> Android
</a> or <a href="https://apps.apple.com/us/app/google-authenticator/id388497605" target="_blank">
<i class="fab fa-apple"></i> iOS
</a>
</li>
<li>
<strong>
<a href="https://authy.com/download/" target="_blank">Authy:</a>
</strong>
available for <i class="fab fa-android"></i> Android and <i class="fab fa-apple"></i> iOS
as well as Windows, macOS and Linux computers.
</li>
</ul>
<h3>Add {{PrettyTitle}} to your Authenticator App</h3>
<p>
When you have your Authenticator App ready, click on its "Add a new site" button and scan
the following QR code to enroll your device for {{PrettyTitle}}:
</p>
{{ToHTML .QRCode}}
<p>
Alternatively (if you can't scan the QR code), you may copy and paste this secret text
in to your Authenticator app:
</p>
<div class="columns is-mobile">
<div class="column is-half-tablet pr-1">
<input type="text" class="input"
id="totp-secret"
value="{{.Key.Secret}}"
readonly
onclick="copySecret()">
</div>
<div class="column is-narrow pl-0">
<button type="button"
class="button is-success"
id="copy-button"
onclick="copySecret()">
<i class="fa fa-copy mr-1"></i> Copy
</button>
</div>
</div>
<h3>Test your Authenticator App</h3>
<p>
After scanning the QR code (or copying the secret key) into your Authenticator app, you
should be able to generate temporary six-digit authentication codes.
</p>
<p>
Test that you have enrolled your authenticator correctly by entering the current six-digit
code below:
</p>
<form action="{{.Request.URL.Path}}" method="POST">
{{InputCSRF}}
<input type="hidden" name="intent" value="setup-verify">
<div class="field">
<label class="label" for="code">
Authentication Code:
</label>
<input type="text" class="input is-one-quarter"
name="code" id="code"
pattern="^[0-9]{6}$"
maxlength="6"
placeholder="000000"
style="max-width: 12em"
autocomplete="off">
</div>
<button type="submit" class="button is-primary">Confirm &amp; Continue</button>
</form>
</div> </div>
</div> </div>
</div> </div>
@ -241,6 +201,97 @@
</div> </div>
{{end}} {{end}}
{{define "2fa-setup"}}
<h2>Set up your Authenticator App</h2>
<p>
To set up Two-Factor Auth, you'll need to download and install a compatible
Authenticator App on your device. Some suggestions for apps that are compatible
with {{PrettyTitle}} are as follows:
</p>
<ul>
<li>
<strong>Google Authenticator:</strong>
for <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2" target="_blank">
<i class="fab fa-android"></i> Android
</a> or <a href="https://apps.apple.com/us/app/google-authenticator/id388497605" target="_blank">
<i class="fab fa-apple"></i> iOS
</a>
</li>
<li>
<strong>
<a href="https://authy.com/download/" target="_blank">Authy:</a>
</strong>
available for <i class="fab fa-android"></i> Android and <i class="fab fa-apple"></i> iOS
as well as Windows, macOS and Linux computers.
</li>
</ul>
<h3>Add {{PrettyTitle}} to your Authenticator App</h3>
<p>
When you have your Authenticator App ready, click on its "Add a new site" button and scan
the following QR code to enroll your device for {{PrettyTitle}}:
</p>
{{ToHTML .QRCode}}
<p>
Alternatively (if you can't scan the QR code), you may copy and paste this secret text
in to your Authenticator app:
</p>
<div class="columns is-mobile">
<div class="column is-half-tablet pr-1">
<input type="text" class="input"
id="totp-secret"
value="{{.Key.Secret}}"
readonly
onclick="copySecret()">
</div>
<div class="column is-narrow pl-0">
<button type="button"
class="button is-success"
id="copy-button"
onclick="copySecret()">
<i class="fa fa-copy mr-1"></i> Copy
</button>
</div>
</div>
<h3>Test your Authenticator App</h3>
<p>
After scanning the QR code (or copying the secret key) into your Authenticator app, you
should be able to generate temporary six-digit authentication codes.
</p>
<p>
Test that you have enrolled your authenticator correctly by entering the current six-digit
code below:
</p>
<form action="{{.Request.URL.Path}}" method="POST">
{{InputCSRF}}
<input type="hidden" name="intent" value="setup-verify">
<div class="field">
<label class="label" for="code">
Authentication Code:
</label>
<input type="text" class="input is-one-quarter"
name="code" id="code"
pattern="^[0-9]{6}$"
maxlength="6"
placeholder="000000"
style="max-width: 12em"
autocomplete="off">
</div>
<button type="submit" class="button is-primary">Confirm &amp; Continue</button>
</form>
{{end}}
{{define "scripts"}} {{define "scripts"}}
<script> <script>
function copySecret() { function copySecret() {