Fixed cookie stuff

This commit is contained in:
Atridad Lahiji 2024-06-28 00:35:58 -06:00
parent b2843ff8b0
commit 54b0f23209
No known key found for this signature in database
9 changed files with 152 additions and 32 deletions

View file

@ -1,14 +1,15 @@
# This file is an example of the .env file that should be created in the root of the project
# This file should not be committed to the repository
# The actual .env file should contain the following variables
# The database credentials
# This file is used to store environment variables for the application
# Database Connection Information
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_PASSWORD=postgres
POSTGRES_USER=postgres
POSTGRES_DB=db
# The secret key used to sign the JWT tokens
AUTH_SECRET=super-duper-secret
# If you want to run the app in development mode, set this to true
POSTGRES_PASSWORD=password
POSTGRES_USER=atridadlahiji
POSTGRES_DB=pollo
# Security
ENCRYPTION_KEY="super-secret"
SIGNING_KEY="super-secret"
AUTH_SECRET="super-secret"
# Feature Flags
DEVMODE=true

View file

@ -27,7 +27,12 @@ func RegisterUserHandler(c echo.Context) error {
}
// Set the session cookie using the helper function
lib.SetSessionCookie(c.Response().Writer, "session_id", sessionID)
lib.SetSessionCookie(c.Response().Writer, "session", lib.SessionData{
SessionID: sessionID,
UserID: user.ID,
Email: user.Email,
Roles: []string{"user"},
})
// Redirect or respond with a success status code
c.Response().Header().Set("HX-Redirect", "/")

View file

@ -25,7 +25,12 @@ func SignInUserHandler(c echo.Context) error {
}
// Set the session cookie with the generated session ID
lib.SetSessionCookie(c.Response().Writer, "session_id", sessionID)
lib.SetSessionCookie(c.Response().Writer, "session", lib.SessionData{
SessionID: sessionID,
UserID: user.ID,
Email: user.Email,
Roles: []string{"user"},
})
// Proceed with login success logic
c.Response().Header().Set("HX-Redirect", "/")

View file

@ -9,7 +9,7 @@ import (
func SignOutUserHandler(c echo.Context) error {
// Clear the session cookie
lib.ClearSessionCookie(c.Response().Writer, "session_id")
lib.ClearSessionCookie(c.Response().Writer, "session")
// Proceed with login success logic
c.Response().Header().Set("HX-Redirect", "/")

View file

@ -11,7 +11,7 @@ import (
)
type User struct {
ID int
ID string
Email string
Password string
}
@ -35,7 +35,8 @@ func GetUserByEmail(dbPool *pgxpool.Pool, email string) (*User, error) {
}
var user User
err := dbPool.QueryRow(context.Background(), "SELECT id, email, password FROM users WHERE email = $1", email).Scan(&user.ID, &user.Email, &user.Password)
// Ensure the ID is being scanned as a string.
err := dbPool.QueryRow(context.Background(), "SELECT id::text, email, password FROM users WHERE email = $1", email).Scan(&user.ID, &user.Email, &user.Password)
if err != nil {
return nil, err
}

View file

@ -1,8 +1,14 @@
package lib
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"os"
@ -12,6 +18,13 @@ import (
"github.com/labstack/echo/v4"
)
type SessionData struct {
SessionID string `json:"sessionID"`
UserID string `json:"userId"`
Email string `json:"email"`
Roles []string `json:"roles"`
}
func InitSessionMiddleware() echo.MiddlewareFunc {
authSecret := os.Getenv("AUTH_SECRET")
if authSecret == "" {
@ -22,30 +35,108 @@ func InitSessionMiddleware() echo.MiddlewareFunc {
return session.Middleware(store)
}
// SetSessionCookie sets a secure cookie with the session ID in the client's browser
func SetSessionCookie(w http.ResponseWriter, name, value string) {
devMode := os.Getenv("DEVMODE") == "true"
secureFlag := !devMode
println("Secure flag: ", secureFlag)
// Encrypt data using AES
func encrypt(data []byte) (string, error) {
encryptionKey := []byte(os.Getenv("ENCRYPTION_KEY"))
fmt.Printf("Encryption Key Length: %d\n", len(encryptionKey))
block, err := aes.NewCipher(encryptionKey)
if err != nil {
return "", err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
nonce := make([]byte, gcm.NonceSize())
_, err = rand.Read(nonce)
if err != nil {
return "", err
}
cipherText := gcm.Seal(nonce, nonce, data, nil)
return base64.StdEncoding.EncodeToString(cipherText), nil
}
// decrypt decrypts the data using AES-GCM.
func decrypt(encryptedString string) (string, error) {
encryptionKey := []byte(os.Getenv("ENCRYPTION_KEY"))
data, err := base64.StdEncoding.DecodeString(encryptedString)
if err != nil {
return "", err
}
block, err := aes.NewCipher(encryptionKey)
if err != nil {
return "", err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
nonceSize := gcm.NonceSize()
if len(data) < nonceSize {
return "", errors.New("ciphertext too short")
}
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return "", err
}
return string(plaintext), nil
}
// Adjusted SetSessionCookie to include more user info
func SetSessionCookie(w http.ResponseWriter, name string, sessionData SessionData) {
// Serialize session data
dataBytes, err := json.Marshal(sessionData)
if err != nil {
log.Fatal("Failed to serialize session data:", err)
}
// Encrypt serialized session data
encryptedData, err := encrypt(dataBytes)
if err != nil {
log.Fatal("Failed to encrypt session data:", err)
}
// Set cookie with encrypted data
http.SetCookie(w, &http.Cookie{
Name: name,
Value: value,
Value: encryptedData,
Path: "/",
HttpOnly: true,
Secure: secureFlag,
Secure: os.Getenv("DEVMODE") != "true",
SameSite: http.SameSiteStrictMode,
MaxAge: 3600,
})
}
// GetSessionCookie retrieves the session cookie value by its name
func GetSessionCookie(r *http.Request, name string) (string, error) {
// Adjusted GetSessionCookie to return SessionData
func GetSessionCookie(r *http.Request, name string) (*SessionData, error) {
cookie, err := r.Cookie(name)
if err != nil {
return "", err
return nil, err
}
return cookie.Value, nil
// Decrypt the cookie value
decryptedValue, err := decrypt(cookie.Value)
if err != nil {
return nil, err
}
// Deserialize session data
var sessionData SessionData
err = json.Unmarshal([]byte(decryptedValue), &sessionData)
if err != nil {
return nil, err
}
return &sessionData, nil
}
// ClearSessionCookie clears the session cookie from the client's browser
@ -57,13 +148,17 @@ func ClearSessionCookie(w http.ResponseWriter, name string) {
HttpOnly: true,
Secure: os.Getenv("DEVMODE") != "true",
SameSite: http.SameSiteStrictMode,
MaxAge: -1, // Set MaxAge to -1 to delete the cookie immediately.
MaxAge: -1, // This will delete the cookie
})
}
// Checks if the user is signed in by checking the session cookie
func IsSignedIn(c echo.Context) bool {
_, err := GetSessionCookie(c.Request(), "session_id")
_, err := GetSessionCookie(c.Request(), "session")
if err != nil {
// Log the error for debugging purposes
log.Printf("Error retrieving session cookie: %v", err)
}
return err == nil
}

View file

@ -8,11 +8,18 @@ import (
type DashboardProps struct {
IsLoggedIn bool
Email string
}
func Dashboard(c echo.Context) error {
currentSession, error := lib.GetSessionCookie(c.Request(), "session")
if error != nil {
return c.Redirect(302, "/signin")
}
props := DashboardProps{
IsLoggedIn: lib.IsSignedIn(c),
Email: currentSession.Email,
}
// Specify the partials used by this page

View file

@ -11,8 +11,14 @@ Pollo // Dashboard
{{end}}
{{define "main"}}
<div class="flex flex-col text-center items-center justify-center px-4 py-16 gap-4">
SIGNED IN
<div class="flex flex-col items-center justify-center gap-8">
<h1 class="flex flex-row flex-wrap text-center justify-center items-center gap-1 text-4xl font-bold">
Hi, {{.Email}}!
</h1>
<label htmlFor="new-room-modal" class="btn btn-primary">
New Room
</label>
</div>
{{end}}

File diff suppressed because one or more lines are too long