Adding response to 2FA

This commit is contained in:
2024-06-19 21:34:50 +07:00
parent b890f5442c
commit c1059072a6
7 changed files with 141 additions and 0 deletions

2
cache/cache.go vendored
View File

@ -15,6 +15,7 @@ type UserWithExpired struct {
Username string
Email string
Password string
Totp string
AccessAt time.Time
mu sync.Mutex
}
@ -103,6 +104,7 @@ func GetUser(email string) (*UserWithExpired, error) {
Username: userData.Username,
Email: userData.Email,
Password: userData.Password,
Totp: userData.Totp,
AccessAt: time.Now(),
}

47
handler/auth/totp/totp.go Normal file
View File

@ -0,0 +1,47 @@
package totpHandler
import (
"fmt"
"github.com/fossyy/filekeeper/session"
"github.com/fossyy/filekeeper/types"
totpView "github.com/fossyy/filekeeper/view/totp"
"github.com/xlzd/gotp"
"net/http"
"time"
)
func GET(w http.ResponseWriter, r *http.Request) {
component := totpView.Main("Filekeeper - 2FA Page")
err := component.Render(r.Context(), w)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
return
}
func POST(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
code := r.Form.Get("code")
_, user, key := session.GetSession(r)
totp := gotp.NewDefaultTOTP(user.Totp)
if totp.Verify(code, time.Now().Unix()) {
storeSession, err := session.Get(key)
if err != nil {
return
}
fmt.Println(storeSession)
storeSession.Values["user"] = types.User{
UserID: user.UserID,
Email: user.Email,
Username: user.Username,
Totp: "",
Authenticated: true,
}
http.Redirect(w, r, "/user", http.StatusFound)
return
} else {
fmt.Fprint(w, "wrong")
}
}

View File

@ -17,6 +17,10 @@ import (
var log *logger.AggregatedLogger
var errorMessages = make(map[string]string)
func init() {
}
func init() {
errorMessages = map[string]string{
"redirect_uri_mismatch": "The redirect URI provided does not match the one registered with our service. Please contact the administrator for assistance.",
@ -92,6 +96,20 @@ func POST(w http.ResponseWriter, r *http.Request) {
}
if email == userData.Email && utils.CheckPasswordHash(password, userData.Password) {
if userData.Totp != "" {
storeSession := session.Create()
storeSession.Values["user"] = types.User{
UserID: userData.UserID,
Email: email,
Username: userData.Username,
Totp: userData.Totp,
Authenticated: false,
}
storeSession.Save(w)
http.Redirect(w, r, "/auth/totp", http.StatusSeeOther)
return
}
storeSession := session.Create()
storeSession.Values["user"] = types.User{
UserID: userData.UserID,

View File

@ -4,6 +4,7 @@ import (
googleOauthHandler "github.com/fossyy/filekeeper/handler/auth/google"
googleOauthCallbackHandler "github.com/fossyy/filekeeper/handler/auth/google/callback"
googleOauthSetupHandler "github.com/fossyy/filekeeper/handler/auth/google/setup"
totpHandler "github.com/fossyy/filekeeper/handler/auth/totp"
downloadHandler "github.com/fossyy/filekeeper/handler/download"
downloadFileHandler "github.com/fossyy/filekeeper/handler/download/file"
forgotPasswordHandler "github.com/fossyy/filekeeper/handler/forgotPassword"
@ -47,7 +48,17 @@ func SetupRoutes() *http.ServeMux {
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
})
authRouter.HandleFunc("/totp", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
middleware.Guest(totpHandler.GET, w, r)
case http.MethodPost:
middleware.Guest(totpHandler.POST, w, r)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
})
authRouter.HandleFunc("/google/callback", func(w http.ResponseWriter, r *http.Request) {

View File

@ -176,6 +176,14 @@ func GetSession(r *http.Request) (UserStatus, types.User, string) {
return Unauthorized, types.User{}, ""
}
if !userSession.Authenticated && userSession.Totp != "" {
return Unauthorized, userSession, cookie.Value
}
if !userSession.Authenticated {
return Unauthorized, types.User{}, ""
}
return Authorized, userSession, cookie.Value
}

View File

@ -13,6 +13,7 @@ type User struct {
UserID uuid.UUID
Email string
Username string
Totp string
Authenticated bool
}

54
view/totp/totp.templ Normal file
View File

@ -0,0 +1,54 @@
package totpView
import (
"github.com/fossyy/filekeeper/view/layout"
)
templ content(title string) {
@layout.Base(title){
<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="w-full max-w-md space-y-8">
<div>
<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">
Enter the 6-digit code sent to your registered device to complete the login process.
</p>
</div>
<form class="space-y-6" action="/auth/totp" method="POST">
<div>
<label for="code" class="block text-sm font-medium text-muted-foreground">
Verification Code
</label>
<div class="mt-1">
<input
class="h-10 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 block w-full appearance-none rounded-md border border-input bg-background px-3 py-2 placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-primary sm:text-sm"
id="code"
autocomplete="one-time-code"
required=""
placeholder="123456"
pattern="[0-9]{6}"
maxlength="6"
type="text"
name="code"
/>
</div>
</div>
<div>
<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"
type="submit"
>
Verify Code
</button>
</div>
</form>
</div>
</div>
</main>
}
}
templ Main(title string) {
@content(title)
}