Skip to content

Commit

Permalink
Merge pull request #71 from COS301-SE-2024/fix/backend/logout-session…
Browse files Browse the repository at this point in the history
…-management

chore: Add some more edge cases to session management for users login and logout sessions
  • Loading branch information
waveyboym authored Jun 19, 2024
2 parents ae2eea8 + 0984420 commit cf246e5
Show file tree
Hide file tree
Showing 9 changed files with 291 additions and 17 deletions.
Binary file modified occupi-backend/.dev.env.gpg
Binary file not shown.
Binary file modified occupi-backend/.env.gpg
Binary file not shown.
Binary file modified occupi-backend/.prod.env.gpg
Binary file not shown.
9 changes: 9 additions & 0 deletions occupi-backend/configs/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,12 @@ func GetSessionSecret() string {
}
return secret
}

func GetOccupiDomains() []string {
domains := os.Getenv("OCCUPI_DOMAINS")
if domains != "" {
domainList := strings.Split(domains, ",")
return domainList
}
return []string{""}
}
11 changes: 9 additions & 2 deletions occupi-backend/pkg/authenticator/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,15 @@ type Claims struct {
}

// GenerateToken generates a JWT token for the user
func GenerateToken(email string, role string) (string, time.Time, error) {
expirationTime := time.Now().Add(5 * time.Minute)
func GenerateToken(email string, role string, optionalExpiryTime ...time.Duration) (string, time.Time, error) {
var expirationTime time.Time

if len(optionalExpiryTime) == 0 {
expirationTime = time.Now().Add(24 * 7 * time.Hour)
} else {
expirationTime = time.Now().Add(optionalExpiryTime[0])
}

claims := &Claims{
Email: email,
Role: role,
Expand Down
15 changes: 12 additions & 3 deletions occupi-backend/pkg/handlers/auth_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"net/http"
"time"

"github.com/COS301-SE-2024/occupi/occupi-backend/configs"
"github.com/COS301-SE-2024/occupi/occupi-backend/pkg/authenticator"
"github.com/COS301-SE-2024/occupi/occupi-backend/pkg/constants"
"github.com/COS301-SE-2024/occupi/occupi-backend/pkg/database"
Expand Down Expand Up @@ -149,7 +150,7 @@ func Login(ctx *gin.Context, appsession *models.AppSession, role string) {
if role == constants.Admin {
token, expirationTime, err = authenticator.GenerateToken(requestUser.Email, constants.Admin)
} else {
token, expirationTime, err = authenticator.GenerateToken(requestUser.Email, "user")
token, expirationTime, err = authenticator.GenerateToken(requestUser.Email, constants.Basic)
}

if err != nil {
Expand Down Expand Up @@ -389,7 +390,7 @@ func ResetPassword(ctx *gin.Context, appsession *models.AppSession) {
// this will contain reset password logic
}

// handler for logging out a user on occupi /auth/logout TODO: complete implementation
// handler for logging out a user
func Logout(ctx *gin.Context) {
session := sessions.Default(ctx)
session.Clear()
Expand All @@ -399,7 +400,15 @@ func Logout(ctx *gin.Context) {
return
}

ctx.SetCookie("token", "", -1, "/", "localhost", false, true)
// List of domains to clear cookies from
domains := configs.GetOccupiDomains()

// Iterate over each domain and clear the "token" and "occupi-sessions-store" cookies
for _, domain := range domains {
ctx.SetCookie("token", "", -1, "/", domain, false, true)
ctx.SetCookie("occupi-sessions-store", "", -1, "/", domain, false, true)
}

ctx.JSON(http.StatusOK, utils.SuccessResponse(
http.StatusOK,
"Logged out successfully!",
Expand Down
42 changes: 36 additions & 6 deletions occupi-backend/pkg/middleware/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,38 @@ func ProtectedRoute(ctx *gin.Context) {
http.StatusUnauthorized,
"Bad Request",
constants.InvalidAuthCode,
"User not authorized",
"Invalid token",
nil))
ctx.Abort()
return
}

// check if email and role session variables are set
session := sessions.Default(ctx)
session.Set("email", claims.Email)
session.Set("role", claims.Role)
if err := session.Save(); err != nil {
ctx.JSON(http.StatusInternalServerError, utils.InternalServerError())
logrus.Error(err)
if session.Get("email") == nil || session.Get("role") == nil {
session.Set("email", claims.Email)
session.Set("role", claims.Role)
if err := session.Save(); err != nil {
ctx.JSON(http.StatusInternalServerError, utils.InternalServerError())
logrus.Error(err)
ctx.Abort()
return
}
}

// check that session variables and token claims match
if session.Get("email") != claims.Email || session.Get("role") != claims.Role {
ctx.JSON(http.StatusUnauthorized,
utils.ErrorResponse(
http.StatusUnauthorized,
"Bad Request",
constants.InvalidAuthCode,
"Inalid auth session",
nil))
ctx.Abort()
return
}

ctx.Next()
}

Expand All @@ -77,6 +94,19 @@ func UnProtectedRoute(ctx *gin.Context) {
}
}

// check if email and role session variables are set
session := sessions.Default(ctx)
if session.Get("email") != nil || session.Get("role") != nil {
session.Delete("email")
session.Delete("role")
if err := session.Save(); err != nil {
ctx.JSON(http.StatusInternalServerError, utils.InternalServerError())
logrus.Error(err)
ctx.Abort()
return
}
}

ctx.Next()
}

Expand Down
29 changes: 26 additions & 3 deletions occupi-backend/tests/authenticator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ func TestGenerateToken(t *testing.T) {
t.Fatal("Error loading .env file: ", err)
}

email := "test@example.com"
email := "test1@example.com"
role := constants.Admin
tokenString, expirationTime, err := authenticator.GenerateToken(email, role)

require.NoError(t, err)
require.NotEmpty(t, tokenString)
require.WithinDuration(t, time.Now().Add(5*time.Minute), expirationTime, time.Second)
require.WithinDuration(t, time.Now().Add(24*7*time.Hour), expirationTime, time.Second)

// Validate the token
claims, err := authenticator.ValidateToken(tokenString)
Expand All @@ -40,7 +40,7 @@ func TestValidateToken(t *testing.T) {
t.Fatal("Error loading .env file: ", err)
}

email := "test@example.com"
email := "test2@example.com"
role := constants.Admin
tokenString, _, err := authenticator.GenerateToken(email, role)

Expand All @@ -60,3 +60,26 @@ func TestValidateToken(t *testing.T) {
require.Error(t, err)
assert.Nil(t, claims)
}

func TestValidateTokenExpired(t *testing.T) {
// Load environment variables from .env file
if err := godotenv.Load("../.env"); err != nil {
t.Fatal("Error loading .env file: ", err)
}

email := "[email protected]"
role := constants.Admin

// Generate a token that expires in 1 second
tokenString, _, err := authenticator.GenerateToken(email, role, 1*time.Second)
require.NoError(t, err)
require.NotEmpty(t, tokenString)

// Wait for the token to expire
time.Sleep(2 * time.Second)

// Validate the token
claims, err := authenticator.ValidateToken(tokenString)
require.Error(t, err)
assert.Nil(t, claims)
}
Loading

0 comments on commit cf246e5

Please sign in to comment.