Adding response to 2FA
This commit is contained in:
2
cache/cache.go
vendored
2
cache/cache.go
vendored
@ -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
47
handler/auth/totp/totp.go
Normal 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")
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
54
view/totp/totp.templ
Normal 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)
|
||||
}
|
Reference in New Issue
Block a user