Fixed cookie stuff
This commit is contained in:
parent
b2843ff8b0
commit
54b0f23209
9 changed files with 152 additions and 32 deletions
23
.env.example
23
.env.example
|
@ -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
|
|
@ -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", "/")
|
||||
|
|
|
@ -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", "/")
|
||||
|
|
|
@ -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", "/")
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
121
lib/session.go
121
lib/session.go
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}}
|
||||
|
||||
|
|
2
public/css/styles.css
vendored
2
public/css/styles.css
vendored
File diff suppressed because one or more lines are too long
Loading…
Add table
Reference in a new issue