package api import ( "context" "net/http" "time" "github.com/google/uuid" "golang.org/x/crypto/bcrypt" ) func Login(w http.ResponseWriter, r *http.Request) { err := r.ParseMultipartForm(64 << 10) if err != nil { http.Error(w, "Unable to parse form", http.StatusBadRequest) return } username := r.FormValue("username") password := r.FormValue("password") if username == "" || password == "" { http.Error(w, "Username and password cannot be empty", http.StatusBadRequest) return } user, err := dbGetUserByName(username) if err != nil { http.Error(w, "Invalid username or password", http.StatusUnauthorized) return } err = validatePassword(user.Password, password) if err != nil { http.Error(w, "Invalid username or password", http.StatusUnauthorized) return } sessionToken := CreateSession(username) http.SetCookie(w, &http.Cookie{ Name: "session_token", Value: sessionToken, Path: "/", HttpOnly: true, Secure: false, }) w.Write([]byte("Login successful")) } func Logout(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("session_token") if err != nil { http.Error(w, "No session cookie found. You are already logged out", http.StatusBadRequest) return } sessionToken := cookie.Value username, valid := ValidateSession(sessionToken) if !valid { http.Error(w, "Session cookie could not be validated. You are already logged out", http.StatusBadRequest) return } DeleteSession(sessionToken) cookie.Expires = time.Now() http.SetCookie(w, cookie) w.Write([]byte(username + " has been logged out")) } type Session struct { Token uuid.UUID Username string } func CreateSession(username string) string { session := Session{ Token: uuid.New(), Username: username, } dbAddSession(&session) return session.Token.String() } func ValidateSession(sessionToken string) (string, bool) { tokenUUID, err := uuid.Parse(sessionToken) if err != nil { return "", false } session, err := dbGetSession(tokenUUID) if err != nil { return "", false } return session.Username, true } func DeleteSession(sessionToken string) (string, bool) { tokenUUID, err := uuid.Parse(sessionToken) if err != nil { return "", false } session, err := dbGetSession(tokenUUID) if err != nil { return "", false } else { dbDeleteSession(session.Token) } return session.Username, true } type contextKey string const usernameKey contextKey = "username" func SessionAuthMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("session_token") if err != nil { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } sessionToken := cookie.Value username, valid := ValidateSession(sessionToken) if !valid { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } // Add username to request context ctx := context.WithValue(r.Context(), usernameKey, username) next.ServeHTTP(w, r.WithContext(ctx)) }) } func (u *UserPayload) Render(w http.ResponseWriter, r *http.Request) error { return nil } func hashPassword(password string) (string, error) { hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) return string(hashedPassword), err } func validatePassword(hashedPassword, password string) error { return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)) }