Improve 2FA verification page messaging
This commit is contained in:
@ -2,7 +2,6 @@ package totpHandler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"github.com/fossyy/filekeeper/session"
|
"github.com/fossyy/filekeeper/session"
|
||||||
"github.com/fossyy/filekeeper/types"
|
"github.com/fossyy/filekeeper/types"
|
||||||
"github.com/fossyy/filekeeper/utils"
|
"github.com/fossyy/filekeeper/utils"
|
||||||
@ -19,7 +18,10 @@ func GET(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
component := totpView.Main("Filekeeper - 2FA Page")
|
component := totpView.Main("Filekeeper - 2FA Page", types.Message{
|
||||||
|
Code: 1,
|
||||||
|
Message: "",
|
||||||
|
})
|
||||||
err := component.Render(r.Context(), w)
|
err := component.Render(r.Context(), w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
@ -73,7 +75,16 @@ func POST(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Redirect(w, r, cookie.Value, http.StatusSeeOther)
|
http.Redirect(w, r, cookie.Value, http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprint(w, "wrong")
|
component := totpView.Main("Filekeeper - 2FA Page", types.Message{
|
||||||
|
Code: 0,
|
||||||
|
Message: "Incorrect code. Please try again with the latest code from your authentication app.",
|
||||||
|
})
|
||||||
|
err := component.Render(r.Context(), w)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,10 @@ func GET(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
component := userTotpSetupView.Main("Filekeeper - 2FA Setup Page", base64Str, secret, userSession)
|
component := userTotpSetupView.Main("Filekeeper - 2FA Setup Page", base64Str, secret, userSession, types.Message{
|
||||||
|
Code: 3,
|
||||||
|
Message: "",
|
||||||
|
})
|
||||||
if err := component.Render(r.Context(), w); err != nil {
|
if err := component.Render(r.Context(), w); err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
@ -59,15 +62,6 @@ func POST(w http.ResponseWriter, r *http.Request) {
|
|||||||
secret := r.Form.Get("secret")
|
secret := r.Form.Get("secret")
|
||||||
totp := gotp.NewDefaultTOTP(secret)
|
totp := gotp.NewDefaultTOTP(secret)
|
||||||
userSession := r.Context().Value("user").(types.User)
|
userSession := r.Context().Value("user").(types.User)
|
||||||
if totp.Verify(code, time.Now().Unix()) {
|
|
||||||
if err := db.DB.InitializeTotp(userSession.Email, secret); err != nil {
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cache.DeleteUser(userSession.Email)
|
|
||||||
fmt.Fprint(w, "Authentication successful! Access granted.")
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
uri := totp.ProvisioningUri(userSession.Email, "filekeeper")
|
uri := totp.ProvisioningUri(userSession.Email, "filekeeper")
|
||||||
|
|
||||||
base64Str, err := generateQRCode(uri)
|
base64Str, err := generateQRCode(uri)
|
||||||
@ -76,8 +70,26 @@ func POST(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if totp.Verify(code, time.Now().Unix()) {
|
||||||
component := userTotpSetupView.Main("Filekeeper - 2FA Setup Page", base64Str, secret, userSession)
|
if err := db.DB.InitializeTotp(userSession.Email, secret); err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cache.DeleteUser(userSession.Email)
|
||||||
|
component := userTotpSetupView.Main("Filekeeper - 2FA Setup Page", base64Str, secret, userSession, types.Message{
|
||||||
|
Code: 1,
|
||||||
|
Message: "Your TOTP setup is complete! Your account is now more secure.",
|
||||||
|
})
|
||||||
|
if err := component.Render(r.Context(), w); err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
component := userTotpSetupView.Main("Filekeeper - 2FA Setup Page", base64Str, secret, userSession, types.Message{
|
||||||
|
Code: 0,
|
||||||
|
Message: "The code you entered is incorrect. Please double-check the code and try again.",
|
||||||
|
})
|
||||||
if err := component.Render(r.Context(), w); err != nil {
|
if err := component.Render(r.Context(), w); err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
@ -2,17 +2,30 @@ package totpView
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fossyy/filekeeper/view/layout"
|
"github.com/fossyy/filekeeper/view/layout"
|
||||||
|
"github.com/fossyy/filekeeper/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
templ content(title string) {
|
templ content(title string, msg types.Message) {
|
||||||
@layout.Base(title){
|
@layout.Base(title){
|
||||||
<main class="container mx-auto px-4 py-12 md:px-6 md:py-16 lg:py-10">
|
<main class="container mx-auto px-4 py-12 md:px-6 md:py-16 lg:py-10">
|
||||||
<div class="flex min-h-screen items-center justify-center bg-background px-4 py-12 sm:px-6 lg:px-8">
|
<div class="flex min-h-screen items-center justify-center bg-background px-4 py-12 sm:px-6 lg:px-8">
|
||||||
<div class="w-full max-w-md space-y-8">
|
<div class="w-full max-w-md space-y-8">
|
||||||
<div>
|
<div>
|
||||||
|
switch msg.Code {
|
||||||
|
case 0:
|
||||||
|
<div class="flex items-center p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50" role="alert">
|
||||||
|
<svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Info</span>
|
||||||
|
<div>
|
||||||
|
<span class="font-medium">Error!</span> {msg.Message}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<h2 class="mt-6 text-center text-3xl font-bold tracking-tight text-foreground">Verify Your Identity</h2>
|
<h2 class="mt-6 text-center text-3xl font-bold tracking-tight text-foreground">Verify Your Identity</h2>
|
||||||
<p class="mt-2 text-center text-sm text-muted-foreground">
|
<p class="mt-2 text-center text-sm text-muted-foreground">
|
||||||
Enter the 6-digit code sent to your registered device to complete the login process.
|
Please enter the 6-digit code generated by your authentication app to complete the login process.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<form class="space-y-6" method="POST">
|
<form class="space-y-6" method="POST">
|
||||||
@ -36,7 +49,7 @@ templ content(title string) {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
class="items-center whitespace-nowrap ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 h-10 flex w-full justify-center rounded-md bg-primary py-2 px-4 text-sm font-medium text-primary-foreground shadow-sm hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
|
class="items-center whitespace-nowrap ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 h-10 flex w-full justify-center rounded-md bg-black py-2 px-4 text-sm font-medium text-primary-foreground shadow-sm text-white hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
Verify Code
|
Verify Code
|
||||||
@ -49,6 +62,6 @@ templ content(title string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
templ Main(title string) {
|
templ Main(title string, msg types.Message) {
|
||||||
@content(title)
|
@content(title, msg)
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ import (
|
|||||||
"github.com/fossyy/filekeeper/types"
|
"github.com/fossyy/filekeeper/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
templ content(title string, qrcode string, code string, user types.User) {
|
templ content(title string, qrcode string, code string, user types.User, msg types.Message) {
|
||||||
@layout.Base(title){
|
@layout.Base(title){
|
||||||
@layout.Navbar(user)
|
@layout.Navbar(user)
|
||||||
<main class="container mx-auto px-4 py-12 md:px-6 md:py-16 lg:py-10">
|
<main class="container mx-auto px-4 py-12 md:px-6 md:py-16 lg:py-10">
|
||||||
@ -50,6 +50,28 @@ templ content(title string, qrcode string, code string, user types.User) {
|
|||||||
</div>
|
</div>
|
||||||
<div class="rounded-lg border rounded-lg bg-muted p-6bg-card text-card-foreground shadow-sm mt-5" data-v0-t="card">
|
<div class="rounded-lg border rounded-lg bg-muted p-6bg-card text-card-foreground shadow-sm mt-5" data-v0-t="card">
|
||||||
<div class="p-6 space-y-6">
|
<div class="p-6 space-y-6">
|
||||||
|
switch msg.Code {
|
||||||
|
case 0:
|
||||||
|
<div class="flex items-center p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50" role="alert">
|
||||||
|
<svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Info</span>
|
||||||
|
<div>
|
||||||
|
<span class="font-medium">Error!</span> {msg.Message}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
case 1:
|
||||||
|
<div class="flex items-center p-4 mb-4 text-sm text-green-800 rounded-lg bg-green-50" role="alert">
|
||||||
|
<svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Info</span>
|
||||||
|
<div>
|
||||||
|
<span class="font-medium">Success!</span> {msg.Message}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<img
|
<img
|
||||||
src={"data:image/png;base64," + qrcode}
|
src={"data:image/png;base64," + qrcode}
|
||||||
@ -62,7 +84,7 @@ templ content(title string, qrcode string, code string, user types.User) {
|
|||||||
</div>
|
</div>
|
||||||
<div class="mt-6 space-y-2">
|
<div class="mt-6 space-y-2">
|
||||||
<p class="font-medium">Backup Code:</p>
|
<p class="font-medium">Backup Code:</p>
|
||||||
<div class="rounded-md bg-background px-4 py-2 text-sm font-mono text-muted-foreground">12345-67890</div>
|
<div class="rounded-md bg-background px-4 py-2 text-sm font-mono text-muted-foreground">----|----</div>
|
||||||
<p class="font-medium">TOTP Secret:</p>
|
<p class="font-medium">TOTP Secret:</p>
|
||||||
<div class="rounded-md bg-background px-4 py-2 text-sm font-mono text-muted-foreground">
|
<div class="rounded-md bg-background px-4 py-2 text-sm font-mono text-muted-foreground">
|
||||||
{code}
|
{code}
|
||||||
@ -94,6 +116,6 @@ templ content(title string, qrcode string, code string, user types.User) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
templ Main(title string, qrcode string, code string, user types.User) {
|
templ Main(title string, qrcode string, code string, user types.User, msg types.Message) {
|
||||||
@content(title, qrcode, code, user)
|
@content(title, qrcode, code, user, msg)
|
||||||
}
|
}
|
Reference in New Issue
Block a user