package auth import ( "context" "fmt" "log" "net/http" "time" "github.com/golang-jwt/jwt/v5" "github.com/jmoiron/sqlx" "sismedika.com/sas/westone/configs" "sismedika.com/sas/westone/types" "sismedika.com/sas/westone/utils" ) type ContextKey string const UserContextKey ContextKey = "mk1S2sKM12KASd02dp1" // func WithJWTAuth(handlerFunc http.HandlerFunc, store types.UserStore) http.HandlerFunc { // return func(w http.ResponseWriter, r *http.Request) { // tokenString := utils.GetTokenFromRequest(r) // token, err := validateJWT(tokenString) // if err != nil { // log.Printf("failed to validate token: %v", err) // permissionDenied(w) // return // } // if !token.Valid { // log.Println("invalid token") // permissionDenied(w) // return // } // claims := token.Claims.(jwt.MapClaims) // str := claims["userID"].(string) // userID, err := strconv.Atoi(str) // if err != nil { // log.Printf("failed to convert userID to int: %v", err) // permissionDenied(w) // return // } // u, err := store.GetUserByID(userID) // if err != nil { // log.Printf("failed to get user by id: %v", err) // permissionDenied(w) // return // } // // Add the user to the context // ctx := r.Context() // ctx = context.WithValue(ctx, UserKey, u.MUserID) // r = r.WithContext(ctx) // // Call the function if the token is valid // handlerFunc(w, r) // } // } func CreateJWT(data types.DataJWT) (string, error) { secret := []byte(configs.Envs.JWTSecret) token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "M_UserID": data.M_UserID, "M_UserEmail": data.M_UserEmail, "M_UserName": data.M_UserName, "M_UserGroupDashboard": data.M_UserGroupDashboard, "M_UserDefaultTSampleStationID": data.M_UserDefaultTSampleStationID, "M_StaffName": data.M_StaffName, "Is_Courier": data.Is_Courier, "Time_Autologout": data.Time_Autologout, "Type_Akun": data.Type_Akun, "IP": data.Ip, "Agent": data.Agent, "Version": data.Version, "LastLogin": data.LastLogin, }) tokenString, err := token.SignedString(secret) if err != nil { return "", err } return tokenString, err } func validateJWT(tokenString string) (*jwt.Token, error) { return jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { err := fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) fmt.Printf("Error validating JWT token: %v\n", err) return nil, err } return []byte(configs.Envs.JWTSecret), nil }) } func permissionDenied(w http.ResponseWriter) { utils.WriteError(w, http.StatusForbidden, fmt.Errorf("PERMISSION DENIED")) } func tokenExpired(w http.ResponseWriter) { utils.WriteError(w, http.StatusForbidden, fmt.Errorf("TOKEN EXPIRED")) } func GetUserIDFromContext(ctx context.Context) int { userID, ok := ctx.Value(UserContextKey).(int) if !ok { return -1 } return userID } func AuthMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { tokenstr := utils.GetTokenFromRequest(r) token, err := validateJWT(tokenstr) if err != nil { log.Printf("[ERROR] Failed to validate jwt token: %v", err) permissionDenied(w) return } if !token.Valid { log.Println("[ERROR] Invalid token") permissionDenied(w) return } claims := token.Claims.(jwt.MapClaims) ctx := r.Context() ctx = context.WithValue(ctx, UserContextKey, claims) r = r.WithContext(ctx) next.ServeHTTP(w, r) }) } func WithAuthStore(store types.OauthStore) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := context.WithValue(r.Context(), "auth_store", store) //start tokenstr := utils.GetTokenFromRequest(r) token, err := validateJWT(tokenstr) if err != nil { log.Printf("[ERROR] Failed to validate jwt token: %v", err) permissionDenied(w) return } if !token.Valid { log.Println("[ERROR] Invalid token") permissionDenied(w) return } // claims := token.Claims.(jwt.MapClaims) // userID := int(claims["M_UserID"].(float64)) // Ambil store dari context // fmt.Printf("store: %v", store) // store, ok := r.Context().Value("auth_store").(types.OauthStore) // if !ok { // log.Println("[ERROR] Auth store not found in context") // permissionDenied(w) // return // } // Update expired token if err := store.UpdateExpiredToken(tokenstr); err != nil { if err.Error() == "token expired" { tokenExpired(w) } else { log.Printf("[ERROR] Failed to update token expiry: %v", err) permissionDenied(w) } return } //end next.ServeHTTP(w, r.WithContext(ctx)) }) } } func UpdateExpiredTokentest(userID int, db *sqlx.DB) error { tx, err := db.BeginTxx(context.Background(), nil) if err != nil { return fmt.Errorf("failed to begin transaction: %v", err) } defer func() { if err != nil { tx.Rollback() } }() qrySys := `SELECT Conf_SystemIsAutoLogOut, Conf_SystemAutoLogOutDuration FROM conf_system LIMIT 1` var isAutoLogOut string var autoLogOutDuration int err = db.QueryRow(qrySys).Scan(&isAutoLogOut, &autoLogOutDuration) if err != nil { return fmt.Errorf("failed to get system config: %v", err) } if isAutoLogOut == "Y" { qryEt := `SELECT DATE_FORMAT(M_UserExpiredToken, '%Y-%m-%d %H:%i:%s') as M_UserExpiredToken FROM m_user WHERE M_UserID = ?` var expiredToken string err = db.Get(&expiredToken, qryEt, userID) if err != nil { return fmt.Errorf("failed to get expired token: %v", err) } timeNow := time.Now() expiredTime, err := time.Parse("2006-01-02 15:04:05", expiredToken) if err != nil { return fmt.Errorf("failed to parse expired token: %v", err) } if timeNow.After(expiredTime) { return fmt.Errorf("token expired") } timeNowAdd := timeNow.Add(time.Duration(autoLogOutDuration) * time.Minute) qryUpdateExpiredToken := `UPDATE m_user SET M_UserExpiredToken = ? WHERE M_UserID = ?` _, err = tx.Exec(qryUpdateExpiredToken, timeNowAdd.Format("2006-01-02 15:04:05"), userID) if err != nil { return fmt.Errorf("failed to update expired token: %v", err) } qry := `UPDATE m_user SET M_UserExpiredToken = NOW() WHERE M_UserID = :userID` _, err = tx.NamedExec(qry, map[string]interface{}{ "userID": userID, }) fmt.Println("err", err) if err != nil { return fmt.Errorf("failed to update expired token: %v", err) } if err = tx.Commit(); err != nil { return fmt.Errorf("failed to commit transaction: %v", err) } } return nil } // Middleware untuk menyimpan store ke context func WithStore(store *Store) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := context.WithValue(r.Context(), "store", store) next.ServeHTTP(w, r.WithContext(ctx)) }) } }