Update frame work and workspace codebase
This commit is contained in:
125
internal/http/auth_handlers.go
Normal file
125
internal/http/auth_handlers.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/example/storage-appliance/internal/auth"
|
||||
)
|
||||
|
||||
// LoginHandler handles user login
|
||||
func (a *App) LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "GET" {
|
||||
// Show login page
|
||||
data := map[string]interface{}{
|
||||
"Title": "Login",
|
||||
}
|
||||
if err := templates.ExecuteTemplate(w, "login", data); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Handle POST login
|
||||
var req struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
if r.Header.Get("Content-Type") == "application/json" {
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
req.Username = r.FormValue("username")
|
||||
req.Password = r.FormValue("password")
|
||||
}
|
||||
|
||||
// Authenticate user
|
||||
userStore := auth.NewUserStore(a.DB)
|
||||
user, err := userStore.Authenticate(r.Context(), req.Username, req.Password)
|
||||
if err != nil {
|
||||
if r.Header.Get("HX-Request") == "true" {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte(`<div class="text-red-600">Invalid username or password</div>`))
|
||||
} else {
|
||||
http.Error(w, "invalid credentials", http.StatusUnauthorized)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create session
|
||||
sessionStore := auth.NewSessionStore(a.DB)
|
||||
session, err := sessionStore.CreateSession(r.Context(), user.ID)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to create session", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Set session cookie
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: auth.SessionCookieName,
|
||||
Value: session.Token,
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
Secure: false, // Set to true in production with HTTPS
|
||||
SameSite: http.SameSiteStrictMode,
|
||||
MaxAge: int(auth.SessionDuration.Seconds()),
|
||||
})
|
||||
|
||||
// Set CSRF token cookie
|
||||
csrfToken := generateCSRFToken()
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "csrf_token",
|
||||
Value: csrfToken,
|
||||
Path: "/",
|
||||
HttpOnly: false, // Needed for HTMX to read it
|
||||
Secure: false,
|
||||
SameSite: http.SameSiteStrictMode,
|
||||
MaxAge: int(auth.SessionDuration.Seconds()),
|
||||
})
|
||||
|
||||
// Redirect or return success
|
||||
if r.Header.Get("HX-Request") == "true" {
|
||||
w.Header().Set("HX-Redirect", "/dashboard")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
} else {
|
||||
http.Redirect(w, r, "/dashboard", http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
// LogoutHandler handles user logout
|
||||
func (a *App) LogoutHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Get session token from cookie
|
||||
cookie, err := r.Cookie(auth.SessionCookieName)
|
||||
if err == nil {
|
||||
// Delete session
|
||||
sessionStore := auth.NewSessionStore(a.DB)
|
||||
sessionStore.DeleteSession(r.Context(), cookie.Value)
|
||||
}
|
||||
|
||||
// Clear cookies
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: auth.SessionCookieName,
|
||||
Value: "",
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
MaxAge: -1,
|
||||
})
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "csrf_token",
|
||||
Value: "",
|
||||
Path: "/",
|
||||
HttpOnly: false,
|
||||
MaxAge: -1,
|
||||
})
|
||||
|
||||
if r.Header.Get("HX-Request") == "true" {
|
||||
w.Header().Set("HX-Redirect", "/login")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
} else {
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user