diff --git a/README.md b/README.md index bc3f496..c082cfd 100644 --- a/README.md +++ b/README.md @@ -137,10 +137,8 @@ frontend/ ├── auth/ │ ├── login/ │ │ └── login.component.ts # Email/password login form - │ ├── register/ - │ │ └── register.component.ts # Name/email/password register form - │ └── oauth-callback/ - │ └── oauth-callback.component.ts # Reads tokens from OAuth redirect + │ └── register/ + │ └── register.component.ts # Name/email/password register form └── dashboard/ ├── dashboard.component.ts # Site cards grid + toolbar ├── create-site-dialog/ @@ -161,8 +159,6 @@ frontend/ | POST | `/api/auth/login` | Login with email + password | | POST | `/api/auth/refresh` | Refresh access token | | POST | `/api/auth/logout` | Logout | -| GET | `/oauth2/authorization/google` | Login with Google | -| GET | `/oauth2/authorization/github` | Login with GitHub | ### Sites (`/api/sites`) diff --git a/backend/build.gradle b/backend/build.gradle index dfc62de..dfeedec 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -34,7 +34,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' - implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' diff --git a/backend/src/main/java/com/krrishg/config/SecurityConfig.java b/backend/src/main/java/com/krrishg/config/SecurityConfig.java index 3fd86be..3221316 100644 --- a/backend/src/main/java/com/krrishg/config/SecurityConfig.java +++ b/backend/src/main/java/com/krrishg/config/SecurityConfig.java @@ -1,7 +1,5 @@ package com.krrishg.config; -import com.krrishg.service.OAuth2SuccessHandler; -import com.krrishg.service.OAuth2UserService; import io.jsonwebtoken.Claims; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -34,23 +32,12 @@ import java.util.UUID; public class SecurityConfig { private final JwtConfig jwtConfig; - private final OAuth2UserService oAuth2UserService; - private final OAuth2SuccessHandler oAuth2SuccessHandler; @Value("${indie.cors.allowed-origins:http://localhost:4200}") private String[] allowedOrigins; - @Value("${spring.security.oauth2.client.registration.google.client-id:}") - private String googleClientId; - - @Value("${spring.security.oauth2.client.registration.github.client-id:}") - private String githubClientId; - - public SecurityConfig(JwtConfig jwtConfig, OAuth2UserService oAuth2UserService, - OAuth2SuccessHandler oAuth2SuccessHandler) { + public SecurityConfig(JwtConfig jwtConfig) { this.jwtConfig = jwtConfig; - this.oAuth2UserService = oAuth2UserService; - this.oAuth2SuccessHandler = oAuth2SuccessHandler; } @Bean @@ -72,25 +59,11 @@ public class SecurityConfig { response.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden")) ); - if (isOAuth2Configured()) { - http.oauth2Login(oauth2 -> oauth2 - .userInfoEndpoint(userInfo -> userInfo - .userService(oAuth2UserService) - ) - .successHandler(oAuth2SuccessHandler) - ); - } - http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } - private boolean isOAuth2Configured() { - return (googleClientId != null && !googleClientId.isBlank()) - || (githubClientId != null && !githubClientId.isBlank()); - } - @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); diff --git a/backend/src/main/java/com/krrishg/dto/AuthResponse.java b/backend/src/main/java/com/krrishg/dto/AuthResponse.java index 4c1a2f4..4e63769 100644 --- a/backend/src/main/java/com/krrishg/dto/AuthResponse.java +++ b/backend/src/main/java/com/krrishg/dto/AuthResponse.java @@ -1,6 +1,5 @@ package com.krrishg.dto; -import com.krrishg.model.AuthProvider; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -32,6 +31,5 @@ public class AuthResponse { private String email; private String name; private String avatarUrl; - private AuthProvider provider; } } diff --git a/backend/src/main/java/com/krrishg/model/AuthProvider.java b/backend/src/main/java/com/krrishg/model/AuthProvider.java index 1660a3c..9cd5101 100644 --- a/backend/src/main/java/com/krrishg/model/AuthProvider.java +++ b/backend/src/main/java/com/krrishg/model/AuthProvider.java @@ -1,5 +1,5 @@ package com.krrishg.model; public enum AuthProvider { - LOCAL, GOOGLE, GITHUB, OPENID + LOCAL } diff --git a/backend/src/main/java/com/krrishg/model/User.java b/backend/src/main/java/com/krrishg/model/User.java index dcb731b..17e2e08 100644 --- a/backend/src/main/java/com/krrishg/model/User.java +++ b/backend/src/main/java/com/krrishg/model/User.java @@ -35,13 +35,6 @@ public class User { @Column(name = "avatar_url") private String avatarUrl; - @Enumerated(EnumType.STRING) - @Column(name = "provider", nullable = false) - private AuthProvider provider; - - @Column(name = "provider_id") - private String providerId; - @Column(name = "email_verified") @Builder.Default private boolean emailVerified = false; diff --git a/backend/src/main/java/com/krrishg/repository/UserRepository.java b/backend/src/main/java/com/krrishg/repository/UserRepository.java index 47e5432..b339324 100644 --- a/backend/src/main/java/com/krrishg/repository/UserRepository.java +++ b/backend/src/main/java/com/krrishg/repository/UserRepository.java @@ -1,6 +1,5 @@ package com.krrishg.repository; -import com.krrishg.model.AuthProvider; import com.krrishg.model.User; import org.springframework.data.jpa.repository.JpaRepository; @@ -11,7 +10,5 @@ public interface UserRepository extends JpaRepository { Optional findByEmail(String email); - Optional findByProviderAndProviderId(AuthProvider provider, String providerId); - boolean existsByEmail(String email); } diff --git a/backend/src/main/java/com/krrishg/service/AuthService.java b/backend/src/main/java/com/krrishg/service/AuthService.java index d7fe19a..88b0d6b 100644 --- a/backend/src/main/java/com/krrishg/service/AuthService.java +++ b/backend/src/main/java/com/krrishg/service/AuthService.java @@ -5,7 +5,6 @@ import com.krrishg.dto.AuthResponse; import com.krrishg.dto.LoginRequest; import com.krrishg.dto.RefreshTokenRequest; import com.krrishg.dto.RegisterRequest; -import com.krrishg.model.AuthProvider; import com.krrishg.model.User; import com.krrishg.repository.UserRepository; import io.jsonwebtoken.Claims; @@ -36,7 +35,6 @@ public class AuthService { .email(email) .password(passwordEncoder.encode(request.getPassword())) .name(request.getName()) - .provider(AuthProvider.LOCAL) .emailVerified(false) .build(); @@ -49,10 +47,6 @@ public class AuthService { User user = userRepository.findByEmail(email) .orElseThrow(() -> new IllegalArgumentException("Invalid email or password")); - if (user.getProvider() != AuthProvider.LOCAL) { - throw new IllegalArgumentException("Please sign in with " + user.getProvider().name().toLowerCase()); - } - if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) { throw new IllegalArgumentException("Invalid email or password"); } @@ -83,7 +77,6 @@ public class AuthService { .email(user.getEmail()) .name(user.getName()) .avatarUrl(user.getAvatarUrl()) - .provider(user.getProvider()) .build(); return AuthResponse.builder() diff --git a/backend/src/main/java/com/krrishg/service/CustomOAuth2User.java b/backend/src/main/java/com/krrishg/service/CustomOAuth2User.java deleted file mode 100644 index 4bac12a..0000000 --- a/backend/src/main/java/com/krrishg/service/CustomOAuth2User.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.krrishg.service; - -import com.krrishg.model.User; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.oauth2.core.user.OAuth2User; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -public class CustomOAuth2User implements OAuth2User { - - private final User user; - private final Map attributes; - - public CustomOAuth2User(User user, Map attributes) { - this.user = user; - this.attributes = attributes; - } - - @Override - public Map getAttributes() { - return attributes; - } - - @Override - public Collection getAuthorities() { - return List.of(new SimpleGrantedAuthority("ROLE_USER")); - } - - @Override - public String getName() { - return user.getId().toString(); - } - - public User getUser() { - return user; - } -} diff --git a/backend/src/main/java/com/krrishg/service/OAuth2SuccessHandler.java b/backend/src/main/java/com/krrishg/service/OAuth2SuccessHandler.java deleted file mode 100644 index 7e61b5b..0000000 --- a/backend/src/main/java/com/krrishg/service/OAuth2SuccessHandler.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.krrishg.service; - -import com.krrishg.config.JwtConfig; -import com.krrishg.model.User; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.security.core.Authentication; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; -import org.springframework.stereotype.Component; - -import java.io.IOException; - -@Component -public class OAuth2SuccessHandler extends SimpleUrlAuthenticationSuccessHandler { - - private final JwtConfig jwtConfig; - - public OAuth2SuccessHandler(JwtConfig jwtConfig) { - this.jwtConfig = jwtConfig; - } - - @Override - public void onAuthenticationSuccess(HttpServletRequest request, - HttpServletResponse response, - Authentication authentication) throws IOException, ServletException { - if (authentication.getPrincipal() instanceof CustomOAuth2User oAuth2User) { - User user = oAuth2User.getUser(); - - String accessToken = jwtConfig.generateAccessToken(user); - String refreshToken = jwtConfig.generateRefreshToken(user); - - String redirectUrl = "http://localhost:4200/oauth2/callback" - + "?accessToken=" + accessToken - + "&refreshToken=" + refreshToken; - - getRedirectStrategy().sendRedirect(request, response, redirectUrl); - } else { - super.onAuthenticationSuccess(request, response, authentication); - } - } -} diff --git a/backend/src/main/java/com/krrishg/service/OAuth2UserService.java b/backend/src/main/java/com/krrishg/service/OAuth2UserService.java deleted file mode 100644 index 416e38d..0000000 --- a/backend/src/main/java/com/krrishg/service/OAuth2UserService.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.krrishg.service; - -import com.krrishg.model.AuthProvider; -import com.krrishg.model.User; -import com.krrishg.repository.UserRepository; -import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; -import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Optional; - -@Service -public class OAuth2UserService extends DefaultOAuth2UserService { - - private final UserRepository userRepository; - - public OAuth2UserService(UserRepository userRepository) { - this.userRepository = userRepository; - } - - @Override - @Transactional - public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { - OAuth2User oAuth2User = super.loadUser(userRequest); - - String registrationId = userRequest.getClientRegistration().getRegistrationId(); - AuthProvider provider = switch (registrationId) { - case "google" -> AuthProvider.GOOGLE; - case "github" -> AuthProvider.GITHUB; - default -> AuthProvider.OPENID; - }; - - String providerId = getProviderId(oAuth2User, provider); - String email = getAttribute(oAuth2User, "email"); - if (email != null) { - email = email.toLowerCase().strip(); - } - String name = getAttribute(oAuth2User, "name"); - String avatarUrl = getAvatarUrl(oAuth2User, provider); - - User user = findOrCreateUser(provider, providerId, email, name, avatarUrl); - return new CustomOAuth2User(user, oAuth2User.getAttributes()); - } - - String getProviderId(OAuth2User oAuth2User, AuthProvider provider) { - return switch (provider) { - case GOOGLE -> oAuth2User.getAttribute("sub"); - case GITHUB -> oAuth2User.getAttribute("id").toString(); - default -> oAuth2User.getName(); - }; - } - - String getAttribute(OAuth2User oAuth2User, String key) { - Object value = oAuth2User.getAttribute(key); - return value != null ? value.toString() : null; - } - - String getAvatarUrl(OAuth2User oAuth2User, AuthProvider provider) { - return switch (provider) { - case GOOGLE -> oAuth2User.getAttribute("picture"); - case GITHUB -> oAuth2User.getAttribute("avatar_url"); - default -> null; - }; - } - - User findOrCreateUser(AuthProvider provider, String providerId, - String email, String name, String avatarUrl) { - Optional existing = userRepository.findByProviderAndProviderId(provider, providerId); - - if (existing.isPresent()) { - User user = existing.get(); - user.setName(name); - if (avatarUrl != null) { - user.setAvatarUrl(avatarUrl); - } - if (email != null && !email.equals(user.getEmail())) { - user.setEmail(email); - } - return userRepository.save(user); - } - - if (email != null) { - Optional emailUser = userRepository.findByEmail(email); - if (emailUser.isPresent()) { - User user = emailUser.get(); - user.setProvider(provider); - user.setProviderId(providerId); - user.setEmailVerified(true); - if (avatarUrl != null) { - user.setAvatarUrl(avatarUrl); - } - return userRepository.save(user); - } - } - - User newUser = User.builder() - .email(email != null ? email : providerId + "@" + provider.name().toLowerCase() + ".indie") - .name(name != null ? name : "User") - .avatarUrl(avatarUrl) - .provider(provider) - .providerId(providerId) - .emailVerified(true) - .build(); - - return userRepository.save(newUser); - } -} diff --git a/backend/src/test/java/com/krrishg/controller/AuthControllerTest.java b/backend/src/test/java/com/krrishg/controller/AuthControllerTest.java index 4d6c5d9..3521b53 100644 --- a/backend/src/test/java/com/krrishg/controller/AuthControllerTest.java +++ b/backend/src/test/java/com/krrishg/controller/AuthControllerTest.java @@ -6,7 +6,6 @@ import com.krrishg.dto.AuthResponse.UserInfo; import com.krrishg.dto.LoginRequest; import com.krrishg.dto.RefreshTokenRequest; import com.krrishg.dto.RegisterRequest; -import com.krrishg.model.AuthProvider; import com.krrishg.service.AuthService; import com.krrishg.support.TestSecurityConfig; import com.fasterxml.jackson.databind.ObjectMapper; @@ -46,7 +45,7 @@ class AuthControllerTest { .accessToken("at-123") .refreshToken("rt-456") .tokenType("Bearer") - .user(new UserInfo(UUID.randomUUID(), "test@example.com", "Test", null, AuthProvider.LOCAL)) + .user(new UserInfo(UUID.randomUUID(), "test@example.com", "Test", null)) .build(); when(authService.register(any(RegisterRequest.class))).thenReturn(response); @@ -81,7 +80,7 @@ class AuthControllerTest { .accessToken("at-123") .refreshToken("rt-456") .tokenType("Bearer") - .user(new UserInfo(UUID.randomUUID(), "login@example.com", "Login User", null, AuthProvider.LOCAL)) + .user(new UserInfo(UUID.randomUUID(), "login@example.com", "Login User", null)) .build(); when(authService.login(any(LoginRequest.class))).thenReturn(response); @@ -119,7 +118,7 @@ class AuthControllerTest { .accessToken("new-at") .refreshToken("new-rt") .tokenType("Bearer") - .user(new UserInfo(UUID.randomUUID(), "user@example.com", "User", null, AuthProvider.LOCAL)) + .user(new UserInfo(UUID.randomUUID(), "user@example.com", "User", null)) .build(); when(authService.refreshToken(any(RefreshTokenRequest.class))).thenReturn(response); diff --git a/backend/src/test/java/com/krrishg/controller/UserSettingsControllerTest.java b/backend/src/test/java/com/krrishg/controller/UserSettingsControllerTest.java index 3f6e971..e7ac9ef 100644 --- a/backend/src/test/java/com/krrishg/controller/UserSettingsControllerTest.java +++ b/backend/src/test/java/com/krrishg/controller/UserSettingsControllerTest.java @@ -4,7 +4,6 @@ import com.krrishg.config.EncryptionService; import com.krrishg.config.GlobalExceptionHandler; import com.krrishg.config.JwtAuthenticationToken; import com.krrishg.config.UserPrincipal; -import com.krrishg.model.AuthProvider; import com.krrishg.model.User; import com.krrishg.repository.UserRepository; import com.krrishg.support.TestSecurityConfig; @@ -61,7 +60,6 @@ class UserSettingsControllerTest { .id(userId) .email("user@test.com") .name("Test User") - .provider(AuthProvider.LOCAL) .build(); } diff --git a/backend/src/test/java/com/krrishg/dto/AuthResponseTest.java b/backend/src/test/java/com/krrishg/dto/AuthResponseTest.java index fc4232b..5ecec8c 100644 --- a/backend/src/test/java/com/krrishg/dto/AuthResponseTest.java +++ b/backend/src/test/java/com/krrishg/dto/AuthResponseTest.java @@ -1,6 +1,5 @@ package com.krrishg.dto; -import com.krrishg.model.AuthProvider; import org.junit.jupiter.api.Test; import java.util.UUID; @@ -12,7 +11,7 @@ class AuthResponseTest { @Test void constructorSetsBearerTokenType() { AuthResponse.UserInfo userInfo = new AuthResponse.UserInfo( - UUID.randomUUID(), "test@example.com", "Test", null, AuthProvider.LOCAL); + UUID.randomUUID(), "test@example.com", "Test", null); AuthResponse response = new AuthResponse("access-token", "refresh-token", userInfo); @@ -28,7 +27,6 @@ class AuthResponseTest { .id(UUID.randomUUID()) .email("user@example.com") .name("User") - .provider(AuthProvider.GOOGLE) .build(); AuthResponse response = AuthResponse.builder() @@ -50,13 +48,11 @@ class AuthResponseTest { .email("avatar@example.com") .name("Avatar User") .avatarUrl("https://example.com/avatar.png") - .provider(AuthProvider.GITHUB) .build(); assertEquals(id, userInfo.getId()); assertEquals("avatar@example.com", userInfo.getEmail()); assertEquals("Avatar User", userInfo.getName()); assertEquals("https://example.com/avatar.png", userInfo.getAvatarUrl()); - assertEquals(AuthProvider.GITHUB, userInfo.getProvider()); } } diff --git a/backend/src/test/java/com/krrishg/model/AuthProviderTest.java b/backend/src/test/java/com/krrishg/model/AuthProviderTest.java index bf28103..ca49843 100644 --- a/backend/src/test/java/com/krrishg/model/AuthProviderTest.java +++ b/backend/src/test/java/com/krrishg/model/AuthProviderTest.java @@ -9,7 +9,7 @@ class AuthProviderTest { @Test void enumValues() { assertArrayEquals( - new AuthProvider[]{AuthProvider.LOCAL, AuthProvider.GOOGLE, AuthProvider.GITHUB, AuthProvider.OPENID}, + new AuthProvider[]{AuthProvider.LOCAL}, AuthProvider.values() ); } diff --git a/backend/src/test/java/com/krrishg/model/UserTest.java b/backend/src/test/java/com/krrishg/model/UserTest.java index efea788..cb7d29e 100644 --- a/backend/src/test/java/com/krrishg/model/UserTest.java +++ b/backend/src/test/java/com/krrishg/model/UserTest.java @@ -14,7 +14,6 @@ class UserTest { User user = new User(); user.setEmail("test@example.com"); user.setName("Test User"); - user.setProvider(AuthProvider.LOCAL); assertNull(user.getId()); assertNull(user.getCreatedAt()); @@ -33,7 +32,6 @@ class UserTest { .id(UUID.randomUUID()) .email("test@example.com") .name("Test") - .provider(AuthProvider.LOCAL) .build(); UUID existingId = user.getId(); @@ -47,7 +45,6 @@ class UserTest { User user = User.builder() .email("test@example.com") .name("Test") - .provider(AuthProvider.LOCAL) .build(); user.onCreate(); @@ -63,7 +60,6 @@ class UserTest { User user = User.builder() .email("test@example.com") .name("Test") - .provider(AuthProvider.LOCAL) .build(); assertFalse(user.isEmailVerified()); diff --git a/backend/src/test/java/com/krrishg/repository/UserRepositoryTest.java b/backend/src/test/java/com/krrishg/repository/UserRepositoryTest.java index af3a129..d5843bf 100644 --- a/backend/src/test/java/com/krrishg/repository/UserRepositoryTest.java +++ b/backend/src/test/java/com/krrishg/repository/UserRepositoryTest.java @@ -1,6 +1,5 @@ package com.krrishg.repository; -import com.krrishg.model.AuthProvider; import com.krrishg.model.User; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -25,7 +24,6 @@ class UserRepositoryTest { User user = User.builder() .email("test@example.com") .name("Test User") - .provider(AuthProvider.LOCAL) .build(); User saved = userRepository.save(user); @@ -42,7 +40,6 @@ class UserRepositoryTest { User user = User.builder() .email("find@example.com") .name("Find Me") - .provider(AuthProvider.LOCAL) .build(); userRepository.save(user); @@ -57,27 +54,11 @@ class UserRepositoryTest { assertTrue(found.isEmpty()); } - @Test - void findByProviderAndProviderId() { - User user = User.builder() - .email("oauth@example.com") - .name("OAuth User") - .provider(AuthProvider.GOOGLE) - .providerId("google-123") - .build(); - userRepository.save(user); - - Optional found = userRepository.findByProviderAndProviderId(AuthProvider.GOOGLE, "google-123"); - assertTrue(found.isPresent()); - assertEquals("oauth@example.com", found.get().getEmail()); - } - @Test void existsByEmail() { User user = User.builder() .email("exists@example.com") .name("Exists") - .provider(AuthProvider.LOCAL) .build(); userRepository.save(user); diff --git a/backend/src/test/java/com/krrishg/service/AuthServiceTest.java b/backend/src/test/java/com/krrishg/service/AuthServiceTest.java index 97f4089..6a14859 100644 --- a/backend/src/test/java/com/krrishg/service/AuthServiceTest.java +++ b/backend/src/test/java/com/krrishg/service/AuthServiceTest.java @@ -4,7 +4,6 @@ import com.krrishg.dto.AuthResponse; import com.krrishg.dto.LoginRequest; import com.krrishg.dto.RefreshTokenRequest; import com.krrishg.dto.RegisterRequest; -import com.krrishg.model.AuthProvider; import com.krrishg.model.User; import com.krrishg.support.FakeJwtConfig; import com.krrishg.support.FakeUserRepository; @@ -39,7 +38,6 @@ class AuthServiceTest { assertEquals("Bearer", response.getTokenType()); assertEquals("new@example.com", response.getUser().getEmail()); assertEquals("New User", response.getUser().getName()); - assertEquals(AuthProvider.LOCAL, response.getUser().getProvider()); } @Test @@ -119,25 +117,6 @@ class AuthServiceTest { assertEquals("Invalid email or password", ex.getMessage()); } - @Test - void loginThrowsOnOAuthUserTryingPasswordLogin() { - User oauthUser = User.builder() - .email("oauth@example.com") - .name("OAuth User") - .provider(AuthProvider.GOOGLE) - .providerId("google-123") - .build(); - userRepository.save(oauthUser); - - LoginRequest login = new LoginRequest(); - login.setEmail("oauth@example.com"); - login.setPassword("anypassword"); - - IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, - () -> authService.login(login)); - assertEquals("Please sign in with google", ex.getMessage()); - } - @Test void refreshTokenReturnsNewTokens() { RegisterRequest reg = new RegisterRequest(); @@ -171,7 +150,6 @@ class AuthServiceTest { User user = User.builder() .email("deleted@example.com") .name("Deleted") - .provider(AuthProvider.LOCAL) .build(); userRepository.save(user); diff --git a/backend/src/test/java/com/krrishg/service/CustomOAuth2UserTest.java b/backend/src/test/java/com/krrishg/service/CustomOAuth2UserTest.java deleted file mode 100644 index 71adb72..0000000 --- a/backend/src/test/java/com/krrishg/service/CustomOAuth2UserTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.krrishg.service; - -import com.krrishg.model.AuthProvider; -import com.krrishg.model.User; -import org.junit.jupiter.api.Test; - -import java.util.Map; -import java.util.UUID; - -import static org.junit.jupiter.api.Assertions.*; - -class CustomOAuth2UserTest { - - private final User user = User.builder() - .id(UUID.randomUUID()) - .email("user@example.com") - .name("Test User") - .provider(AuthProvider.GOOGLE) - .providerId("google-123") - .build(); - - private final Map attributes = Map.of("sub", "google-123", "email", "user@example.com"); - - private final CustomOAuth2User oAuth2User = new CustomOAuth2User(user, attributes); - - @Test - void getNameReturnsUserId() { - assertEquals(user.getId().toString(), oAuth2User.getName()); - } - - @Test - void getAttributesReturnsProvidedMap() { - assertEquals(attributes, oAuth2User.getAttributes()); - } - - @Test - void getAuthoritiesContainsRoleUser() { - var authorities = oAuth2User.getAuthorities(); - assertEquals(1, authorities.size()); - assertEquals("ROLE_USER", authorities.iterator().next().getAuthority()); - } - - @Test - void getUserReturnsTheUser() { - assertSame(user, oAuth2User.getUser()); - } -} diff --git a/backend/src/test/java/com/krrishg/service/OAuth2SuccessHandlerTest.java b/backend/src/test/java/com/krrishg/service/OAuth2SuccessHandlerTest.java deleted file mode 100644 index 65398c9..0000000 --- a/backend/src/test/java/com/krrishg/service/OAuth2SuccessHandlerTest.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.krrishg.service; - -import com.krrishg.config.JwtConfig; -import com.krrishg.model.AuthProvider; -import com.krrishg.model.User; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.security.core.Authentication; -import org.springframework.security.web.RedirectStrategy; - -import java.util.UUID; - -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.*; - -@ExtendWith(MockitoExtension.class) -class OAuth2SuccessHandlerTest { - - @Mock - private JwtConfig jwtConfig; - - @Mock - private HttpServletRequest request; - - @Mock - private HttpServletResponse response; - - @Mock - private Authentication authentication; - - @Mock - private RedirectStrategy redirectStrategy; - - private OAuth2SuccessHandler handler; - - @BeforeEach - void setUp() { - handler = new OAuth2SuccessHandler(jwtConfig); - handler.setRedirectStrategy(redirectStrategy); - } - - @Test - void onAuthenticationSuccessWithOAuth2UserRedirectsWithTokens() throws Exception { - CustomOAuth2User oAuth2User = mock(CustomOAuth2User.class); - User user = User.builder() - .id(UUID.randomUUID()) - .email("user@example.com") - .name("User") - .provider(AuthProvider.GOOGLE) - .build(); - when(authentication.getPrincipal()).thenReturn(oAuth2User); - when(oAuth2User.getUser()).thenReturn(user); - when(jwtConfig.generateAccessToken(user)).thenReturn("access-token-value"); - when(jwtConfig.generateRefreshToken(user)).thenReturn("refresh-token-value"); - - handler.onAuthenticationSuccess(request, response, authentication); - - String expectedRedirect = "http://localhost:4200/oauth2/callback" - + "?accessToken=access-token-value&refreshToken=refresh-token-value"; - verify(redirectStrategy).sendRedirect(request, response, expectedRedirect); - } - - @Test - void onAuthenticationSuccessWithNonOAuth2UserCallsSuper() throws Exception { - when(authentication.getPrincipal()).thenReturn("plain principal"); - - handler.onAuthenticationSuccess(request, response, authentication); - - verify(redirectStrategy).sendRedirect(eq(request), eq(response), anyString()); - } -} diff --git a/backend/src/test/java/com/krrishg/service/OAuth2UserServiceTest.java b/backend/src/test/java/com/krrishg/service/OAuth2UserServiceTest.java deleted file mode 100644 index 947f077..0000000 --- a/backend/src/test/java/com/krrishg/service/OAuth2UserServiceTest.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.krrishg.service; - -import com.krrishg.model.AuthProvider; -import com.krrishg.model.User; -import com.krrishg.support.FakeUserRepository; -import com.krrishg.support.TestOAuth2User; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.*; - -class OAuth2UserServiceTest { - - private FakeUserRepository userRepository; - private OAuth2UserService oAuth2UserService; - - @BeforeEach - void setUp() { - userRepository = new FakeUserRepository(); - oAuth2UserService = new OAuth2UserService(userRepository); - } - - @Test - void getProviderIdForGoogleReturnsSub() { - TestOAuth2User oAuth2User = new TestOAuth2User(Map.of("sub", "google-123")); - assertEquals("google-123", oAuth2UserService.getProviderId(oAuth2User, AuthProvider.GOOGLE)); - } - - @Test - void getProviderIdForGitHubReturnsId() { - TestOAuth2User oAuth2User = new TestOAuth2User(Map.of("id", 456)); - assertEquals("456", oAuth2UserService.getProviderId(oAuth2User, AuthProvider.GITHUB)); - } - - @Test - void getAvatarUrlForGoogleReturnsPicture() { - TestOAuth2User oAuth2User = new TestOAuth2User(Map.of("picture", "https://example.com/avatar.jpg")); - assertEquals("https://example.com/avatar.jpg", - oAuth2UserService.getAvatarUrl(oAuth2User, AuthProvider.GOOGLE)); - } - - @Test - void getAvatarUrlForGitHubReturnsAvatarUrl() { - TestOAuth2User oAuth2User = new TestOAuth2User(Map.of("avatar_url", "https://github.com/avatar.png")); - assertEquals("https://github.com/avatar.png", - oAuth2UserService.getAvatarUrl(oAuth2User, AuthProvider.GITHUB)); - } - - @Test - void getAvatarUrlForOpenIdReturnsNull() { - TestOAuth2User oAuth2User = new TestOAuth2User(Map.of()); - assertNull(oAuth2UserService.getAvatarUrl(oAuth2User, AuthProvider.OPENID)); - } - - @Test - void getAttributeReturnsStringValue() { - TestOAuth2User oAuth2User = new TestOAuth2User(Map.of("email", "test@example.com")); - assertEquals("test@example.com", oAuth2UserService.getAttribute(oAuth2User, "email")); - } - - @Test - void getAttributeReturnsNullWhenMissing() { - TestOAuth2User oAuth2User = new TestOAuth2User(Map.of()); - assertNull(oAuth2UserService.getAttribute(oAuth2User, "missing")); - } - - @Test - void findOrCreateUserCreatesNewUser() { - User user = oAuth2UserService.findOrCreateUser( - AuthProvider.GOOGLE, "google-789", - "new@example.com", "New User", "https://example.com/avatar.jpg"); - - assertNotNull(user.getId()); - assertEquals("new@example.com", user.getEmail()); - assertEquals("New User", user.getName()); - assertEquals(AuthProvider.GOOGLE, user.getProvider()); - assertEquals("google-789", user.getProviderId()); - assertTrue(user.isEmailVerified()); - } - - @Test - void findOrCreateUserUpdatesExistingOAuthUser() { - User existing = User.builder() - .email("existing@example.com") - .name("Old Name") - .provider(AuthProvider.GOOGLE) - .providerId("google-456") - .build(); - userRepository.save(existing); - - User updated = oAuth2UserService.findOrCreateUser( - AuthProvider.GOOGLE, "google-456", - "existing@example.com", "Updated Name", "https://example.com/new-avatar.jpg"); - - assertEquals("Updated Name", updated.getName()); - assertEquals("https://example.com/new-avatar.jpg", updated.getAvatarUrl()); - } - - @Test - void findOrCreateUserLinksLocalUserToOAuth() { - User localUser = User.builder() - .email("link@example.com") - .name("Link User") - .provider(AuthProvider.LOCAL) - .password("hashedpw") - .build(); - userRepository.save(localUser); - - User linked = oAuth2UserService.findOrCreateUser( - AuthProvider.GITHUB, "github-789", - "link@example.com", "Link User", "https://github.com/avatar.png"); - - assertEquals(AuthProvider.GITHUB, linked.getProvider()); - assertEquals("github-789", linked.getProviderId()); - assertTrue(linked.isEmailVerified()); - } - - @Test - void findOrCreateUserGeneratesEmailWhenMissing() { - User user = oAuth2UserService.findOrCreateUser( - AuthProvider.GITHUB, "github-nomail", - null, "No Email", null); - - assertTrue(user.getEmail().contains("github-nomail")); - assertTrue(user.getEmail().endsWith("@github.indie")); - } -} diff --git a/backend/src/test/java/com/krrishg/support/FakeUserRepository.java b/backend/src/test/java/com/krrishg/support/FakeUserRepository.java index 893399c..8656b4c 100644 --- a/backend/src/test/java/com/krrishg/support/FakeUserRepository.java +++ b/backend/src/test/java/com/krrishg/support/FakeUserRepository.java @@ -1,6 +1,5 @@ package com.krrishg.support; -import com.krrishg.model.AuthProvider; import com.krrishg.model.User; import com.krrishg.repository.UserRepository; @@ -32,13 +31,6 @@ public class FakeUserRepository extends InMemoryRepository implement .findFirst(); } - @Override - public Optional findByProviderAndProviderId(AuthProvider provider, String providerId) { - return store.values().stream() - .filter(u -> u.getProvider() == provider && providerId.equals(u.getProviderId())) - .findFirst(); - } - @Override public boolean existsByEmail(String email) { return store.values().stream() diff --git a/backend/src/test/java/com/krrishg/support/TestOAuth2User.java b/backend/src/test/java/com/krrishg/support/TestOAuth2User.java deleted file mode 100644 index 93ac29b..0000000 --- a/backend/src/test/java/com/krrishg/support/TestOAuth2User.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.krrishg.support; - -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.oauth2.core.user.OAuth2User; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -public class TestOAuth2User implements OAuth2User { - - private final Map attributes; - - public TestOAuth2User(Map attributes) { - this.attributes = attributes; - } - - @Override - public Map getAttributes() { - return attributes; - } - - @Override - public Collection getAuthorities() { - return List.of(new SimpleGrantedAuthority("ROLE_USER")); - } - - @Override - public String getName() { - Object sub = attributes.get("sub"); - if (sub != null) return sub.toString(); - Object id = attributes.get("id"); - if (id != null) return id.toString(); - return "test-user"; - } -} diff --git a/frontend/nginx.conf b/frontend/nginx.conf index 0a044f4..51a4c17 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -11,12 +11,4 @@ server { location /api/ { proxy_pass http://backend:8080; } - - location /oauth2/ { - proxy_pass http://backend:8080; - } - - location /login/ { - proxy_pass http://backend:8080; - } } diff --git a/frontend/proxy.conf.json b/frontend/proxy.conf.json index 92df52a..62a2dd0 100644 --- a/frontend/proxy.conf.json +++ b/frontend/proxy.conf.json @@ -2,9 +2,5 @@ "/api": { "target": "http://127.0.0.1:8080", "secure": false - }, - "/oauth2": { - "target": "http://127.0.0.1:8080", - "secure": false } } diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index 4e91faa..9a627db 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -14,10 +14,6 @@ export const routes: Routes = [ loadComponent: () => import('./auth/register/register.component').then(m => m.RegisterComponent), canActivate: [LoginGuard], }, - { - path: 'oauth2/callback', - loadComponent: () => import('./auth/oauth-callback/oauth-callback.component').then(m => m.OAuthCallbackComponent), - }, { path: 'dashboard', loadComponent: () => import('./dashboard/dashboard.component').then(m => m.DashboardComponent), diff --git a/frontend/src/app/auth/login/login.component.ts b/frontend/src/app/auth/login/login.component.ts index bed01a2..c4d54c2 100644 --- a/frontend/src/app/auth/login/login.component.ts +++ b/frontend/src/app/auth/login/login.component.ts @@ -6,7 +6,6 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatButtonModule } from '@angular/material/button'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; -import { MatDividerModule } from '@angular/material/divider'; import { AuthService } from '../../core/services/auth.service'; import { NgIf } from '@angular/common'; @@ -16,7 +15,7 @@ import { NgIf } from '@angular/common'; imports: [ RouterLink, ReactiveFormsModule, NgIf, MatCardModule, MatFormFieldModule, MatInputModule, MatButtonModule, - MatProgressSpinnerModule, MatDividerModule, + MatProgressSpinnerModule, ], template: `
@@ -48,19 +47,6 @@ import { NgIf } from '@angular/common'; -
- -
- - -

Don't have an account? Register diff --git a/frontend/src/app/auth/oauth-callback/oauth-callback.component.ts b/frontend/src/app/auth/oauth-callback/oauth-callback.component.ts deleted file mode 100644 index f761c38..0000000 --- a/frontend/src/app/auth/oauth-callback/oauth-callback.component.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { AuthService } from '../../core/services/auth.service'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; - -@Component({ - selector: 'app-oauth-callback', - standalone: true, - imports: [MatProgressSpinnerModule], - template: ` -

-
- -

Completing sign in...

-
-
- `, -}) -export class OAuthCallbackComponent implements OnInit { - constructor( - private route: ActivatedRoute, - private router: Router, - private authService: AuthService, - ) {} - - ngOnInit(): void { - const accessToken = this.route.snapshot.queryParamMap.get('accessToken'); - const refreshToken = this.route.snapshot.queryParamMap.get('refreshToken'); - - if (accessToken && refreshToken) { - this.authService.setSessionFromOAuth(accessToken, refreshToken); - this.router.navigate(['/dashboard']); - } else { - this.router.navigate(['/login']); - } - } -} diff --git a/frontend/src/app/core/models/auth-response.ts b/frontend/src/app/core/models/auth-response.ts index 40682e3..e30bb20 100644 --- a/frontend/src/app/core/models/auth-response.ts +++ b/frontend/src/app/core/models/auth-response.ts @@ -10,5 +10,4 @@ export interface UserInfo { email: string; name: string; avatarUrl: string; - provider: 'LOCAL' | 'GOOGLE' | 'GITHUB' | 'OPENID'; } diff --git a/frontend/src/app/core/services/auth.service.ts b/frontend/src/app/core/services/auth.service.ts index 51f60bc..16a00e0 100644 --- a/frontend/src/app/core/services/auth.service.ts +++ b/frontend/src/app/core/services/auth.service.ts @@ -50,23 +50,6 @@ export class AuthService { return !!this.getAccessToken(); } - setSessionFromOAuth(accessToken: string, refreshToken: string): void { - localStorage.setItem(this.ACCESS_TOKEN_KEY, accessToken); - localStorage.setItem(this.REFRESH_TOKEN_KEY, refreshToken); - const payload = this.decodeToken(accessToken); - if (payload) { - const user: UserInfo = { - id: payload.sub, - email: payload.email, - name: payload.name, - avatarUrl: payload.avatarUrl || '', - provider: payload.provider || 'LOCAL', - }; - localStorage.setItem(this.USER_KEY, JSON.stringify(user)); - this.userSubject.next(user); - } - } - clearSessionForLogout(): void { this.clearSession(); } @@ -90,12 +73,4 @@ export class AuthService { return data ? JSON.parse(data) : null; } - private decodeToken(token: string): any { - try { - const parts = token.split('.'); - return JSON.parse(atob(parts[1])); - } catch { - return null; - } - } }