Index: finkwave/.gitignore
===================================================================
--- finkwave/.gitignore	(revision 3727852b5e0f31eb857eab69ab7d5090fa06bcad)
+++ finkwave/.gitignore	(revision 484dc3f54a72450588e7ece748ea1b64ad9ffdc3)
@@ -33,2 +33,4 @@
 .vscode/
 application.properties
+
+uploads/profile-pictures/*
Index: finkwave/src/main/java/com/ukim/finki/develop/finkwave/controller/AuthController.java
===================================================================
--- finkwave/src/main/java/com/ukim/finki/develop/finkwave/controller/AuthController.java	(revision 3727852b5e0f31eb857eab69ab7d5090fa06bcad)
+++ finkwave/src/main/java/com/ukim/finki/develop/finkwave/controller/AuthController.java	(revision 484dc3f54a72450588e7ece748ea1b64ad9ffdc3)
@@ -5,14 +5,10 @@
 import com.ukim.finki.develop.finkwave.config.AuthProperties;
 import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.web.bind.annotation.CookieValue;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 import org.springframework.web.server.ResponseStatusException;
 
@@ -32,7 +28,7 @@
     private final AuthService authService;
 
-    @PostMapping("/register")
+    @PostMapping(value = "/register", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
     public ResponseEntity<Map<String, Object>> register(
-            @RequestBody AuthRequestDto authRequestDto,
+            @ModelAttribute AuthRequestDto authRequestDto,
             HttpServletResponse httpServletResponse){
         try {
Index: finkwave/src/main/java/com/ukim/finki/develop/finkwave/dto/AuthRequestDto.java
===================================================================
--- finkwave/src/main/java/com/ukim/finki/develop/finkwave/dto/AuthRequestDto.java	(revision 3727852b5e0f31eb857eab69ab7d5090fa06bcad)
+++ finkwave/src/main/java/com/ukim/finki/develop/finkwave/dto/AuthRequestDto.java	(revision 484dc3f54a72450588e7ece748ea1b64ad9ffdc3)
@@ -2,4 +2,5 @@
 
 import jakarta.annotation.Nullable;
+import org.springframework.web.multipart.MultipartFile;
 
 public record AuthRequestDto(
@@ -8,5 +9,5 @@
         String email,
         String password,
-        @Nullable String profilePhoto
+        @Nullable MultipartFile profilePhoto
 ){
 }
Index: finkwave/src/main/java/com/ukim/finki/develop/finkwave/filters/JwtFilter.java
===================================================================
--- finkwave/src/main/java/com/ukim/finki/develop/finkwave/filters/JwtFilter.java	(revision 3727852b5e0f31eb857eab69ab7d5090fa06bcad)
+++ finkwave/src/main/java/com/ukim/finki/develop/finkwave/filters/JwtFilter.java	(revision 484dc3f54a72450588e7ece748ea1b64ad9ffdc3)
@@ -33,4 +33,5 @@
             @NonNull HttpServletResponse response,
             @NonNull FilterChain filterChain) throws ServletException, IOException {
+        
         String token = null;
         if (request.getCookies() != null){
@@ -58,7 +59,9 @@
             } catch (JwtException e){
                 System.out.println("Invalid jwt token.");
+                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                 return;
             } catch (Exception e){
                 System.out.println(e.getMessage());
+                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                 return;
             }
Index: finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/AuthService.java
===================================================================
--- finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/AuthService.java	(revision 3727852b5e0f31eb857eab69ab7d5090fa06bcad)
+++ finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/AuthService.java	(revision 484dc3f54a72450588e7ece748ea1b64ad9ffdc3)
@@ -19,4 +19,11 @@
 import jakarta.transaction.Transactional;
 import lombok.RequiredArgsConstructor;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.UUID;
 
 @Service
@@ -32,5 +39,6 @@
 
     @Transactional
-    public UserResponseDto registerAndLogIn(HttpServletResponse response, AuthRequestDto authRequestDto){
+    public UserResponseDto registerAndLogIn(HttpServletResponse response, AuthRequestDto authRequestDto)
+    throws IOException {
         if (userRepository.findByUsername(authRequestDto.username()).isPresent()){
             throw new RuntimeException("User already exists");
@@ -90,13 +98,29 @@
     }
 
-    private User createNonAdminUser(AuthRequestDto authRequestDto){
-        User user = User.builder()
+    private User createNonAdminUser(AuthRequestDto authRequestDto) throws IOException {
+        User.UserBuilder userBuilder = User.builder()
                 .fullName(authRequestDto.fullname())
                 .username(authRequestDto.username())
                 .email(authRequestDto.email())
-                .profilePhoto(authRequestDto.profilePhoto())
                 .password(passwordEncoder.encode(authRequestDto.password()))
-                .role(Role.NONADMIN)
-                .build();
+                .role(Role.NONADMIN);
+
+
+        MultipartFile profilePhoto = authRequestDto.profilePhoto();
+        if (profilePhoto != null) {
+            if (profilePhoto.getContentType() != null && !profilePhoto.getContentType().startsWith("image/"))
+                throw new IllegalArgumentException("Invalid fyle type");
+
+            if (profilePhoto.getSize() > 5_000_000) {
+                throw new IllegalArgumentException(("File too large."));
+            }
+
+            String filename = UUID.randomUUID() + "-" + profilePhoto.getOriginalFilename();
+            Path path = Paths.get("uploads/profile-pictures", filename);
+            Files.copy(profilePhoto.getInputStream(), path);
+            userBuilder.profilePhoto("profile-pictures/" + filename);
+        }
+
+        User user = userBuilder.build();
 
         NonAdminUser nonAdminUser = new NonAdminUser();
Index: frontend/src/Login.tsx
===================================================================
--- frontend/src/Login.tsx	(revision 3727852b5e0f31eb857eab69ab7d5090fa06bcad)
+++ frontend/src/Login.tsx	(revision 484dc3f54a72450588e7ece748ea1b64ad9ffdc3)
@@ -20,10 +20,5 @@
 			}>("/auth/login", { username, password });
 			scheduleTokenRefresh(response.data.tokenExpiresIn);
-			setUser({
-				username: response.data.user.username,
-				fullName: "demo",
-				email: "demo@demo.com",
-				role: response.data.user.role,
-			});
+			setUser(response.data.user);
 			navigate("/");
 		} catch (error) {
Index: frontend/src/Nav.tsx
===================================================================
--- frontend/src/Nav.tsx	(revision 3727852b5e0f31eb857eab69ab7d5090fa06bcad)
+++ frontend/src/Nav.tsx	(revision 484dc3f54a72450588e7ece748ea1b64ad9ffdc3)
@@ -1,4 +1,4 @@
 import { Link } from "react-router-dom";
-import axiosInstance from "./api/axiosInstance";
+import axiosInstance, { baseURL } from "./api/axiosInstance";
 import Logo from "./assets/logo-finkwave.png";
 import { useAuth } from "./context/authContext";
@@ -29,9 +29,18 @@
 							<div className="flex items-center space-x-3">
 								<div className="flex items-center space-x-2 bg-slate-700 rounded-lg px-3 py-2">
-									<div className="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center">
-										<span className="text-white text-sm font-semibold">
-											{user.username.charAt(0).toUpperCase()}
-										</span>
-									</div>
+									{user.profilePhoto ? (
+										<img
+											src={`${baseURL}/${user.profilePhoto}`}
+											alt={`${user.username}'s profile`}
+											className="w-8 h-8 rounded-full object-cover"
+										/>
+									) : (
+										<div className="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center">
+											<span className="text-white text-sm font-semibold">
+												{user.username.charAt(0).toUpperCase()}
+											</span>
+										</div>
+									)}
+
 									<div className="text-white">
 										<p className="text-sm font-medium">{user.username}</p>
@@ -42,7 +51,5 @@
 								</div>
 								<button
-									onClick={(e: React.MouseEvent<HTMLButtonElement>) =>
-										handleLogout(e)
-									}
+									onClick={handleLogout}
 									className="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg text-sm 
                                     font-medium transition-colors duration-200 flex items-center space-x-1 cursor-pointer"
Index: frontend/src/Register.tsx
===================================================================
--- frontend/src/Register.tsx	(revision 3727852b5e0f31eb857eab69ab7d5090fa06bcad)
+++ frontend/src/Register.tsx	(revision 484dc3f54a72450588e7ece748ea1b64ad9ffdc3)
@@ -1,3 +1,3 @@
-import { useState } from "react";
+import { useEffect, useState } from "react";
 import { useNavigate } from "react-router";
 import axiosInstance, { scheduleTokenRefresh } from "./api/axiosInstance";
@@ -11,21 +11,43 @@
 	const [fullname, setFullname] = useState("");
 	const [email, setEmail] = useState("");
-	const [profilePhoto, setProfilePhoto] = useState<string | null>(null);
+	const [profilePhotoFile, setProfilePhotoFile] = useState<File | null>(null);
+	const [previewProfilePhoto, setPreviewProfilePhoto] = useState<string | null>(
+		null,
+	);
 
 	const navigate = useNavigate();
 
+	useEffect(() => {
+		return () => {
+			if (previewProfilePhoto) {
+				URL.revokeObjectURL(previewProfilePhoto);
+			}
+		};
+	}, [previewProfilePhoto]);
+
 	const handleRegister = async (e: React.FormEvent) => {
 		e.preventDefault();
+		// todo: add proper error handling
+		if (profilePhotoFile && profilePhotoFile.size > 5 * 1024 * 1024) {
+			alert("Max file size is 5MB");
+			return;
+		}
+
+		if (profilePhotoFile && !profilePhotoFile.type.startsWith("image/")) {
+			alert("Only images allowed");
+			return;
+		}
+		const formData = new FormData();
+		if (profilePhotoFile) formData.append("profilePhoto", profilePhotoFile);
+		formData.append("username", username);
+		formData.append("fullname", fullname);
+		formData.append("email", email);
+		formData.append("password", password);
+
 		try {
 			const response = await axiosInstance.post<{
 				user: User;
 				tokenExpiresIn: number;
-			}>("/auth/register", {
-				username,
-				fullname,
-				email,
-				password,
-				profilePhoto,
-			});
+			}>("/auth/register", formData);
 			scheduleTokenRefresh(response.data.tokenExpiresIn);
 			setUser(response.data.user);
@@ -89,22 +111,43 @@
 				</div>
 				<div className="mb-4">
-					<label className="block text-gray-700 mb-2" htmlFor="profilePhoto">
-						Profile Photo URL
-					</label>
+					<label className="block text-gray-700 mb-2">Profile Photo</label>
+					<div className="flex items-center gap-3">
+						<label
+							htmlFor="profilePhoto"
+							className="px-4 py-2 bg-blue-500 text-white rounded cursor-pointer hover:bg-blue-600 transition-colors text-sm font-medium"
+						>
+							Choose File
+						</label>
+						<span className="text-gray-600 text-sm">
+							{profilePhotoFile ? profilePhotoFile.name : "No file chosen"}
+						</span>
+					</div>
 					<input
-						placeholder="todo"
-						type="text"
+						type="file"
+						accept="image/*"
 						id="profilePhoto"
-						className="w-full p-2 border border-gray-300 rounded"
-						value={profilePhoto || ""}
-						onChange={(e) => setProfilePhoto(e.target.value)}
+						className="hidden"
+						onChange={(e) => {
+							if (e.target.files && e.target.files[0]) {
+								setProfilePhotoFile(e.target.files[0]);
+								setPreviewProfilePhoto(URL.createObjectURL(e.target.files[0]));
+							}
+						}}
 					/>
 				</div>
+				{previewProfilePhoto && (
+					<div className="mb-4">
+						<p className="block text-gray-700 mb-2">Profile Photo Preview:</p>
+						<img
+							src={previewProfilePhoto}
+							alt="Profile Preview"
+							className="w-20 h-20 object-cover rounded-full"
+						/>
+					</div>
+				)}
 				<button
 					type="submit"
 					className="w-full bg-blue-500 text-white p-2 rounded hover:bg-blue-600 cursor-pointer"
-					onClick={(e: React.MouseEvent<HTMLButtonElement>) =>
-						handleRegister(e)
-					}
+					onClick={handleRegister}
 				>
 					Register
Index: frontend/src/api/axiosInstance.ts
===================================================================
--- frontend/src/api/axiosInstance.ts	(revision 3727852b5e0f31eb857eab69ab7d5090fa06bcad)
+++ frontend/src/api/axiosInstance.ts	(revision 484dc3f54a72450588e7ece748ea1b64ad9ffdc3)
@@ -5,8 +5,13 @@
 export const axiosInstance = axios.create({
 	baseURL,
-	headers: {
-		"Content-Type": "application/json",
-	},
 	withCredentials: true,
+});
+
+// set content-type to application/json unless sending FormData
+axiosInstance.interceptors.request.use((config) => {
+	if (!(config.data instanceof FormData)) {
+		config.headers["Content-Type"] = "application/json";
+	}
+	return config;
 });
 
@@ -23,4 +28,8 @@
 export const scheduleTokenRefresh = (expiryTimeSeconds: number) => {
 	clearRefreshTimeout();
+
+	if (!expiryTimeSeconds || expiryTimeSeconds <= 0) {
+		return;
+	}
 
 	const refreshTime = (expiryTimeSeconds - 60) * 1000;
@@ -44,4 +53,5 @@
 			scheduleTokenRefresh(refreshResponse.data.tokenExpiresIn);
 		} catch (err) {
+			clearRefreshTimeout();
 			console.error(err);
 			throw err;
@@ -53,5 +63,5 @@
 };
 
-axios.interceptors.response.use(
+axiosInstance.interceptors.response.use(
 	(response) => response,
 	async (error) => {
