-
- {/* Header */}
-
-
-
- Create Account
-
-
- Join us and get started
-
-
+ {/* Confirm Password Field */}
+
+
+
+
+
+
+ {errors.confirmPassword && (
+
{errors.confirmPassword}
+ )}
+
- {/* Signup Form */}
-
-
-
-
+ {/* Login link */}
+
+
+ Already have an account?{" "}
+ {
+ if (isLoading || isAuthenticated) {
+ e.preventDefault();
+ }
+ }}
+ >
+ Login
+
+
- );
+
+
+ );
}
\ No newline at end of file
diff --git a/savebook/app/api/auth/login/route.js b/savebook/app/api/auth/login/route.js
index 79f88dc..d0c9cf6 100644
--- a/savebook/app/api/auth/login/route.js
+++ b/savebook/app/api/auth/login/route.js
@@ -1,8 +1,9 @@
import { NextResponse } from "next/server";
import dbConnect from "@/lib/db/mongodb";
import User from "@/lib/models/User";
-import { generateAuthToken } from "@/lib/utils/jwtAuth";
+import { generateAuthToken } from "@/lib/utils/jwtAuth"; // ensure lowercase filename
import { generateRecoveryCodes } from "@/lib/utils/recoveryCodes";
+import bcrypt from "bcryptjs";
export async function POST(request) {
try {
@@ -22,35 +23,27 @@ export async function POST(request) {
);
}
- const user = await User.findOne({ username });
-
-const isPasswordValid = user
- ? await user.comparePassword(password)
- : false;
-
-if (!user || !isPasswordValid) {
- return NextResponse.json(
- { success: false, message: "Invalid username or password" },
- { status: 401 }
- );
-}
+ // Find user
+ const user = await User.findOne({ username }).select("+password");
+ if (!user) {
+ return NextResponse.json(
+ { success: false, message: "Invalid Username! Try Again!" },
+ { status: 401 }
+ );
+ }
+ // Verify password
+ const isPasswordValid = await user.comparePassword(password);
+ if (!isPasswordValid) {
+ return NextResponse.json(
+ { error: 'Invalid username or password' },
+ { status: 401 }
+ );
+ }
- // Generate auth token
+ // Generate token and set cookie
const { authToken } = await generateAuthToken(user._id.toString());
- // FIRST LOGIN: generate recovery codes
- let recoveryCodes = null;
- if (!user.recoveryCodes || user.recoveryCodes.length === 0) {
- const generated = generateRecoveryCodes(8);
-
- user.recoveryCodes = generated.hashedCodes;
- await user.save();
-
- // Plain codes only sent once
- recoveryCodes = generated.plainCodes;
- }
-
const response = NextResponse.json(
{
success: true,
@@ -66,12 +59,13 @@ if (!user || !isPasswordValid) {
// only present on first login
recoveryCodes,
},
- message: "Login successful",
+ message: "Login successful"
},
{ status: 200 }
);
-
- response.cookies.set("authToken", authToken, {
+
+ // Set cookie only on success
+ response.cookies.set('authToken', authToken, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
@@ -81,7 +75,6 @@ if (!user || !isPasswordValid) {
return response;
} catch (error) {
- console.error("Login error:", error);
return NextResponse.json(
{ success: false, message: "Internal server error" },
{ status: 500 }
diff --git a/savebook/app/api/auth/register/route.js b/savebook/app/api/auth/register/route.js
index 1dc57da..122de98 100644
--- a/savebook/app/api/auth/register/route.js
+++ b/savebook/app/api/auth/register/route.js
@@ -4,44 +4,55 @@ import dbConnect from "@/lib/db/mongodb";
export async function POST(request) {
try {
- await dbConnect();
-
const { username, password } = await request.json();
-
- // β
Input validation
- if (
- !username ||
- !password ||
- typeof username !== "string" ||
- typeof password !== "string" ||
- password.length < 6
- ) {
+
+ // Validate required fields
+ if (!username || !password) {
return NextResponse.json(
- { success: false, message: "Invalid input" },
+ { success: false, message: "All fields are required" },
{ status: 400 }
);
}
- // β
Prevent username enumeration
- const existingUser = await User.findOne({ username });
+ // Password strength check
+ if (password.length < 6) {
+ return NextResponse.json(
+ { success: false, message: "Password must be at least 6 characters" },
+ { status: 400 }
+ );
+ }
- if (existingUser) {
+ // Check if user exists
+ const user = await User.findOne({ username });
+ if (user) {
return NextResponse.json(
- { success: false, message: "Unable to create account" },
+ { success: false, message: "User with this username already exists" },
{ status: 400 }
);
}
+
+ // Try creating user
+ try {
+ await User.create({ username, password });
- // β
Create user
- await User.create({
- username,
- password
- });
+ return NextResponse.json(
+ { success: true, message: "Account created successfully" },
+ { status: 201 }
+ );
+ } catch (err) {
+ // Handle duplicate key error explicitly
+ if (err.code === 11000) {
+ return NextResponse.json(
+ { success: false, message: "Username already taken" },
+ { status: 400 }
+ );
+ }
- return NextResponse.json(
- { success: true, message: "Account created successfully" },
- { status: 201 }
- );
+ return NextResponse.json(
+ { success: false, message: err.message || "Failed to create user" },
+ { status: 500 }
+ );
+ }
} catch (error) {
console.error("Register error:", error);
return NextResponse.json(
diff --git a/savebook/app/api/auth/update-profile/route.js b/savebook/app/api/auth/update-profile/route.js
index c584cf5..224f3f2 100644
--- a/savebook/app/api/auth/update-profile/route.js
+++ b/savebook/app/api/auth/update-profile/route.js
@@ -11,57 +11,86 @@ export async function PUT(request) {
// Get token from cookies
const authtoken = request.cookies.get("authToken");
-
if (!authtoken) {
- return NextResponse.json({ success: false, message: "Unauthorized - No token provided" }, { status: 401 });
+ return NextResponse.json(
+ { success: false, message: "Unauthorized - No token provided" },
+ { status: 401 }
+ );
}
// Verify token
const decoded = verifyJwtToken(authtoken.value);
-
if (!decoded || !decoded.success) {
- return NextResponse.json({ success: false, message: "Unauthorized - Invalid token" }, { status: 401 });
+ return NextResponse.json(
+ { success: false, message: "Unauthorized - Invalid token" },
+ { status: 401 }
+ );
}
-
// Get user ID from token
const userId = new mongoose.Types.ObjectId(decoded.userId);
- // Get updated user data from request
- const { profileImage, firstName, lastName, bio, location } = await request.json();
- // Update user
+ // Get updated user data from request (only safe fields)
+ const {
+ profileImage,
+ firstName,
+ lastName,
+ bio,
+ location,
+ } = await request.json();
+
+ // Update user with validation
const updatedUser = await User.findByIdAndUpdate(
userId,
- {
+ {
...(profileImage !== undefined && { profileImage }),
...(firstName !== undefined && { firstName }),
...(lastName !== undefined && { lastName }),
...(bio !== undefined && { bio }),
...(location !== undefined && { location })
},
- { new: true, select: "-password" } // Return updated user without password
+ {
+ new: true,
+ select: "-password",
+ runValidators: true // enforce schema validation
+ }
);
if (!updatedUser) {
- return NextResponse.json({ success: false, message: "User not found" }, { status: 404 });
+ return NextResponse.json(
+ { success: false, message: "User not found" },
+ { status: 404 }
+ );
}
- // Return success response with updated user data
- return NextResponse.json({
- success: true,
- message: "Profile updated successfully",
- user: {
- username: updatedUser.username,
- profileImage: updatedUser.profileImage,
- firstName: updatedUser.firstName,
- lastName: updatedUser.lastName,
- bio: updatedUser.bio,
- location: updatedUser.location
- }
- }, { status: 200 });
-
+ // Return success response with updated user data (only safe fields)
+ return NextResponse.json(
+ {
+ success: true,
+ message: "Profile updated successfully",
+ user: {
+ username: updatedUser.username,
+ profileImage: updatedUser.profileImage,
+ firstName: updatedUser.firstName,
+ lastName: updatedUser.lastName,
+ bio: updatedUser.bio,
+ location: updatedUser.location
+ }
+ },
+ { status: 200 }
+ );
} catch (error) {
- console.error("Error updating profile:", error);
- return NextResponse.json({ success: false, message: "Internal Server Error" }, { status: 500 });
+ // Handle validation errors explicitly
+ if (error.name === "ValidationError") {
+ return NextResponse.json(
+ { success: false, message: error.message },
+ { status: 400 }
+ );
+ }
+
+ return NextResponse.json(
+ { success: false, message: "Internal Server Error" },
+ { status: 500 }
+ );
}
}
\ No newline at end of file
diff --git a/savebook/app/api/auth/user/route.js b/savebook/app/api/auth/user/route.js
index 97eca38..92c8ba3 100644
--- a/savebook/app/api/auth/user/route.js
+++ b/savebook/app/api/auth/user/route.js
@@ -1,7 +1,7 @@
import dbConnect from "@/lib/db/mongodb";
import User from "@/lib/models/User";
import { verifyJwtToken } from "@/lib/utils/jwtAuth";
-import { NextResponse } from 'next/server';
+import { NextResponse } from "next/server";
export async function GET(request) {
try {
@@ -12,51 +12,34 @@ export async function GET(request) {
const authToken = request.cookies.get('authToken');
const tokenValue = authToken?.value;
- if (!tokenValue) {
- return NextResponse.json(
- { success: false, message: 'Unauthorized - No token provided' },
- { status: 401 }
- );
- }
-
- // Verify token
- const tokenInfo = await verifyJwtToken(tokenValue);
-
- if (!tokenInfo || !tokenInfo.success) {
- return NextResponse.json(
- { success: false, message: 'Unauthorized - Invalid token' },
- { status: 401 }
- );
- }
-
- // Find user by ID but exclude the password
- const user = await User.findById(tokenInfo.userId).select("-password");
-
- if (!user) {
- return NextResponse.json(
- { success: false, message: "User not found" },
- { status: 404 }
- );
- }
-
- // Return user data
- return NextResponse.json({
- success: true,
- user: {
- username: user.username,
- profileImage: user.profileImage,
- firstName: user.firstName,
- lastName: user.lastName,
- bio: user.bio,
- location: user.location
- }
- }, { status: 200 });
-
- } catch (error) {
- console.error("Error fetching user:", error);
- return NextResponse.json(
- { success: false, message: "Internal Server Error" },
- { status: 500 }
- );
+ await dbConnect();
+
+ // Use tokenInfo.userId instead of tokenInfo.data._id
+ const user = await User.findById(tokenInfo.userId).select('-password');
+
+ if (!user) {
+ return NextResponse.json(
+ { success: false, message: 'User not found' },
+ { status: 404 }
+ );
}
+
+ return NextResponse.json({
+ success: true,
+ user: {
+ username: user.username,
+ profileImage: user.profileImage,
+ firstName: user.firstName,
+ lastName: user.lastName,
+ bio: user.bio,
+ location: user.location
+ }
+ });
+ } catch (error) {
+ console.error("Error fetching user:", error);
+ return NextResponse.json(
+ { success: false, message: "Internal Server Error" },
+ { status: 500 }
+ );
+ }
}
\ No newline at end of file
diff --git a/savebook/app/api/notes/[id]/route.js b/savebook/app/api/notes/[id]/route.js
index e861427..2e66baf 100644
--- a/savebook/app/api/notes/[id]/route.js
+++ b/savebook/app/api/notes/[id]/route.js
@@ -1,7 +1,7 @@
-import { NextResponse } from 'next/server';
-import mongoose from 'mongoose';
-import dbConnect from '@/lib/db/mongodb';
-import Notes from '@/lib/models/Notes';
+import { NextResponse } from "next/server";
+import mongoose from "mongoose";
+import dbConnect from "@/lib/db/mongodb";
+import Notes from "@/lib/models/Notes";
import { verifyJwtToken } from "@/lib/utils/jwtAuth";
// Get a specific note by ID
diff --git a/savebook/app/profile/page.js b/savebook/app/profile/page.js
index d1f4beb..1a1483b 100644
--- a/savebook/app/profile/page.js
+++ b/savebook/app/profile/page.js
@@ -1,125 +1,100 @@
"use client";
-import { useState, useEffect } from 'react';
-import { useAuth } from '@/context/auth/authContext';
-import { useRouter } from 'next/navigation';
+
+import { useState, useEffect } from "react";
+import { useAuth } from "@/context/auth/authContext";
+import { useRouter } from "next/navigation";
export default function ProfilePage() {
const { user, loading, checkUserAuthentication } = useAuth();
const router = useRouter();
+
const [formData, setFormData] = useState({
- profileImage: '',
- firstName: '',
- lastName: '',
- bio: '',
- location: ''
+ profileImage: "",
+ firstName: "",
+ lastName: "",
+ bio: "",
+ location: ""
});
- const [imagePreview, setImagePreview] = useState('');
- const [message, setMessage] = useState('');
- const [error, setError] = useState('');
+
+ const [imagePreview, setImagePreview] = useState("");
+ const [message, setMessage] = useState("");
+ const [error, setError] = useState("");
const [isDataLoaded, setIsDataLoaded] = useState(false);
const [isEditing, setIsEditing] = useState(false);
useEffect(() => {
if (user) {
setFormData({
- profileImage: user.profileImage || '',
- firstName: user.firstName || '',
- lastName: user.lastName || '',
- bio: user.bio || '',
- location: user.location || ''
+ profileImage: user.profileImage || "",
+ firstName: user.firstName || "",
+ lastName: user.lastName || "",
+ bio: user.bio || "",
+ location: user.location || ""
});
- setImagePreview(user.profileImage || '');
+ setImagePreview(user.profileImage || "");
setIsDataLoaded(true);
}
}, [user]);
const handleInputChange = (e) => {
const { name, value } = e.target;
- setFormData(prev => ({
- ...prev,
- [name]: value
- }));
+ setFormData((prev) => ({ ...prev, [name]: value }));
};
const handleImageChange = async (e) => {
const file = e.target.files[0];
-
if (file) {
- // Validate file type
- if (!file.type.match('image.*')) {
- setError('Please select an image file');
+ if (!file.type.match("image.*")) {
+ setError("Please select an image file");
return;
}
-
- // Validate file size (max 5MB)
if (file.size > 5 * 1024 * 1024) {
- setError('File size exceeds 5MB limit');
+ setError("File size exceeds 5MB limit");
return;
}
-
- // Show loading state
- setMessage('Uploading image...');
-
- const formData = new FormData();
- formData.append('image', file);
-
+
+ setMessage("Uploading image...");
+ const fd = new FormData();
+ fd.append("image", file);
+
try {
- const response = await fetch('/api/upload', {
- method: 'POST',
- body: formData,
- credentials: 'include'
+ const response = await fetch("/api/upload", {
+ method: "POST",
+ body: fd,
+ credentials: "include"
});
-
const result = await response.json();
-
if (result.success) {
setImagePreview(result.imageUrl);
- setFormData(prev => ({
- ...prev,
- profileImage: result.imageUrl
- }));
- setMessage('Image uploaded successfully!');
- setError(''); // Clear any previous error
-
- // Clear message after 2 seconds
- setTimeout(() => setMessage(''), 2000);
+ setFormData((prev) => ({ ...prev, profileImage: result.imageUrl }));
+ setMessage("Image uploaded successfully!");
+ setError("");
+ setTimeout(() => setMessage(""), 2000);
} else {
- setError(result.message || 'Failed to upload image');
+ setError(result.message || "Failed to upload image");
}
- } catch (err) {
- setError('An error occurred while uploading the image');
- console.error('Image upload error:', err);
+ } catch {
+ setError("An error occurred while uploading the image");
}
}
};
const handleSubmit = async (e) => {
e.preventDefault();
- setMessage('');
- setError('');
+ setMessage("");
+ setError("");
try {
- const response = await fetch('/api/auth/update-profile', {
- method: 'PUT',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- profileImage: formData.profileImage,
- firstName: formData.firstName,
- lastName: formData.lastName,
- bio: formData.bio,
- location: formData.location
- }),
- credentials: 'include'
+ const response = await fetch("/api/auth/update-profile", {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(formData),
+ credentials: "include"
});
-
const data = await response.json();
if (data.success) {
- setMessage('Profile updated successfully!');
-
- // Update form data to reflect the changes immediately
+ setMessage("Profile updated successfully!");
setFormData({
profileImage: data.user.profileImage,
firstName: data.user.firstName,
@@ -127,34 +102,23 @@ export default function ProfilePage() {
bio: data.user.bio,
location: data.user.location
});
-
- // Update image preview
setImagePreview(data.user.profileImage);
-
- // Refresh user data from the server to ensure we have the latest data in context
+
if (checkUserAuthentication) {
await checkUserAuthentication();
}
-
- setTimeout(() => {
- setIsEditing(false);
- }, 500);
-
- setTimeout(() => {
- setMessage(''); // Clear message
- // Optionally redirect after update
- // router.push('/'); // Redirect to home page after successful update
- }, 2000);
+
+ setTimeout(() => setIsEditing(false), 500);
+ setTimeout(() => setMessage(""), 2000);
} else {
- setError(data.message || 'Failed to update profile');
+ setError(data.message || "Failed to update profile");
}
- } catch (err) {
- setError('An error occurred while updating profile');
- console.error('Profile update error:', err);
+ } catch {
+ setError("An error occurred while updating profile");
}
};
- if (loading) {
+ if (loading || !isDataLoaded) {
return (
@@ -163,162 +127,140 @@ export default function ProfilePage() {
}
if (!user) {
- router.push('/login');
+ router.push("/login");
return null;
}
- if (loading || !isDataLoaded) {
- return (
-
- );
- }
-
- const handleEditClick = () => {
- setIsEditing(true);
- };
-
const handleCancelEdit = () => {
setIsEditing(false);
- // Reset form to current user data
if (user) {
setFormData({
- profileImage: user.profileImage || '',
- firstName: user.firstName || '',
- lastName: user.lastName || '',
- bio: user.bio || '',
- location: user.location || ''
+ profileImage: user.profileImage || "",
+ firstName: user.firstName || "",
+ lastName: user.lastName || "",
+ bio: user.bio || "",
+ location: user.location || ""
});
- setImagePreview(user.profileImage || '');
+ setImagePreview(user.profileImage || "");
+ setError("");
+ setMessage("");
}
};
-
+
return (
-
Edit Profile
-
+
+ {isEditing ? "Edit Profile" : "Your Profile"}
+
+
{message && (
-
+
{message}
)}
-
{error && (
-
+
{error}
)}
-
+
{!isEditing ? (
- {/* Profile Preview Card */}
-
-
-
Your Profile
-
+ {/* Avatar + username */}
+
+
+ {user?.profileImage ? (
+

+ ) : (
+
+
+ {user?.username?.charAt(0)?.toUpperCase() || "U"}
+
+
+ )}
+
+
+ {user?.username || "N/A"}
+
+
+
+
+
+
Full Name
+
{user.firstName || "N/A"} {user.lastName || ""}
+
+
+
Location
+
{user.location || "N/A"}
-
-
-
- {user?.profileImage ? (
-

+
Bio
+
{user.bio || "N/A"}
+
+
+
+
+
+
+
+ ) : isDataLoaded ? (
+
- ) : isDataLoaded ? (
-
- ) : (
-
- )}
+ {/* Actions */}
+
+
+
+
+
+ ) : null}
diff --git a/savebook/components/notes/AddNote.js b/savebook/components/notes/AddNote.js
index bde454c..062524c 100644
--- a/savebook/components/notes/AddNote.js
+++ b/savebook/components/notes/AddNote.js
@@ -128,12 +128,6 @@ export default function Addnote() {
setAudioData(null);
};
-
-
-
-
-
-
const handleSaveNote = async (e) => {
e.preventDefault();
if (isSubmitting) return;
@@ -380,7 +374,6 @@ export default function Addnote() {
)}
-
{/* Upload Section */}
diff --git a/savebook/context/auth/AuthState.js b/savebook/context/auth/AuthState.js
index dcdc85d..3f49950 100644
--- a/savebook/context/auth/AuthState.js
+++ b/savebook/context/auth/AuthState.js
@@ -46,18 +46,17 @@ const AuthProvider = ({ children }) => {
};
// Login function
- const login = async (username, password) => {
- try {
- setLoading(true);
- const response = await fetch('/api/auth/login', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ username, password }),
- credentials: 'include' // Important: to store the cookie
- });
+ const login = async (username, password, rememberMe) => {
+ try {
+ setLoading(true);
+ const response = await fetch("/api/auth/login", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ username, password, rememberMe }),
+ credentials: "include", // keep cookie
+ });
+<<<<<<< HEAD
const data = await response.json();
if (data.success) {
@@ -78,7 +77,35 @@ const AuthProvider = ({ children }) => {
};
} finally {
setLoading(false);
+=======
+ const data = await response.json();
+
+ if (response.ok && data.success) {
+ // Only set authenticated on success
+ setUser(data.data.user);
+ setIsAuthenticated(true);
+ return { success: true, message: data.message };
+ } else {
+ // Do not set isAuthenticated here
+ setIsAuthenticated(false);
+ setUser(null);
+ setIsAuthenticated(false);
+ return {
+ success: false,
+ message: data.message || "Invalid credentials",
+ };
+>>>>>>> a61f6ef (Updated login page and update profile page, fix: removal of console logs, restored .env)
}
+ } catch (error) {
+ console.error("Login error:", error);
+ setIsAuthenticated(false);
+ return {
+ success: false,
+ message: "An error occurred during login",
+ };
+ } finally {
+ setLoading(false);
+ }
};
// Register function
@@ -90,7 +117,7 @@ const AuthProvider = ({ children }) => {
headers: {
'Content-Type': 'application/json'
},
- body: JSON.stringify({ username,password }),
+ body: JSON.stringify({ username,password}),
credentials: 'include'
});
@@ -135,6 +162,19 @@ const AuthProvider = ({ children }) => {
}
};
+ //function created for providing path for updateProfile
+ const updateProfile = async (updates) => {
+ const response = await fetch('/api/auth/updateProfile', {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(updates),
+ credentials: 'include'
+ });
+ const data = await response.json();
+ if (data.success) setUser(data.user);
+ return data;
+ };
+
// Create the context value
const contextValue = {
user,
diff --git a/savebook/docker-compose.yml b/savebook/docker-compose.yml
index 2486613..a3a4c15 100644
--- a/savebook/docker-compose.yml
+++ b/savebook/docker-compose.yml
@@ -53,7 +53,7 @@ services:
networks:
- savebook-network
healthcheck:
- test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/api/auth/user', res => { if (res.statusCode !== 200) process.exit(1); }).on('error', () => process.exit(1));"]
+ test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/api/auth/user"]
interval: 30s
timeout: 10s
retries: 3
diff --git a/savebook/lib/utils/jwtAuth.js b/savebook/lib/utils/jwtAuth.js
index 032f254..bd3393c 100644
--- a/savebook/lib/utils/jwtAuth.js
+++ b/savebook/lib/utils/jwtAuth.js
@@ -1,4 +1,8 @@
+<<<<<<< HEAD:savebook/lib/utils/jwtAuth.js
import * as jose from 'jose';
+=======
+import * as jose from 'jose'
+>>>>>>> a61f6ef (Updated login page and update profile page, fix: removal of console logs, restored .env):savebook/lib/utils/JWT.js
const JWT_SECRET = process.env.ACCESS_TOKEN_SECRET || 'your-secret-key-here';
diff --git a/savebook/package-lock.json b/savebook/package-lock.json
index a4cf780..f6521ca 100644
--- a/savebook/package-lock.json
+++ b/savebook/package-lock.json
@@ -13,7 +13,7 @@
"cloudinary": "^2.8.0",
"framer-motion": "^12.24.7",
"jose": "^6.1.3",
- "jsonwebtoken": "^9.0.2",
+ "jsonwebtoken": "^9.0.3",
"lucide-react": "^0.562.0",
"mongoose": "^8.19.1",
"next": "^15.5.7",
@@ -4483,12 +4483,12 @@
}
},
"node_modules/jsonwebtoken": {
- "version": "9.0.2",
- "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
- "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
+ "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
"license": "MIT",
"dependencies": {
- "jws": "^3.2.2",
+ "jws": "^4.0.1",
"lodash.includes": "^4.3.0",
"lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4",
@@ -4521,9 +4521,9 @@
}
},
"node_modules/jwa": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
- "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
"license": "MIT",
"dependencies": {
"buffer-equal-constant-time": "^1.0.1",
@@ -4532,12 +4532,12 @@
}
},
"node_modules/jws": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz",
- "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
+ "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
"license": "MIT",
"dependencies": {
- "jwa": "^1.4.2",
+ "jwa": "^2.0.1",
"safe-buffer": "^5.0.1"
}
},
diff --git a/savebook/package.json b/savebook/package.json
index 66eab19..7114296 100644
--- a/savebook/package.json
+++ b/savebook/package.json
@@ -14,7 +14,7 @@
"cloudinary": "^2.8.0",
"framer-motion": "^12.24.7",
"jose": "^6.1.3",
- "jsonwebtoken": "^9.0.2",
+ "jsonwebtoken": "^9.0.3",
"lucide-react": "^0.562.0",
"mongoose": "^8.19.1",
"next": "^15.5.7",