Index: backend/pom.xml
===================================================================
--- backend/pom.xml	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ backend/pom.xml	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -170,4 +170,8 @@
 			<artifactId>google-auth-library-oauth2-http</artifactId>
 			<version>1.37.1</version>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-oauth2-client</artifactId>
 		</dependency>
 
Index: backend/src/main/java/com/shifterwebapp/shifter/auth/AuthService.java
===================================================================
--- backend/src/main/java/com/shifterwebapp/shifter/auth/AuthService.java	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ backend/src/main/java/com/shifterwebapp/shifter/auth/AuthService.java	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -3,4 +3,5 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.shifterwebapp.shifter.config.JwtService;
+import com.shifterwebapp.shifter.enums.LoginProvider;
 import com.shifterwebapp.shifter.exception.InvalidVerificationTokenException;
 import com.shifterwebapp.shifter.external.email.EmailService;
@@ -19,7 +20,10 @@
 import org.springframework.http.ResponseCookie;
 import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.AuthenticationException;
 import org.springframework.stereotype.Service;
 
+import java.util.Optional;
 import java.io.IOException;
 import java.time.Duration;
@@ -66,5 +70,5 @@
 
     public void register(String email, String password) {
-        User user = userService.createInitialUser(email, password);
+        User user = userService.createInitialUser(email, password, LoginProvider.LOCAL);
 
         UUID token = verificationTokenService.generateNewToken(user);
@@ -98,11 +102,30 @@
 
     public void authenticate(LoginDto request, HttpServletResponse response) throws IOException {
-        authenticationManager.authenticate(
-                new UsernamePasswordAuthenticationToken(
-                        request.getEmail(),
-                        request.getPassword()
-                )
-        );
+        Optional<User> userOptional = userRepository.findByEmail(request.getEmail());
+
+        if (userOptional.isPresent()) {
+            User user = userOptional.get();
+
+            if (user.getLoginProvider() != LoginProvider.LOCAL) {
+                throw new BadCredentialsException(
+                        "This account was registered with " + user.getLoginProvider() +
+                                ". Please use the corresponding login method."
+                );
+            }
+        }
+
+        try {
+            authenticationManager.authenticate(
+                    new UsernamePasswordAuthenticationToken(
+                            request.getEmail(),
+                            request.getPassword()
+                    )
+            );
+        } catch (AuthenticationException ex) {
+            throw new BadCredentialsException("Invalid email or password.");
+        }
+
         User user = userService.getUserEntityByEmail(request.getEmail());
+
         if (user.getIsEnabled())
             sendTokens(response, user);
Index: backend/src/main/java/com/shifterwebapp/shifter/config/SecurityConfig.java
===================================================================
--- backend/src/main/java/com/shifterwebapp/shifter/config/SecurityConfig.java	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ backend/src/main/java/com/shifterwebapp/shifter/config/SecurityConfig.java	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -1,4 +1,5 @@
 package com.shifterwebapp.shifter.config;
 
+import com.shifterwebapp.shifter.external.google.OAuth2SuccessHandler;
 import lombok.RequiredArgsConstructor;
 import org.springframework.context.annotation.Bean;
@@ -27,4 +28,5 @@
     private final JwtAuthFilter jwtAuthFilter;
     private final AuthenticationEntryPoint authenticationEntryPoint;
+    private final OAuth2SuccessHandler oauth2SuccessHandler;
 
     @Bean
@@ -36,10 +38,17 @@
                 .authorizeHttpRequests(request -> request
                         .requestMatchers("/api/auth/**").permitAll()
+                        .requestMatchers("/api/courses/**").permitAll()
                         .requestMatchers("/api/test/**").permitAll()
-                        .requestMatchers("/api/courses/**").permitAll()
+                        .requestMatchers("/api/auth/oauth2/**").permitAll()
+                        .requestMatchers("/oauth2/**", "/login/oauth2/**").permitAll()
                         .anyRequest().authenticated()
                 )
                 .exceptionHandling(exception -> exception
                         .authenticationEntryPoint(authenticationEntryPoint)
+                )
+                .oauth2Login(oauth2 -> oauth2
+                        // Use the custom handler instead of defaultSuccessUrl()
+                        .successHandler(oauth2SuccessHandler)
+                        .failureUrl("http://localhost:5173/login?error")
                 )
                 .authenticationProvider(authenticationProvider)
Index: backend/src/main/java/com/shifterwebapp/shifter/enums/LoginProvider.java
===================================================================
--- backend/src/main/java/com/shifterwebapp/shifter/enums/LoginProvider.java	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
+++ backend/src/main/java/com/shifterwebapp/shifter/enums/LoginProvider.java	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -0,0 +1,6 @@
+package com.shifterwebapp.shifter.enums;
+
+public enum LoginProvider {
+    LOCAL,
+    GOOGLE
+}
Index: backend/src/main/java/com/shifterwebapp/shifter/exception/GlobalExceptionHandler.java
===================================================================
--- backend/src/main/java/com/shifterwebapp/shifter/exception/GlobalExceptionHandler.java	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ backend/src/main/java/com/shifterwebapp/shifter/exception/GlobalExceptionHandler.java	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -3,4 +3,5 @@
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.AuthenticationException;
 import org.springframework.web.bind.annotation.ControllerAdvice;
 import org.springframework.web.bind.annotation.ExceptionHandler;
@@ -67,4 +68,11 @@
     }
 
+    @ExceptionHandler(AuthenticationException.class)
+    public ResponseEntity<ErrorResponse> handleAuthenticationException(AuthenticationException ex) {
+        return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
+                .body(new ErrorResponse(ex.getMessage()));
+    }
+
+
     @ExceptionHandler(MultipartException.class)
     public ResponseEntity<?> handleMultipartException(MultipartException ex) {
Index: ckend/src/main/java/com/shifterwebapp/shifter/external/GoogleCalendarService.java
===================================================================
--- backend/src/main/java/com/shifterwebapp/shifter/external/GoogleCalendarService.java	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ 	(revision )
@@ -1,171 +1,0 @@
-package com.shifterwebapp.shifter.external;
-
-import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
-import com.google.api.client.json.gson.GsonFactory;
-import com.google.api.client.util.DateTime;
-import com.google.api.services.calendar.Calendar;
-import com.google.api.services.calendar.model.*;
-import com.google.auth.http.HttpCredentialsAdapter;
-import com.google.auth.oauth2.GoogleCredentials;
-import com.google.auth.oauth2.ServiceAccountCredentials;
-import com.shifterwebapp.shifter.exception.GoogleCalendarException;
-import lombok.Getter;
-import org.springframework.stereotype.Service;
-
-import java.io.InputStream;
-import java.time.*;
-import java.time.temporal.ChronoUnit;
-import java.util.*;
-
-@Service
-public class GoogleCalendarService {
-
-    private static final String APPLICATION_NAME = "Shifter App";
-    private static final GsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
-
-    @Getter
-    private final String expertCalendarId = System.getProperty("GOOGLE_EXPERT_CALENDAR_ID");
-
-    private final Calendar calendarService;
-
-    public GoogleCalendarService() {
-        this.calendarService = initService();
-    }
-
-    /**
-     * Initialize Google Calendar service using the service-account.json
-     */
-    private Calendar initService() {
-        try {
-            InputStream serviceAccountStream = getClass().getResourceAsStream("/service-account.json");
-            if (serviceAccountStream == null) {
-                throw new GoogleCalendarException("Service account JSON not found in classpath", null, false);
-            }
-
-            GoogleCredentials credentials = GoogleCredentials.fromStream(serviceAccountStream)
-                    .createScoped(Collections.singletonList("https://www.googleapis.com/auth/calendar"));
-
-            return new Calendar.Builder(
-                    GoogleNetHttpTransport.newTrustedTransport(),
-                    JSON_FACTORY,
-                    new HttpCredentialsAdapter(credentials)
-            ).setApplicationName(APPLICATION_NAME).build();
-
-        } catch (Exception e) {
-            throw new GoogleCalendarException("Failed to initialize Google Calendar service", e, false);
-        }
-    }
-
-    /* ==================== FREE/BUSY & SLOTS ==================== */
-
-    public Map<String, List<String>> computeFreeSlotsByDate(
-            List<TimeInterval> busySlots,
-            ZonedDateTime scheduleStart,
-            ZonedDateTime scheduleEnd,
-            int slotDurationMinutes,
-            ZoneId userZone) {
-
-        Map<String, List<String>> freeSlotsByDate = new LinkedHashMap<>();
-        ZonedDateTime cursorDay = scheduleStart.truncatedTo(ChronoUnit.DAYS);
-
-        while (cursorDay.isBefore(scheduleEnd)) {
-            DayOfWeek dayOfWeek = cursorDay.getDayOfWeek();
-
-            if (dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY) {
-                ZonedDateTime workdayStart = cursorDay.withHour(8).withMinute(0);
-                ZonedDateTime workdayEnd = cursorDay.withHour(16).withMinute(30);
-
-                ZonedDateTime cursorSlot = workdayStart;
-                while (cursorSlot.plusMinutes(slotDurationMinutes).compareTo(workdayEnd) <= 0) {
-                    ZonedDateTime slotEnd = cursorSlot.plusMinutes(slotDurationMinutes);
-                    Instant slotStartInstant = cursorSlot.toInstant();
-                    Instant slotEndInstant = slotEnd.toInstant();
-
-                    boolean overlaps = busySlots.stream()
-                            .anyMatch(busy -> busy.overlaps(slotStartInstant, slotEndInstant));
-
-                    if (!overlaps && cursorSlot.isAfter(scheduleStart)) {
-                        ZonedDateTime userTimeSlot = cursorSlot.withZoneSameInstant(userZone);
-                        String date = userTimeSlot.toLocalDate().toString();
-                        String time = userTimeSlot.toLocalTime().truncatedTo(ChronoUnit.MINUTES).toString();
-                        freeSlotsByDate.computeIfAbsent(date, k -> new ArrayList<>()).add(time);
-                    }
-
-                    cursorSlot = cursorSlot.plusMinutes(slotDurationMinutes);
-                }
-            }
-            cursorDay = cursorDay.plusDays(1);
-        }
-
-        return freeSlotsByDate;
-    }
-
-    public boolean isSlotFree(FreeBusyResponse freeBusy, ZonedDateTime start, ZonedDateTime end) {
-        if (start.getDayOfWeek() == DayOfWeek.SATURDAY || start.getDayOfWeek() == DayOfWeek.SUNDAY
-                || end.getDayOfWeek() == DayOfWeek.SATURDAY || end.getDayOfWeek() == DayOfWeek.SUNDAY) {
-            return false;
-        }
-
-        var busyList = freeBusy.getCalendars().get(expertCalendarId).getBusy();
-        for (var interval : busyList) {
-            Instant busyStart = Instant.ofEpochMilli(interval.getStart().getValue());
-            Instant busyEnd = Instant.ofEpochMilli(interval.getEnd().getValue());
-            if (!(busyEnd.isBefore(start.toInstant()) || busyStart.isAfter(end.toInstant()))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public FreeBusyResponse queryFreeBusy(Instant start, Instant end) {
-        try {
-            FreeBusyRequest fbRequest = new FreeBusyRequest()
-                    .setTimeMin(new DateTime(Date.from(start)))
-                    .setTimeMax(new DateTime(Date.from(end)))
-                    .setTimeZone("Europe/Skopje")
-                    .setItems(Collections.singletonList(new FreeBusyRequestItem().setId(expertCalendarId)));
-
-            return calendarService.freebusy().query(fbRequest).execute();
-        } catch (Exception e) {
-            throw new GoogleCalendarException("Failed to query free/busy times", e, false);
-        }
-    }
-
-    public String createEvent(String title, Instant start, Instant end) {
-        try {
-            Event event = new Event()
-                    .setSummary(title)
-                    .setStart(new EventDateTime().setDateTime(new DateTime(start.toEpochMilli())).setTimeZone("Europe/Skopje"))
-                    .setEnd(new EventDateTime().setDateTime(new DateTime(end.toEpochMilli())).setTimeZone("Europe/Skopje"));
-
-            Event createdEvent = calendarService.events().insert(expertCalendarId, event).execute();
-            return createdEvent.getId();
-        } catch (Exception e) {
-            throw new GoogleCalendarException("Failed to create event", e, false);
-        }
-    }
-
-    public void deleteEvent(String eventId) {
-        try {
-            calendarService.events().delete(expertCalendarId, eventId).execute();
-        } catch (Exception e) {
-            throw new GoogleCalendarException("Failed to delete event with ID: " + eventId, e, false);
-        }
-    }
-
-    /* ==================== TIME INTERVAL CLASS ==================== */
-
-    public static class TimeInterval {
-        private final Instant start;
-        private final Instant end;
-
-        public TimeInterval(Instant start, Instant end) {
-            this.start = start;
-            this.end = end;
-        }
-
-        public boolean overlaps(Instant otherStart, Instant otherEnd) {
-            return !start.isAfter(otherEnd) && !end.isBefore(otherStart);
-        }
-    }
-}
Index: backend/src/main/java/com/shifterwebapp/shifter/external/google/GoogleCalendarService.java
===================================================================
--- backend/src/main/java/com/shifterwebapp/shifter/external/google/GoogleCalendarService.java	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
+++ backend/src/main/java/com/shifterwebapp/shifter/external/google/GoogleCalendarService.java	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -0,0 +1,170 @@
+package com.shifterwebapp.shifter.external.google;
+
+import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
+import com.google.api.client.json.gson.GsonFactory;
+import com.google.api.client.util.DateTime;
+import com.google.api.services.calendar.Calendar;
+import com.google.api.services.calendar.model.*;
+import com.google.auth.http.HttpCredentialsAdapter;
+import com.google.auth.oauth2.GoogleCredentials;
+import com.shifterwebapp.shifter.exception.GoogleCalendarException;
+import lombok.Getter;
+import org.springframework.stereotype.Service;
+
+import java.io.InputStream;
+import java.time.*;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+
+@Service
+public class GoogleCalendarService {
+
+    private static final String APPLICATION_NAME = "Shifter App";
+    private static final GsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
+
+    @Getter
+    private final String expertCalendarId = System.getProperty("GOOGLE_EXPERT_CALENDAR_ID");
+
+    private final Calendar calendarService;
+
+    public GoogleCalendarService() {
+        this.calendarService = initService();
+    }
+
+    /**
+     * Initialize Google Calendar service using the service-account.json
+     */
+    private Calendar initService() {
+        try {
+            InputStream serviceAccountStream = getClass().getResourceAsStream("/service-account.json");
+            if (serviceAccountStream == null) {
+                throw new GoogleCalendarException("Service account JSON not found in classpath", null, false);
+            }
+
+            GoogleCredentials credentials = GoogleCredentials.fromStream(serviceAccountStream)
+                    .createScoped(Collections.singletonList("https://www.googleapis.com/auth/calendar"));
+
+            return new Calendar.Builder(
+                    GoogleNetHttpTransport.newTrustedTransport(),
+                    JSON_FACTORY,
+                    new HttpCredentialsAdapter(credentials)
+            ).setApplicationName(APPLICATION_NAME).build();
+
+        } catch (Exception e) {
+            throw new GoogleCalendarException("Failed to initialize Google Calendar service", e, false);
+        }
+    }
+
+    /* ==================== FREE/BUSY & SLOTS ==================== */
+
+    public Map<String, List<String>> computeFreeSlotsByDate(
+            List<TimeInterval> busySlots,
+            ZonedDateTime scheduleStart,
+            ZonedDateTime scheduleEnd,
+            int slotDurationMinutes,
+            ZoneId userZone) {
+
+        Map<String, List<String>> freeSlotsByDate = new LinkedHashMap<>();
+        ZonedDateTime cursorDay = scheduleStart.truncatedTo(ChronoUnit.DAYS);
+
+        while (cursorDay.isBefore(scheduleEnd)) {
+            DayOfWeek dayOfWeek = cursorDay.getDayOfWeek();
+
+            if (dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY) {
+                ZonedDateTime workdayStart = cursorDay.withHour(8).withMinute(0);
+                ZonedDateTime workdayEnd = cursorDay.withHour(16).withMinute(30);
+
+                ZonedDateTime cursorSlot = workdayStart;
+                while (cursorSlot.plusMinutes(slotDurationMinutes).compareTo(workdayEnd) <= 0) {
+                    ZonedDateTime slotEnd = cursorSlot.plusMinutes(slotDurationMinutes);
+                    Instant slotStartInstant = cursorSlot.toInstant();
+                    Instant slotEndInstant = slotEnd.toInstant();
+
+                    boolean overlaps = busySlots.stream()
+                            .anyMatch(busy -> busy.overlaps(slotStartInstant, slotEndInstant));
+
+                    if (!overlaps && cursorSlot.isAfter(scheduleStart)) {
+                        ZonedDateTime userTimeSlot = cursorSlot.withZoneSameInstant(userZone);
+                        String date = userTimeSlot.toLocalDate().toString();
+                        String time = userTimeSlot.toLocalTime().truncatedTo(ChronoUnit.MINUTES).toString();
+                        freeSlotsByDate.computeIfAbsent(date, k -> new ArrayList<>()).add(time);
+                    }
+
+                    cursorSlot = cursorSlot.plusMinutes(slotDurationMinutes);
+                }
+            }
+            cursorDay = cursorDay.plusDays(1);
+        }
+
+        return freeSlotsByDate;
+    }
+
+    public boolean isSlotFree(FreeBusyResponse freeBusy, ZonedDateTime start, ZonedDateTime end) {
+        if (start.getDayOfWeek() == DayOfWeek.SATURDAY || start.getDayOfWeek() == DayOfWeek.SUNDAY
+                || end.getDayOfWeek() == DayOfWeek.SATURDAY || end.getDayOfWeek() == DayOfWeek.SUNDAY) {
+            return false;
+        }
+
+        var busyList = freeBusy.getCalendars().get(expertCalendarId).getBusy();
+        for (var interval : busyList) {
+            Instant busyStart = Instant.ofEpochMilli(interval.getStart().getValue());
+            Instant busyEnd = Instant.ofEpochMilli(interval.getEnd().getValue());
+            if (!(busyEnd.isBefore(start.toInstant()) || busyStart.isAfter(end.toInstant()))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public FreeBusyResponse queryFreeBusy(Instant start, Instant end) {
+        try {
+            FreeBusyRequest fbRequest = new FreeBusyRequest()
+                    .setTimeMin(new DateTime(Date.from(start)))
+                    .setTimeMax(new DateTime(Date.from(end)))
+                    .setTimeZone("Europe/Skopje")
+                    .setItems(Collections.singletonList(new FreeBusyRequestItem().setId(expertCalendarId)));
+
+            return calendarService.freebusy().query(fbRequest).execute();
+        } catch (Exception e) {
+            throw new GoogleCalendarException("Failed to query free/busy times", e, false);
+        }
+    }
+
+    public String createEvent(String title, Instant start, Instant end) {
+        try {
+            Event event = new Event()
+                    .setSummary(title)
+                    .setStart(new EventDateTime().setDateTime(new DateTime(start.toEpochMilli())).setTimeZone("Europe/Skopje"))
+                    .setEnd(new EventDateTime().setDateTime(new DateTime(end.toEpochMilli())).setTimeZone("Europe/Skopje"));
+
+            Event createdEvent = calendarService.events().insert(expertCalendarId, event).execute();
+            return createdEvent.getId();
+        } catch (Exception e) {
+            throw new GoogleCalendarException("Failed to create event", e, false);
+        }
+    }
+
+    public void deleteEvent(String eventId) {
+        try {
+            calendarService.events().delete(expertCalendarId, eventId).execute();
+        } catch (Exception e) {
+            throw new GoogleCalendarException("Failed to delete event with ID: " + eventId, e, false);
+        }
+    }
+
+    /* ==================== TIME INTERVAL CLASS ==================== */
+
+    public static class TimeInterval {
+        private final Instant start;
+        private final Instant end;
+
+        public TimeInterval(Instant start, Instant end) {
+            this.start = start;
+            this.end = end;
+        }
+
+        public boolean overlaps(Instant otherStart, Instant otherEnd) {
+            return !start.isAfter(otherEnd) && !end.isBefore(otherStart);
+        }
+    }
+}
Index: backend/src/main/java/com/shifterwebapp/shifter/external/google/OAuth2SuccessHandler.java
===================================================================
--- backend/src/main/java/com/shifterwebapp/shifter/external/google/OAuth2SuccessHandler.java	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
+++ backend/src/main/java/com/shifterwebapp/shifter/external/google/OAuth2SuccessHandler.java	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -0,0 +1,83 @@
+package com.shifterwebapp.shifter.external.google;
+
+import com.shifterwebapp.shifter.config.JwtService;
+import com.shifterwebapp.shifter.enums.LoginProvider;
+import com.shifterwebapp.shifter.user.User;
+import com.shifterwebapp.shifter.user.service.UserService;
+import com.shifterwebapp.shifter.verificationtoken.service.VerificationTokenService;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
+import org.springframework.security.oauth2.core.user.OAuth2User;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.UUID;
+
+/**
+ * Executes immediately after a successful OAuth2 handshake, allowing us to
+ * access the authenticated OAuth2User before the SecurityContext is lost
+ * due to a STATELESS session policy.
+ */
+@Component
+@RequiredArgsConstructor
+public class OAuth2SuccessHandler implements AuthenticationSuccessHandler {
+
+    private final UserService userService;
+    private final VerificationTokenService verificationTokenService;
+    private final JwtService jwtService;
+
+    // Frontend base URL where the final redirect must go
+    private static final String FRONTEND_REDIRECT_BASE = "http://localhost:5173/oauth2/redirect";
+
+    @Override
+    public void onAuthenticationSuccess(
+            HttpServletRequest request,
+            HttpServletResponse response,
+            Authentication authentication
+    ) throws IOException, ServletException {
+
+        // 1. Safely cast and extract the OAuth2User Principal
+        if (!(authentication instanceof OAuth2AuthenticationToken)) {
+            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid authentication token type.");
+            return;
+        }
+
+        OAuth2User oauthUser = ((OAuth2AuthenticationToken) authentication).getPrincipal();
+
+        // At this point, oauthUser is guaranteed to be non-null if this handler is reached
+        String email = oauthUser.getAttribute("email");
+        String token;
+        boolean login;
+        User user;
+
+        boolean userExists = userService.existsUserByEmail(email);
+        if (userExists) {
+            // Existing user → generate and return JWT
+            user = userService.getUserEntityByEmail(email);
+            token = jwtService.generateToken(user);
+            login = true;
+        } else {
+            // New user → create minimal account and generate verification token
+            String randomPassword = UUID.randomUUID().toString() + UUID.randomUUID().toString();
+            user = userService.createInitialUser(email, randomPassword, LoginProvider.GOOGLE);
+            token = verificationTokenService.generateNewToken(user).toString();
+            login = false;
+        }
+
+        assert email != null;
+        String redirectUrl = String.format("%s?token=%s&login=%s&email=%s",
+                FRONTEND_REDIRECT_BASE,
+                URLEncoder.encode(token, StandardCharsets.UTF_8),
+                login,
+                URLEncoder.encode(email, StandardCharsets.UTF_8));
+
+        response.sendRedirect(redirectUrl);
+    }
+}
Index: backend/src/main/java/com/shifterwebapp/shifter/external/meeting/service/MeetingService.java
===================================================================
--- backend/src/main/java/com/shifterwebapp/shifter/external/meeting/service/MeetingService.java	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ backend/src/main/java/com/shifterwebapp/shifter/external/meeting/service/MeetingService.java	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -7,5 +7,5 @@
 import com.shifterwebapp.shifter.external.email.EmailService;
 import com.shifterwebapp.shifter.external.ZoomService;
-import com.shifterwebapp.shifter.external.GoogleCalendarService;
+import com.shifterwebapp.shifter.external.google.GoogleCalendarService;
 import com.shifterwebapp.shifter.external.meeting.MeetingUtils;
 import com.shifterwebapp.shifter.external.meeting.UserMeetingInfoRequest;
Index: backend/src/main/java/com/shifterwebapp/shifter/sql/initializeUser.sql
===================================================================
--- backend/src/main/java/com/shifterwebapp/shifter/sql/initializeUser.sql	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ backend/src/main/java/com/shifterwebapp/shifter/sql/initializeUser.sql	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -5,5 +5,7 @@
                    points,
                    work_position,
-                   company_size)
+                   company_size,
+                   is_enabled,
+                   login_provider)
 VALUES (
         'admin@gmail.com',
@@ -13,4 +15,6 @@
         1000,
         'Administrator',
-        'ENTERPRISE'
+        'ENTERPRISE',
+        true,
+        'LOCAL'
     )
Index: backend/src/main/java/com/shifterwebapp/shifter/user/User.java
===================================================================
--- backend/src/main/java/com/shifterwebapp/shifter/user/User.java	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ backend/src/main/java/com/shifterwebapp/shifter/user/User.java	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -3,4 +3,5 @@
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.shifterwebapp.shifter.enums.CompanySize;
+import com.shifterwebapp.shifter.enums.LoginProvider;
 import com.shifterwebapp.shifter.payment.Payment;
 import jakarta.persistence.*;
@@ -30,5 +31,8 @@
     @Column(unique = true, nullable = false)
     private String email;
-    
+
+    @Enumerated(EnumType.STRING)
+    private LoginProvider loginProvider;
+
     @JsonIgnore
     private String passwordHash;    // SHOULD I USE JSON IGNORE HERE? OR IS IT ENOUGH TO NOT EXPOSE IT IN DTO?
Index: backend/src/main/java/com/shifterwebapp/shifter/user/UserController.java
===================================================================
--- backend/src/main/java/com/shifterwebapp/shifter/user/UserController.java	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ backend/src/main/java/com/shifterwebapp/shifter/user/UserController.java	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -19,4 +19,11 @@
     private final UserService userService;
     private final Validate validate;
+
+    @GetMapping("/me")
+    public ResponseEntity<?> getUser(Authentication authentication) {
+        Long userId = validate.extractUserId(authentication);
+
+        return ResponseEntity.ok(userService.getUserById(userId));
+    }
 
     @PutMapping("/favorite-course/{courseId}")
Index: backend/src/main/java/com/shifterwebapp/shifter/user/service/ImplUserService.java
===================================================================
--- backend/src/main/java/com/shifterwebapp/shifter/user/service/ImplUserService.java	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ backend/src/main/java/com/shifterwebapp/shifter/user/service/ImplUserService.java	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -2,4 +2,5 @@
 
 import com.shifterwebapp.shifter.auth.UserPersonalizationDto;
+import com.shifterwebapp.shifter.enums.LoginProvider;
 import com.shifterwebapp.shifter.user.UserInfoDto;
 import com.shifterwebapp.shifter.payment.Payment;
@@ -19,5 +20,5 @@
     Boolean existsUserByEmail(String email);
 
-    User createInitialUser(String email, String password);
+    User createInitialUser(String email, String password, LoginProvider loginProvider);
     User createUser(UserPersonalizationDto userPersonalizationDto);
     void deleteUser(Long id);
Index: backend/src/main/java/com/shifterwebapp/shifter/user/service/UserService.java
===================================================================
--- backend/src/main/java/com/shifterwebapp/shifter/user/service/UserService.java	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ backend/src/main/java/com/shifterwebapp/shifter/user/service/UserService.java	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -3,4 +3,5 @@
 import com.shifterwebapp.shifter.Validate;
 import com.shifterwebapp.shifter.auth.UserPersonalizationDto;
+import com.shifterwebapp.shifter.enums.LoginProvider;
 import com.shifterwebapp.shifter.user.UserInfoDto;
 import com.shifterwebapp.shifter.payment.Payment;
@@ -66,5 +67,5 @@
 
     @Override
-    public User createInitialUser(String email, String password) {
+    public User createInitialUser(String email, String password, LoginProvider loginProvider) {
         if (userRepository.existsUserByEmail(email)) {
             throw new RuntimeException("Email already in use");
@@ -77,4 +78,5 @@
                 .hasUsedFreeConsultation(false)
                 .points(0)
+                .loginProvider(loginProvider)
                 .build();
 
@@ -84,6 +86,4 @@
     @Override
     public User createUser(UserPersonalizationDto userPersonalizationDto) {
-        System.out.println(userPersonalizationDto.toString());
-        System.out.println(userPersonalizationDto.getEmail());
         User user = userRepository.findByEmail(userPersonalizationDto.getEmail()).orElseThrow();
 
Index: backend/src/main/resources/application.properties
===================================================================
--- backend/src/main/resources/application.properties	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ backend/src/main/resources/application.properties	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -47,5 +47,4 @@
 aws.secret-key=${AWS_S3_SECRET_KEY}
 
-
 # JWT Configuration Key
 jwt.secret=${JWT_CONFIG_SECRET}
@@ -61,2 +60,14 @@
 spring.mail.properties.mail.smtp.starttls.required=true
 
+# Google
+spring.security.oauth2.client.registration.google.client-id=${GOOGLE_CLIENT_ID}
+spring.security.oauth2.client.registration.google.client-secret=${GOOGLE_CLIENT_SECRET}
+spring.security.oauth2.client.registration.google.scope=email,profile
+spring.security.oauth2.client.registration.google.redirect-uri=http://localhost:8080/login/oauth2/code/google
+
+spring.security.oauth2.client.provider.google.authorization-uri=https://accounts.google.com/o/oauth2/v2/auth
+spring.security.oauth2.client.provider.google.token-uri=https://oauth2.googleapis.com/token
+spring.security.oauth2.client.provider.google.user-info-uri=https://www.googleapis.com/oauth2/v3/userinfo
+spring.security.oauth2.client.provider.google.user-name-attribute=sub
+
+
Index: frontend/package-lock.json
===================================================================
--- frontend/package-lock.json	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ frontend/package-lock.json	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -16,4 +16,5 @@
         "@mui/icons-material": "^7.1.2",
         "@mui/material": "^7.1.2",
+        "@react-oauth/google": "^0.12.2",
         "@react-three/fiber": "^9.3.0",
         "@splinetool/react-spline": "^4.0.0",
@@ -1626,4 +1627,14 @@
         "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
         "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+      }
+    },
+    "node_modules/@react-oauth/google": {
+      "version": "0.12.2",
+      "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.12.2.tgz",
+      "integrity": "sha512-d1GVm2uD4E44EJft2RbKtp8Z1fp/gK8Lb6KHgs3pHlM0PxCXGLaq8LLYQYENnN4xPWO1gkL4apBtlPKzpLvZwg==",
+      "license": "MIT",
+      "peerDependencies": {
+        "react": ">=16.8.0",
+        "react-dom": ">=16.8.0"
       }
     },
Index: frontend/package.json
===================================================================
--- frontend/package.json	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ frontend/package.json	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -18,4 +18,5 @@
     "@mui/icons-material": "^7.1.2",
     "@mui/material": "^7.1.2",
+    "@react-oauth/google": "^0.12.2",
     "@react-three/fiber": "^9.3.0",
     "@splinetool/react-spline": "^4.0.0",
Index: frontend/src/App.tsx
===================================================================
--- frontend/src/App.tsx	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ frontend/src/App.tsx	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -26,4 +26,5 @@
 import Academies from "./pages/Academies.tsx";
 import Personalize from "./pages/Personalize.tsx";
+import OAuth2RedirectHandler from "./api/OAuth2RedirectHandler.tsx";
 
 function LayoutWrapper() {
@@ -78,7 +79,9 @@
                 }/>
 
-                <Route path="/about" element={<About/>}/>
+                <Route path="/oauth2/redirect" element={<OAuth2RedirectHandler />} />
 
                 <Route path="/" element={<Home/>}/>
+
+                <Route path="/about" element={<About/>}/>
 
                 <Route path="/mentoring" element={<Mentoring/>}/>
Index: frontend/src/api/OAuth2RedirectHandler.tsx
===================================================================
--- frontend/src/api/OAuth2RedirectHandler.tsx	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
+++ frontend/src/api/OAuth2RedirectHandler.tsx	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -0,0 +1,36 @@
+import { useEffect, useRef } from "react";
+import { useNavigate, useLocation } from "react-router-dom";
+import { useAuthContext } from "../context/AuthContext.tsx";
+import {getUserApi} from "./userApi.ts";
+
+export default function OAuth2RedirectHandler() {
+    const navigate = useNavigate();
+    const { setAccessToken, setUser, setAuthChecked } = useAuthContext();
+    const location = useLocation();
+
+    useEffect(() => {
+        const params = new URLSearchParams(location.search);
+        const token = params.get("token");
+        const login = params.get("login") === "true";
+
+        if (token) {
+            if (login) {
+                getUserApi(token)
+                    .then(user => {
+                        setUser(user);
+                        setAuthChecked(true);
+                        setAccessToken(token);
+                        navigate("/", { replace: true }); // Use replace to avoid adding to history
+                    })
+                    .catch(err => console.log("Cannot fetch user: ", err));
+            } else {
+                navigate(`/welcome?token=${token}`, { replace: true });
+            }
+        } else {
+            console.error("OAuth2 login failed: No token received.");
+            navigate("/login?error", { replace: true });
+        }
+    }, [navigate, setAccessToken]);
+
+    return <div className="h-screen"></div>;
+}
Index: frontend/src/api/reviewApi.ts
===================================================================
--- frontend/src/api/reviewApi.ts	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
+++ frontend/src/api/reviewApi.ts	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -0,0 +1,65 @@
+import axios from "axios";
+import type {Review} from "../models/Review.tsx";
+
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-expect-error
+const backendUrl = import.meta.env.VITE_BACKEND_URL;
+
+export const writeReviewApi = async (
+    accessToken: string,
+    courseId: number,
+    rating: number,
+    comment: string,
+): Promise<void> => {
+
+    await axios.post(
+        `${backendUrl}/api/review/${courseId}`,
+        {
+            rating,
+            comment
+        },
+        {
+            headers: {
+                Authorization: `Bearer ${accessToken}`
+            }
+        }
+    );
+}
+
+export const updateReviewApi = async (
+    accessToken: string,
+    courseId: number,
+    rating: number,
+    comment: string,
+): Promise<void> => {
+
+    await axios.put(
+        `${backendUrl}/api/review/${courseId}`,
+        {
+            rating,
+            comment
+        },
+        {
+            headers: {
+                Authorization: `Bearer ${accessToken}`
+            }
+        }
+    );
+}
+
+export const getReviewApi = async (
+    accessToken: string,
+    courseId: number,
+): Promise<Review> => {
+
+    const  res = await axios.get(
+        `${backendUrl}/api/review/course/${courseId}`,
+        {
+            headers: {
+                Authorization: `Bearer ${accessToken}`
+            }
+        }
+    );
+
+    return res.data;
+}
Index: ontend/src/api/reviewApi.tsx
===================================================================
--- frontend/src/api/reviewApi.tsx	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ 	(revision )
@@ -1,65 +1,0 @@
-import axios from "axios";
-import type {Review} from "../models/Review.tsx";
-
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-expect-error
-const backendUrl = import.meta.env.VITE_BACKEND_URL;
-
-export const writeReviewApi = async (
-    accessToken: string,
-    courseId: number,
-    rating: number,
-    comment: string,
-): Promise<void> => {
-
-    await axios.post(
-        `${backendUrl}/api/review/${courseId}`,
-        {
-            rating,
-            comment
-        },
-        {
-            headers: {
-                Authorization: `Bearer ${accessToken}`
-            }
-        }
-    );
-}
-
-export const updateReviewApi = async (
-    accessToken: string,
-    courseId: number,
-    rating: number,
-    comment: string,
-): Promise<void> => {
-
-    await axios.put(
-        `${backendUrl}/api/review/${courseId}`,
-        {
-            rating,
-            comment
-        },
-        {
-            headers: {
-                Authorization: `Bearer ${accessToken}`
-            }
-        }
-    );
-}
-
-export const getReviewApi = async (
-    accessToken: string,
-    courseId: number,
-): Promise<Review> => {
-
-    const  res = await axios.get(
-        `${backendUrl}/api/review/course/${courseId}`,
-        {
-            headers: {
-                Authorization: `Bearer ${accessToken}`
-            }
-        }
-    );
-
-    return res.data;
-}
Index: frontend/src/api/userApi.ts
===================================================================
--- frontend/src/api/userApi.ts	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ frontend/src/api/userApi.ts	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -1,3 +1,4 @@
 import axios from "axios";
+import type {User} from "../models/javaObjects/User.tsx";
 
 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -52,2 +53,15 @@
     )
 }
+
+export const getUserApi = async (accessToken: string): Promise<User> => {
+    const res = await axios.get(
+        `${backendUrl}/api/users/me`,
+        {
+            headers: {
+                Authorization: `Bearer ${accessToken}`,
+            }
+        }
+    )
+
+    return res.data;
+}
Index: frontend/src/components/GoogleLoginButton.tsx
===================================================================
--- frontend/src/components/GoogleLoginButton.tsx	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
+++ frontend/src/components/GoogleLoginButton.tsx	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -0,0 +1,22 @@
+
+export default function GoogleLoginButton({text = "Sign in with Google"}) {
+    const handleLogin = () => {
+        // Redirect to backend OAuth2 login
+        window.location.href = "http://localhost:8080/oauth2/authorization/google";
+    };
+
+    return (
+        <button
+            onClick={handleLogin}
+            className="cursor-pointer flex items-center justify-center w-full max-w-sm px-4 py-2 border
+            border-gray-300 rounded shadow-sm bg-white hover:bg-gray-100 transition duration-200"
+        >
+            <img
+                src="https://upload.wikimedia.org/wikipedia/commons/c/c1/Google_%22G%22_logo.svg"
+                alt="Google logo"
+                className="w-6 h-6 mr-3"
+            />
+            <span className="text-gray-700 font-medium">{text}</span>
+        </button>
+    );
+}
Index: frontend/src/components/ModalReviewCourse.tsx
===================================================================
--- frontend/src/components/ModalReviewCourse.tsx	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ frontend/src/components/ModalReviewCourse.tsx	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -4,5 +4,5 @@
 import StarFilled from "../assets/icons/StarFilled";
 import StarOutline from "../assets/icons/StarOutline";
-import {getReviewApi, updateReviewApi, writeReviewApi} from "../api/reviewApi.tsx";
+import {getReviewApi, updateReviewApi, writeReviewApi} from "../api/reviewApi.ts";
 import {useAuthContext} from "../context/AuthContext.tsx";
 import ModalReviewCourseSkeleton from "./skeletons/ModalReviewCourseSkeleton.tsx";
Index: frontend/src/components/inputs/PersonalizationInput.tsx
===================================================================
--- frontend/src/components/inputs/PersonalizationInput.tsx	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ frontend/src/components/inputs/PersonalizationInput.tsx	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -3,5 +3,5 @@
 import type { UserPersonalization } from "../../models/javaObjects/UserPersonalization.tsx";
 
-type UserStrFields = 'email' | 'password' | 'passwordConfirmation' | 'name' | 'workPosition' | 'companyType';
+type UserStrFields = 'name' | 'workPosition' | 'companySize';
 interface InputProps {
     placeholder: string;
Index: ontend/src/components/inputs/RegisterInput.tsx
===================================================================
--- frontend/src/components/inputs/RegisterInput.tsx	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ 	(revision )
@@ -1,67 +1,0 @@
-
-import {Eye, EyeOff} from "lucide-react";
-import React from "react";
-import type { UserPersonalization } from "../../models/javaObjects/UserPersonalization.tsx";
-
-type UserStrFields = 'email' | 'password' | 'passwordConfirmation' | 'name' | 'workPosition' | 'companyType';
-interface InputProps {
-    placeholder: string;
-    label: string;
-    name: UserStrFields;
-    type: string;
-    id: string;
-    showPassword?: boolean;
-    setShowPassword?: React.Dispatch<React.SetStateAction<boolean>>;
-    setUser: React.Dispatch<React.SetStateAction<UserPersonalization>>;
-    user: UserPersonalization;
-}
-
-function PersonalizationInput(inputProps: InputProps) {
-
-    return (
-        <div
-            className="w-8/10 relative flex flex-col items- gap-1 px-6 py-1 border-2 border-shifter group focus-within:border-l-20 transition-all ease-in-out duration-300 items-start rounded-sm">
-            <label
-                htmlFor={inputProps.id}
-                className="text-shifter font-medium"
-            >
-                {inputProps.label}
-            </label>
-            <div className="flex gap-2 w-full">
-                <div className="w-full">
-                    <input
-                        id={inputProps.id}
-                        type={!inputProps.name.includes("password") ? "text" : inputProps.showPassword ? "text" : "password"}
-                        name={inputProps.name}
-                        placeholder={inputProps.placeholder}
-                        className="w-full focus:outline-none text-lg"
-                        value={inputProps.user[inputProps.name] || ""}
-                        onChange={e =>
-                            inputProps.setUser((prev: UserPersonalization) => ({
-                                ...prev,
-                                [inputProps.name]: e.target.value
-                            }))
-                        }
-                    />
-                    <hr className="border-t-2 border-black/5 rounded-full w-full"/>
-                </div>
-                {inputProps.name.includes("password") && (
-                    <button
-                        type="button"
-                        onClick={() => inputProps.setShowPassword?.((prev: boolean) => !prev)}
-                        className="text-black cursor-pointer hover:bg-black/5 rounded-full p-1"
-                        aria-label={inputProps.showPassword ? "Hide password" : "Show password"}
-                    >
-                        {!inputProps.showPassword ? (
-                            <EyeOff size={20} opacity={0.6}/>
-                        ) : (
-                            <Eye size={20} opacity={0.6}/>
-                        )}
-                    </button>
-                )}
-            </div>
-        </div>
-    );
-}
-
-export default PersonalizationInput;
Index: frontend/src/pages/Login.tsx
===================================================================
--- frontend/src/pages/Login.tsx	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ frontend/src/pages/Login.tsx	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -9,4 +9,5 @@
 import {Link, useNavigate} from "react-router-dom";
 import {useAuthContext} from "../context/AuthContext.tsx";
+import GoogleLoginButton from "../components/GoogleLoginButton.tsx";
 
 interface InputProps {
@@ -45,5 +46,5 @@
             .catch((err) => {
                 if (err.response?.status === 401) {
-                    setError("Invalid email or password.");
+                    setError(err.response.data.message);
                 } else {
                     setError("Something went wrong. Please try again.");
@@ -126,5 +127,5 @@
 
                     {/* Buttons */}
-                    <div className="flex gap-2 justify-start text-md w-full text-lg mt-4">
+                    <div className="flex gap-2 justify-center text-md w-full text-lg mt-4">
                         <button
                             type="submit"
@@ -154,5 +155,14 @@
                         }
                     </div>
+
                 </form>
+
+                <div className="my-4 flex items-center gap-2 w-9/10 text-black opacity-20">
+                    <hr className="border-t-2 border-black w-full"/>
+                    <p>or</p>
+                    <hr className="border-t-2 border-black w-full"/>
+                </div>
+
+                <GoogleLoginButton text="Log in with Google"/>
             </section>
         </main>
Index: frontend/src/pages/Register.tsx
===================================================================
--- frontend/src/pages/Register.tsx	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ frontend/src/pages/Register.tsx	(revision 8367d4a0cfcda1481e7de4f45b1b24b5c3b5ba8b)
@@ -9,4 +9,5 @@
 import {Eye, EyeOff} from "lucide-react";
 import {useRegisterHook} from "../hooks/useRegisterHook.tsx";
+import GoogleLoginButton from "../components/GoogleLoginButton.tsx";
 
 function Register() {
@@ -46,5 +47,5 @@
 
             {/* RIGHT FORM CONTAINER */}
-            <section className="relative flex flex-col justify-center items-center flex-1 px-horizontal-md gap-6">
+            <section className="relative flex flex-col justify-center items-center flex-1 px-horizontal-md">
                 <div className="absolute top-0 px-4 py-4 flex w-full justify-between items-center">
                     <Link to={"/"} >
@@ -76,74 +77,84 @@
                             </div>
                     ) : (
-                        <form
-                            onSubmit={handleRegister}
-                            className="flex flex-col gap-4 w-full items-center">
-                            <Input
-                                placeholder={"name@email.com"}
-                                label={"Email address"}
-                                name={"email"}
-                                type={"email"}
-                                id={"email"}
-                                value={email}
-                                onChange={e => setEmail(e.target.value)}
-                                showPassword={showPassword}
-                                setShowPassword={setShowPassword}
-                            />
-                            <Input
-                                placeholder={"********"}
-                                label={"Password"}
-                                name={"password"}
-                                type={"password"}
-                                id={"password"}
-                                value={password}
-                                onChange={e => setPassword(e.target.value)}
-                                showPassword={showPassword}
-                                setShowPassword={setShowPassword}
-                            />
-                            <Input
-                                placeholder={"********"}
-                                label={"Confirm password"}
-                                name={"passwordConfirmation"}
-                                type={"password"}
-                                id={"password-confirmation"}
-                                value={passwordConfirmation}
-                                onChange={e => setPasswordConfirmation(e.target.value)}
-                                showPassword={showPassword}
-                                setShowPassword={setShowPassword}
-                            />
-
-                            {/* Error Message */}
-                            {showError && <p className="text-red-500 text-sm">{error}</p>}
-
-                            {/* Buttons */}
-                            <div className="flex gap-2 justify-start text-md w-full text-lg mt-4">
-                                <button
-                                    type="submit"
-                                    disabled={isLoading}
-                                    className={`hover:shadow-md hover:shadow-shifter/60 transition-all duration-200 ease-in-out cursor-pointer
+                        <>
+                            <form
+                                onSubmit={handleRegister}
+                                className="flex flex-col gap-4 w-full items-center">
+                                <Input
+                                    placeholder={"name@email.com"}
+                                    label={"Email address"}
+                                    name={"email"}
+                                    type={"email"}
+                                    id={"email"}
+                                    value={email}
+                                    onChange={e => setEmail(e.target.value)}
+                                    showPassword={showPassword}
+                                    setShowPassword={setShowPassword}
+                                />
+                                <Input
+                                    placeholder={"********"}
+                                    label={"Password"}
+                                    name={"password"}
+                                    type={"password"}
+                                    id={"password"}
+                                    value={password}
+                                    onChange={e => setPassword(e.target.value)}
+                                    showPassword={showPassword}
+                                    setShowPassword={setShowPassword}
+                                />
+                                <Input
+                                    placeholder={"********"}
+                                    label={"Confirm password"}
+                                    name={"passwordConfirmation"}
+                                    type={"password"}
+                                    id={"password-confirmation"}
+                                    value={passwordConfirmation}
+                                    onChange={e => setPasswordConfirmation(e.target.value)}
+                                    showPassword={showPassword}
+                                    setShowPassword={setShowPassword}
+                                />
+
+                                {/* Error Message */}
+                                {showError && <p className="text-red-500 text-sm">{error}</p>}
+
+                                {/* Buttons */}
+                                <div className="flex gap-2 justify-center text-md w-full text-lg mt-4">
+                                    <button
+                                        type="submit"
+                                        disabled={isLoading}
+                                        className={`hover:shadow-md hover:shadow-shifter/60 transition-all duration-200 ease-in-out cursor-pointer
                                     rounded-md border-3 border-white/50 text-white px-8 py-1 bg-shifter font-medium
                                     whitespace-nowrap ${isLoading ? "opacity-50 cursor-not-allowed" : ""}`}
-                                >
-                                    {
-                                        isLoading ? "Creating account..." : "Create Account"
-                                    }
-                                </button>
-                                <Link
-                                    to="/login"
-                                    className="hover:shadow-md hover:shadow-shifter/20 transition-all duration-200 ease-in-out cursor-pointer
+                                    >
+                                        {
+                                            isLoading ? "Creating account..." : "Create Account"
+                                        }
+                                    </button>
+                                    <Link
+                                        to="/login"
+                                        className="hover:shadow-md hover:shadow-shifter/20 transition-all duration-200 ease-in-out cursor-pointer
                                     rounded-md text-shifter/80 w-1/3 py-1 bg-white border-3 border-shifter/20 font-medium
                                     whitespace-nowrap opacity-80"
-                                >
-                                    Log In
-                                </Link>
-
-                                {/* Loading Animation */}
-                                {
-                                    isLoading && (
-                                        <div className="h-full loader"></div>
-                                    )
-                                }
+                                    >
+                                        Log In
+                                    </Link>
+
+                                    {/* Loading Animation */}
+                                    {
+                                        isLoading && (
+                                            <div className="h-full loader"></div>
+                                        )
+                                    }
+                                </div>
+                            </form>
+
+                            <div className="my-4 flex items-center gap-2 w-9/10 text-black opacity-20">
+                                <hr className="border-t-2 border-black w-full"/>
+                                <p>or</p>
+                                <hr className="border-t-2 border-black w-full"/>
                             </div>
-                        </form>
+
+                            <GoogleLoginButton text="Sign in with Google"/>
+                        </>
                     )
                 }
Index: ontend/src/pages/RegisterOld.tsx
===================================================================
--- frontend/src/pages/RegisterOld.tsx	(revision 97a665a612b08a13d4e257a0904f0c0dc0247cca)
+++ 	(revision )
@@ -1,268 +1,0 @@
-import React from "react";
-import ShifterLogo from "../../public/Shifter-S2W-Transparent.png";
-import ShifterArrow from "../../public/Shifter-Arrow-White.png";
-import {
-    Stepper,
-    Step,
-    StepLabel,
-    Box,
-} from "@mui/material";
-import {CustomStepperConnector, CustomStepperStepIcon} from "../components/registerSteps/CustomStepper.tsx";
-import {Link, useNavigate} from "react-router-dom";
-import {motion, AnimatePresence} from "framer-motion";
-import type {UserPersonalization} from "../models/javaObjects/UserPersonalization.tsx";
-import RegisterStepOne from "../components/registerSteps/RegisterStepOne.tsx";
-import PersonalizeStepOne from "../components/registerSteps/PersonalizeStepOne.tsx";
-import PersonalizationStepTwo from "../components/registerSteps/PersonalizationStepTwo.tsx";
-import PersonalizationStepThree from "../components/registerSteps/PersonalizationStepThree.tsx";
-import {useAuthContext} from "../context/AuthContext.tsx";
-import {isValidEmail} from "../utils/validation.ts";
-import {checkEmailExistsApi} from "../api/authApi.ts";
-
-function RegisterOld() {
-    const {register} = useAuthContext();
-    const [isLoading, setIsLoading] = React.useState<boolean>(false);
-    const [isCheckingEmail, setIsCheckingEmail] = React.useState<boolean>(false);
-    const [activeStep, setActiveStep] = React.useState(0);
-    const [showError, setShowError] = React.useState(false);
-    const [error, setError] = React.useState("");
-    const [direction, setDirection] = React.useState(0); // 1 for next, -1 for back
-    const [user, setUser] = React.useState<UserPersonalization>({
-        email: "",
-        password: "",
-        passwordConfirmation: "",
-        name: "",
-        workPosition: "",
-        companySize: "",
-        interests: [],
-        desiredSkills: [],
-    });
-    const navigate = useNavigate();
-
-    const handleNext = async () => {
-        if (error.length > 0) {
-            setShowError(true);
-            return;
-        }
-
-        if (activeStep === 0) {
-            if (!isValidEmail(user.email)) {
-                setError("Please enter a valid email.");
-                setShowError(true);
-                return;
-            }
-
-            setIsCheckingEmail(true);
-            try {
-                const exists = await checkEmailExistsApi(user.email);
-                if (exists) {
-                    setError("Email already exists");
-                    setShowError(true);
-                    setIsCheckingEmail(false);
-                    return;
-                }
-            } catch (err) {
-                setError("Error checking email. There's a problem with the server.");
-                setShowError(true);
-                setIsCheckingEmail(false);
-                console.error("Error checking email: ", err);
-                return;
-            }
-            setIsCheckingEmail(false);
-        }
-
-        // If we get here, no errors => proceed to next step
-        setError("");
-        setShowError(false);
-        setDirection(1);
-        setActiveStep((prev) => prev + 1);
-    };
-
-
-    const handleBack = () => {
-        setDirection(-1);
-        setActiveStep((prev) => prev - 1);
-    };
-    const variants = {
-        enter: (dir: number) => ({
-            x: dir > 0 ? 100 : -100,
-            opacity: 0,
-        }),
-        center: {
-            x: 0,
-            opacity: 1,
-        },
-        exit: (dir: number) => ({
-            x: dir > 0 ? -100 : 100,
-            opacity: 0,
-        }),
-    };
-
-    const handleRegister = async () => {
-        if (error.length > 0) {
-            setShowError(true);
-            return;
-        }
-
-        setIsLoading(true);
-
-        try {
-            await register(user);
-            navigate("/");
-        } catch (err) {
-            setError("Registration failed. Please try again.");
-            console.error("Registration error: ", err);
-        } finally {
-            setIsLoading(false);
-        }
-    }
-
-    const stepsContent = [
-        <RegisterStepOne setUser={setUser} user={user} setError={setError} />,
-        <PersonalizeStepOne setUser={setUser} user={user} setError={setError} />,
-        <PersonalizationStepTwo setUser={setUser} user={user} setError={setError} />,
-        <PersonalizationStepThree setUser={setUser} user={user} setError={setError} />
-    ];
-
-    return (
-        <main className="flex font-montserrat h-screen bg-white">
-
-            {/* LEFT HEADER AND BACKGROUND */}
-            <section className="relative bg-black w-[55%] overflow-hidden">
-                <div
-                    className="absolute w-full h-full bg-shifter/80 z-0 text-white px-16 flex flex-col gap-4 justify-center text-start">
-                    {/*Arrows*/}
-                    <img src={ShifterArrow} alt="Shifter Arrow"
-                         className="absolute -top-20 right-20 -rotate-130 w-auto h-100 opacity-70 z-2"/>
-                    <img src={ShifterArrow} alt="Shifter Arrow"
-                         className="absolute -bottom-20 left-20 rotate-50 w-auto h-100 opacity-70 z-2"/>
-                    <h1 className="text-7xl font-semibold z-2 whitespace-nowrap">Shift into Success</h1>
-                    <p className="text-2xl font-light z-2">Start your journey toward smarter, scalable business
-                        growth.</p>
-                </div>
-            </section>
-
-            {/* RIGHT FORM CONTAINER */}
-            <section className="relative flex flex-col justify-center items-center flex-1 px-20 gap-6">
-                <div className="absolute top-0 px-4 py-4 flex w-full justify-between items-center">
-                    <Link to={"/"} >
-                        <img
-                            src={ShifterLogo}
-                            alt="Shifter Logo"
-                            className="w-40 h-auto object-contain"
-                        />
-                    </Link>
-                    <Link
-                        to={"/"}
-                        className="hover:bg-shifter/20 hover:text-shifter underline decoration-current
-                             font-semibold text-black/80 rounded-sm px-4 py-2"
-                    >
-                        Back to Main Page
-                    </Link>
-                </div>
-
-                {/* STEPPER */}
-                <Box className="w-full flex flex-col">
-                    <Stepper
-                        activeStep={activeStep}
-                        alternativeLabel
-                        connector={<CustomStepperConnector/>}
-                    >
-                        {stepsContent.map((_, index) => (
-                            <Step key={index}>
-                                <StepLabel StepIconComponent={CustomStepperStepIcon}
-                                           className="text-shifter font-semibold"
-                                />
-                            </Step>
-                        ))}
-                    </Stepper>
-
-                    <Box className="flex flex-col overflow-hidden gap-2">
-
-                        {/*STEPPER CONTENT*/}
-                        <AnimatePresence mode="wait" initial={false} custom={direction}>
-                            <motion.div
-                                key={activeStep}
-                                custom={direction}
-                                variants={variants}
-                                initial="enter"
-                                animate="center"
-                                exit="exit"
-                                transition={{
-                                    x: {type: "spring", stiffness: 500, damping: 40},
-                                    opacity: {duration: 0.2},
-                                }}
-                                className="h-80 flex flex-col justify-center"
-                            >
-                                {stepsContent[activeStep]}
-                            </motion.div>
-                        </AnimatePresence>
-
-                        {/* Error Message */}
-                        {showError && <p className="text-red-500 text-sm">{error}</p>}
-
-
-                        {/*STEPPER BUTTONS*/}
-                        <Box className="flex flex-col justify-center items-center gap-2">
-                            <div className="flex justify-center gap-4 ">
-                                <button
-                                    disabled={activeStep === 0}
-                                    onClick={handleBack}
-                                    className="disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:shadow-none
-                                    hover:shadow-sm hover:shadow-black/20 transition-all duration-200 ease-in-out
-                                border-3 border-white/50 px-10 py-1 bg-black/10 text-black/60 cursor-pointer rounded-sm "
-                                >
-                                    Back
-                                </button>
-                                {activeStep === stepsContent.length - 1 ? (
-                                    <button
-                                        onClick={handleRegister}
-                                        className={`hover:shadow-md hover:shadow-shifter/60 transition-all duration-200 ease-in-out
-                                    px-20 border-3 border-white/50 bg-shifter text-white cursor-pointer rounded-md 
-                                    ${isLoading ? "opacity-50 cursor-not-allowed" : ""}`}
-                                    >
-                                        {
-                                            isLoading ? "Setting up..." : "Start Your Journey"
-                                        }
-                                    </button>
-                                ) : (
-                                    <button
-                                        onClick={handleNext}
-                                        // disabled={!canContinue}
-                                        className={`disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:shadow-none
-                                        hover:shadow-md hover:shadow-shifter/60 transition-all duration-200 ease-in-out
-                                    px-20 border-3 border-white/50 bg-shifter text-white cursor-pointer rounded-md`}
-                                    >
-                                        {
-                                            isCheckingEmail ? "Checking if email exists..." : "Next"
-                                        }
-                                    </button>
-                                )}
-
-
-                                {/* Loading Animation */}
-                                {
-                                    isLoading && (
-                                        <div className="h-full loader"></div>
-                                    )
-                                }
-                            </div>
-                            <p
-                                className="text-black/40"
-                            >
-                                Already have an account?
-                                <Link to={"/login"}
-                                      className="relative text-shifter font-medium w-fit hover:font-semibold"
-                                >
-                                    {" "}Log In
-                                </Link>
-                            </p>
-                        </Box>
-                    </Box>
-                </Box>
-            </section>
-        </main>
-    );
-}
-
-export default RegisterOld;
