diff --git a/client/package-lock.json b/client/package-lock.json index b66816c..3b89572 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -10,14 +10,17 @@ "license": "MIT", "dependencies": { "@tanstack/react-query": "^5.56.1", + "js-cookie": "^3.0.5", "lucide-react": "^0.440.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.26.2", + "react-tooltip": "^5.28.0", "sonner": "^1.5.0" }, "devDependencies": { "@eslint/js": "^9.9.0", + "@types/js-cookie": "^3.0.6", "@types/node": "^22.5.4", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", @@ -562,6 +565,31 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz", + "integrity": "sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.7" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.10", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.10.tgz", + "integrity": "sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.7" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz", + "integrity": "sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==", + "license": "MIT" + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1231,6 +1259,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/js-cookie": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz", + "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.5.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", @@ -1823,6 +1858,12 @@ "node": ">= 6" } }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2630,6 +2671,15 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3316,6 +3366,20 @@ "react-dom": ">=16.8" } }, + "node_modules/react-tooltip": { + "version": "5.28.0", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.28.0.tgz", + "integrity": "sha512-R5cO3JPPXk6FRbBHMO0rI9nkUG/JKfalBSQfZedZYzmqaZQgq7GLzF8vcCWx6IhUCKg0yPqJhXIzmIO5ff15xg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.6.1", + "classnames": "^2.3.0" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/client/package.json b/client/package.json index 0f0da5c..4997937 100644 --- a/client/package.json +++ b/client/package.json @@ -16,14 +16,17 @@ }, "dependencies": { "@tanstack/react-query": "^5.56.1", + "js-cookie": "^3.0.5", "lucide-react": "^0.440.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.26.2", + "react-tooltip": "^5.28.0", "sonner": "^1.5.0" }, "devDependencies": { "@eslint/js": "^9.9.0", + "@types/js-cookie": "^3.0.6", "@types/node": "^22.5.4", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", diff --git a/client/src/App.tsx b/client/src/App.tsx index 2d77a30..d0f97cd 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,5 +1,6 @@ import { Route, Routes } from "react-router-dom"; +import ProtectedGuard from "@/guards/protected-guard"; import { AppRoutes } from "@/constants/routes"; import SignUpPage from "@/pages/sign-up/page"; import SignInPage from "@/pages/sign-in/page"; @@ -7,9 +8,11 @@ import RootPage from "@/pages/root/page"; function App() { return ( -
+
- } /> + }> + } /> + } /> } /> diff --git a/client/src/components/input.tsx b/client/src/components/input.tsx index b74a098..be62024 100644 --- a/client/src/components/input.tsx +++ b/client/src/components/input.tsx @@ -5,10 +5,12 @@ import { useState, } from "react"; import { Eye, EyeOff, Info, LucideProps } from "lucide-react"; +import { Tooltip } from "react-tooltip"; interface InputProps { name?: string; label: string; + tooltip: string; placeholder: string; type: React.HTMLInputTypeAttribute | undefined; autoComplete: React.HTMLInputAutoCompleteAttribute | undefined; @@ -18,11 +20,13 @@ interface InputProps { disabled?: boolean; validation: (value: string) => boolean; required?: boolean; + maxLength?: number; } const Input = ({ name, label, + tooltip, placeholder, type, autoComplete, @@ -30,6 +34,7 @@ const Input = ({ disabled, validation, required, + maxLength, }: InputProps) => { const [value, setValue] = useState(""); const [obscure, setObscure] = useState(true); @@ -51,7 +56,20 @@ const Input = ({ > {label} - + +
setObscure(!obscure)} - className="absolute inset-y-0 right-0 flex-center pr-3" + className="absolute inset-y-0 right-0 flex-center pr-3 active:scale-90 transition hover:drop-shadow-primary-glow" > {obscure ? ( diff --git a/client/src/components/loading.tsx b/client/src/components/loading.tsx new file mode 100644 index 0000000..22ae20b --- /dev/null +++ b/client/src/components/loading.tsx @@ -0,0 +1,16 @@ +import { Loader2 } from "lucide-react"; + +const Loading = () => { + return ( +
+
+ +
+
+ ); +}; + +export { Loading }; diff --git a/client/src/components/or.tsx b/client/src/components/or.tsx index 13dcdc1..8d7a48a 100644 --- a/client/src/components/or.tsx +++ b/client/src/components/or.tsx @@ -1,9 +1,9 @@ const Or = () => { return ( -
- -

Or

- +
+ +

Or

+
); }; diff --git a/client/src/components/switch-auth.tsx b/client/src/components/switch-auth.tsx index 9ef91eb..55fae3a 100644 --- a/client/src/components/switch-auth.tsx +++ b/client/src/components/switch-auth.tsx @@ -8,7 +8,7 @@ interface SwitchAuthProps { const SwitchAuth = ({ label, tag, href }: SwitchAuthProps) => { return ( -
+

{label}

{loading ? : label} diff --git a/client/src/constants/collections.ts b/client/src/constants/collections.ts index 4be1ddf..9e9f811 100644 --- a/client/src/constants/collections.ts +++ b/client/src/constants/collections.ts @@ -11,37 +11,48 @@ export const SIGNUPINPUTS = [ { name: "username", label: "Username", + tooltip: "Choose a username between 3 and 20 characters.", placeholder: "e.g. john doe", type: "text", autoComplete: "username", suffixIcon: User, validation: ValidateUsername, + maxLength: 20, }, { name: "email", label: "Email Address", + tooltip: + "Please enter a valid email address. Ensure it includes an '@' symbol and a domain name.", placeholder: "e.g. john@domain.com", type: "email", autoComplete: "email", suffixIcon: Mail, validation: ValidateEmail, + maxLength: 254, }, { name: "password", label: "Password", + tooltip: + "Create a strong password with at least 8 characters. Include a mix of uppercase letters, lowercase letters, numbers, and special characters for better security.", placeholder: "e.g. m#P52s@ap$V", type: "password", autoComplete: "new-password", suffixIcon: Lock, validation: ValidatePassword, + maxLength: 128, }, { label: "Confirm Password", + tooltip: + "Re-enter your password to confirm it matches the one you typed above. Ensure both passwords are identical.", placeholder: "e.g. m#P52s@ap$V", type: "password", autoComplete: "new-password", suffixIcon: Lock, validation: ValidateConfirmPassword, + maxLength: 128, }, ]; @@ -49,19 +60,25 @@ export const SIGNININPUTS = [ { name: "email", label: "Email Address", + tooltip: + "Please enter a valid email address. Ensure it includes an '@' symbol and a domain name.", placeholder: "e.g. john@domain.com", type: "email", autoComplete: "email", suffixIcon: Mail, validation: ValidateEmail, + maxLength: 254, }, { name: "password", label: "Password", + tooltip: + "Create a strong password with at least 8 characters. Include a mix of uppercase letters, lowercase letters, numbers, and special characters for better security.", placeholder: "e.g. m#P52s@ap$V", type: "password", autoComplete: "new-password", suffixIcon: Lock, validation: ValidatePassword, + maxLength: 128, }, ]; diff --git a/client/src/constants/keys.ts b/client/src/constants/keys.ts index 847bddf..169a40a 100644 --- a/client/src/constants/keys.ts +++ b/client/src/constants/keys.ts @@ -1,3 +1,4 @@ export const SIGNUPKEY = "sign-up"; export const SIGNINKEY = "sign-in"; export const SIGNOUTKEY = "sign-out"; +export const VERIFYTOKENKEY = "verify-token"; diff --git a/client/src/constants/routes.ts b/client/src/constants/routes.ts index 1af9252..0143113 100644 --- a/client/src/constants/routes.ts +++ b/client/src/constants/routes.ts @@ -6,4 +6,5 @@ export enum AppRoutes { ForgotPassword = "/forgot-password", ResetPassword = "/reset-password", VerifyEmail = "/verify-email", + VerifyToken = "/verify-token", } diff --git a/client/src/guards/protected-guard.tsx b/client/src/guards/protected-guard.tsx new file mode 100644 index 0000000..4a3e37d --- /dev/null +++ b/client/src/guards/protected-guard.tsx @@ -0,0 +1,34 @@ +import { Navigate, Outlet } from "react-router-dom"; +import { useQuery } from "@tanstack/react-query"; +import Cookies from "js-cookie"; + +import { AuthService } from "@/lib/services/auth-service"; +import { VERIFYTOKENKEY } from "@/constants/keys"; +import { AppRoutes } from "@/constants/routes"; +import { Loading } from "@/components/loading"; + +const ProtectedGuard = () => { + const token = Cookies.get("token"); + + const { isLoading, isError } = useQuery({ + queryKey: [VERIFYTOKENKEY], + queryFn: AuthService.verifyToken, + enabled: !!token, + }); + + if (!token) { + return ; + } + + if (isLoading) { + return ; + } + + if (isError) { + return ; + } + + return ; +}; + +export default ProtectedGuard; diff --git a/client/src/index.css b/client/src/index.css index f12b33b..b868319 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -10,7 +10,6 @@ --background: 242 253 251; --primary: 38 220 212; --secondary: 235 158 132; - --secondary-fade: rgba(235, 158, 132, 0.2); --accent: 187 230 96; } @@ -19,7 +18,6 @@ --background: 2 13 11; --primary: 35 215 206; --secondary: 123 46 20; - --secondary-fade: rgba(123, 46, 20, 0.2); --accent: 116 159 25; } @@ -36,18 +34,15 @@ width: 4px; } ::-webkit-scrollbar-track { - background-color: var(--secondary-fade); - opacity: 0.2; + background-color: rgb(var(--primary) / 0.25); } ::-webkit-scrollbar-thumb { - background-color: var(--secondary); - opacity: 0.8; + background-color: rgb(var(--primary) / 0.8); border-radius: 9999px; transition: all 5s ease-out; } ::-webkit-scrollbar-thumb:hover { - background-color: var(--secondary); - opacity: 1; + background-color: rgb(var(--primary)); } } diff --git a/client/src/lib/services/auth-service.ts b/client/src/lib/services/auth-service.ts index a2ade0c..c454281 100644 --- a/client/src/lib/services/auth-service.ts +++ b/client/src/lib/services/auth-service.ts @@ -9,14 +9,22 @@ const baseURL = export const AuthService = { signUp: async (signUp: SignUpDTO) => { - return await fetch(`${baseURL}${AppRoutes.SignUp}`, { + const res = await fetch(`${baseURL}${AppRoutes.SignUp}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(signUp), }); + + const data = await res.json(); + + if (!res.ok) { + throw new Error(data.error); + } + + return data; }, signIn: async (signIn: SignInDTO) => { - return await fetch(`${baseURL}${AppRoutes.SignIn}`, { + const res = await fetch(`${baseURL}${AppRoutes.SignIn}`, { method: "POST", credentials: "include", headers: { @@ -24,10 +32,41 @@ export const AuthService = { }, body: JSON.stringify(signIn), }); + + const data = await res.json(); + + if (!res.ok) { + throw new Error(data.error); + } + + return data; }, signOut: async () => { - return await fetch(`${baseURL}${AppRoutes.SignOut}`, { + const res = await fetch(`${baseURL}${AppRoutes.SignOut}`, { method: "POST", + credentials: "include", + }); + + const data = await res.json(); + + if (!res.ok) { + throw new Error(data.error); + } + + return data; + }, + verifyToken: async () => { + const res = await fetch(`${baseURL}${AppRoutes.VerifyToken}`, { + method: "GET", + credentials: "include", }); + + const data = await res.json(); + + if (!res.ok) { + throw new Error(data.error); + } + + return data; }, }; diff --git a/client/src/pages/sign-in/_components/sign-in-form.tsx b/client/src/pages/sign-in/_components/sign-in-form.tsx index 7e5a3c5..d6cf3ce 100644 --- a/client/src/pages/sign-in/_components/sign-in-form.tsx +++ b/client/src/pages/sign-in/_components/sign-in-form.tsx @@ -6,9 +6,9 @@ import { toast } from "sonner"; import { AuthService } from "@/lib/services/auth-service"; import { SIGNININPUTS } from "@/constants/collections"; import { SignInDTO } from "@/lib/DTO/sign-in.dto"; +import { Button } from "@/components/text-button"; import { AppRoutes } from "@/constants/routes"; import { SIGNINKEY } from "@/constants/keys"; -import { Button } from "@/components/text-button"; import { Input } from "@/components/input"; const SignInForm = () => { @@ -21,7 +21,7 @@ const SignInForm = () => { toast.success("Welcome! You have signed in"); navigate(AppRoutes.Root); }, - onError: ({ message }) => toast.error(message || "Unable to login"), + onError: ({ message }) => toast.error(message || "Unable to sign in"), }); const onSubmit = (e: FormEvent) => { @@ -37,9 +37,9 @@ const SignInForm = () => { return (
-

+

Access Your Account

diff --git a/client/src/pages/sign-in/page.tsx b/client/src/pages/sign-in/page.tsx index a7187fd..f62d12c 100644 --- a/client/src/pages/sign-in/page.tsx +++ b/client/src/pages/sign-in/page.tsx @@ -6,7 +6,7 @@ import { SignInForm } from "./_components/sign-in-form"; const SignInPage = () => { return ( -
+
diff --git a/client/src/pages/sign-up/_components/sign-up-form.tsx b/client/src/pages/sign-up/_components/sign-up-form.tsx index c04a29b..e713ed4 100644 --- a/client/src/pages/sign-up/_components/sign-up-form.tsx +++ b/client/src/pages/sign-up/_components/sign-up-form.tsx @@ -6,9 +6,9 @@ import { toast } from "sonner"; import { AuthService } from "@/lib/services/auth-service"; import { SIGNUPINPUTS } from "@/constants/collections"; import { SignUpDTO } from "@/lib/DTO/sign-up.dto"; +import { Button } from "@/components/text-button"; import { AppRoutes } from "@/constants/routes"; import { SIGNUPKEY } from "@/constants/keys"; -import { Button } from "@/components/text-button"; import { Input } from "@/components/input"; const SignUpForm = () => { @@ -38,9 +38,9 @@ const SignUpForm = () => { return ( -

+

Create Account

diff --git a/client/src/pages/sign-up/page.tsx b/client/src/pages/sign-up/page.tsx index 162aa4b..38efb87 100644 --- a/client/src/pages/sign-up/page.tsx +++ b/client/src/pages/sign-up/page.tsx @@ -6,7 +6,7 @@ import { SignUpForm } from "./_components/sign-up-form"; const SignUpPage = () => { return ( -
+
diff --git a/controllers/auth_controller.go b/controllers/auth_controller.go index 26e3f3d..fdecab8 100644 --- a/controllers/auth_controller.go +++ b/controllers/auth_controller.go @@ -175,14 +175,15 @@ func ResendVerify(c fiber.Ctx) error { } func VerifyToken(c fiber.Ctx) error { - claims, err := utils.ParseCookieToken(c) - if err != nil { - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Unauthorized"}) + id := c.Locals("id").(string) + + if id == "" { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid id"}) } var user models.User - res := database.Instance.Where("id = ?", claims.Issuer).First(&user) + res := database.Instance.Where("id = ?", id).First(&user) if res.Error != nil { if res.Error == gorm.ErrRecordNotFound { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"}) diff --git a/middlewares/verify-token.go b/middlewares/verify-token.go new file mode 100644 index 0000000..a38958b --- /dev/null +++ b/middlewares/verify-token.go @@ -0,0 +1,44 @@ +package middlewares + +import ( + "log" + "os" + "time" + + "github.com/gofiber/fiber/v3" + "github.com/golang-jwt/jwt/v5" +) + +func VerifyToken(c fiber.Ctx) error { + tokenString := c.Cookies("token") + + if tokenString == "" { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "No token found"}) + } + + SECRET_KEY := os.Getenv("SECRET_KEY") + if SECRET_KEY == "" { + log.Fatal("SECRET_KEY must be set") + } + + token, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) { + return []byte(SECRET_KEY), nil + }) + + if err != nil { + return err + } + + claims, ok := token.Claims.(*jwt.RegisteredClaims) + if !ok { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid token claims"}) + } + + if time.Now().After(claims.ExpiresAt.Time) { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid or expired token"}) + } + + c.Locals("id", claims.Issuer) + + return c.Next() +} diff --git a/routes/auth_route.go b/routes/auth_route.go index 30220c6..04910e7 100644 --- a/routes/auth_route.go +++ b/routes/auth_route.go @@ -5,6 +5,7 @@ import ( "github.com/Fingertips18/go-auth/constants" "github.com/Fingertips18/go-auth/controllers" + "github.com/Fingertips18/go-auth/middlewares" "github.com/gofiber/fiber/v3" ) @@ -14,7 +15,7 @@ func UseAuthRoute(app *fiber.App) { app.Post(fmt.Sprintf("%s%s", constants.AUTH, constants.SIGNOUT), controllers.SignOut) app.Post(fmt.Sprintf("%s%s", constants.AUTH, constants.VERIFYEMAIL), controllers.VerifyEmail) app.Post(fmt.Sprintf("%s%s", constants.AUTH, constants.RESENDVERIFY), controllers.ResendVerify) - app.Get(fmt.Sprintf("%s%s", constants.AUTH, constants.VERIFYTOKEN), controllers.VerifyToken) + app.Get(fmt.Sprintf("%s%s", constants.AUTH, constants.VERIFYTOKEN), controllers.VerifyToken, middlewares.VerifyToken) app.Post(fmt.Sprintf("%s%s", constants.AUTH, constants.FORGOTPASSWORD), controllers.ForgotPassword) app.Post(fmt.Sprintf("%s%s/:token", constants.AUTH, constants.RESETPASSWORD), controllers.ResetPassword) } diff --git a/utils/session_token.go b/utils/session_token.go index 61dd8d9..832648a 100644 --- a/utils/session_token.go +++ b/utils/session_token.go @@ -77,34 +77,6 @@ func ClearCookieToken(c fiber.Ctx) { c.Cookie(&cookie) } -func ParseCookieToken(c fiber.Ctx) (*jwt.RegisteredClaims, error) { - tokenString := c.Cookies("token") - - SECRET_KEY := os.Getenv("SECRET_KEY") - if SECRET_KEY == "" { - log.Fatal("SECRET_KEY must be set") - } - - token, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) { - return []byte(SECRET_KEY), nil - }) - - if err != nil { - return nil, err - } - - claims, ok := token.Claims.(*jwt.RegisteredClaims) - if !ok { - return nil, fiber.NewError(fiber.StatusUnauthorized, "Invalid token claims") - } - - if time.Now().After(claims.ExpiresAt.Time) { - return nil, fiber.NewError(fiber.StatusUnauthorized, "Invalid token") - } - - return claims, nil -} - func GenerateResetToken() (*string, error) { byteToken := make([]byte, 16) _, err := rand.Read(byteToken) diff --git a/utils/session_token_test.go b/utils/session_token_test.go index f340f3e..8057e16 100644 --- a/utils/session_token_test.go +++ b/utils/session_token_test.go @@ -13,7 +13,6 @@ import ( "time" "github.com/gofiber/fiber/v3" - "github.com/golang-jwt/jwt/v5" ) type JWTHeader struct { @@ -167,37 +166,6 @@ func TestToken(t *testing.T) { } }) - t.Run("ParseCookieToken_Validate", func(t *testing.T) { - app, token := _HandleCookieToken(t, id, username) - - // Simulate request to parse cookie token - req := httptest.NewRequest(http.MethodGet, "/parse-cookie", nil) - req.AddCookie(&http.Cookie{Name: "token", Value: token}) - resp, err := app.Test(req) - if err != nil { - t.Fatalf("Error making parse cookie request: %v", err) - } - - var body struct { - Claims *jwt.RegisteredClaims `json:"claims"` - Error string `json:"error"` - } - - if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { - t.Fatalf("Error decoding response body: %v", err) - } - - if resp.StatusCode == fiber.StatusUnauthorized { - t.Fatalf("Error unable to parse cookie token: %v", body.Error) - } - - if body.Claims.Issuer != id { - t.Errorf("Expected issuer to be %s but got %s", id, body.Claims.Issuer) - } else { - t.Logf("Issuer is %s", body.Claims.Issuer) - } - }) - t.Run("GenerateResetToken_Validate", func(t *testing.T) { token, err := GenerateResetToken() if err != nil { @@ -241,14 +209,5 @@ func _HandleCookieToken(t *testing.T, id string, username string) (*fiber.App, s return c.SendString("Cookies cleared!") }) - // Define dummy route to parse cookie token - app.Get("/parse-cookie", func(c fiber.Ctx) error { - claims, err := ParseCookieToken(c) - if err != nil { - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": err.Error()}) - } - return c.JSON(fiber.Map{"claims": claims}) - }) - return app, token }