diff --git a/pom.xml b/pom.xml
index ed47212..6dcea8e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -44,6 +44,11 @@
runtime
true
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-resource-server
+ 2.4.12
+
org.postgresql
postgresql
@@ -83,6 +88,32 @@
3.3.4
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.11.5
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.11.5
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.11.5
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
diff --git a/src/main/java/_11/asktpk/artisanconnectbackend/config/AppConfig.java b/src/main/java/_11/asktpk/artisanconnectbackend/config/AppConfig.java
new file mode 100644
index 0000000..9636a39
--- /dev/null
+++ b/src/main/java/_11/asktpk/artisanconnectbackend/config/AppConfig.java
@@ -0,0 +1,15 @@
+package _11.asktpk.artisanconnectbackend.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+@Configuration
+public class AppConfig {
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/_11/asktpk/artisanconnectbackend/config/SecurityConfig.java b/src/main/java/_11/asktpk/artisanconnectbackend/config/SecurityConfig.java
new file mode 100644
index 0000000..bf88946
--- /dev/null
+++ b/src/main/java/_11/asktpk/artisanconnectbackend/config/SecurityConfig.java
@@ -0,0 +1,38 @@
+package _11.asktpk.artisanconnectbackend.config;
+
+import _11.asktpk.artisanconnectbackend.security.JwtRequestFilter;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig {
+
+ private final JwtRequestFilter jwtRequestFilter;
+
+ public SecurityConfig(JwtRequestFilter jwtRequestFilter) {
+ this.jwtRequestFilter = jwtRequestFilter;
+ }
+
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ http
+ .cors(cors -> cors.configure(http))
+ .csrf(AbstractHttpConfigurer::disable)
+ .authorizeHttpRequests(auth -> auth
+ .requestMatchers("/api/v1/auth/**").permitAll()
+ .anyRequest().authenticated())
+ .sessionManagement(session -> session
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS));
+
+ http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
+
+ return http.build();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/_11/asktpk/artisanconnectbackend/controller/AuthController.java b/src/main/java/_11/asktpk/artisanconnectbackend/controller/AuthController.java
new file mode 100644
index 0000000..231f1df
--- /dev/null
+++ b/src/main/java/_11/asktpk/artisanconnectbackend/controller/AuthController.java
@@ -0,0 +1,126 @@
+package _11.asktpk.artisanconnectbackend.controller;
+
+import _11.asktpk.artisanconnectbackend.dto.*;
+import _11.asktpk.artisanconnectbackend.entities.Client;
+import _11.asktpk.artisanconnectbackend.security.JwtUtil;
+import _11.asktpk.artisanconnectbackend.service.ClientService;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.*;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.Map;
+
+@Slf4j
+@RestController
+@RequestMapping("/api/v1/auth")
+public class AuthController {
+
+ private final ClientService clientService;
+ private final JwtUtil jwtUtil;
+
+ public AuthController(ClientService clientService, JwtUtil jwtUtil) {
+ this.clientService = clientService;
+ this.jwtUtil = jwtUtil;
+ }
+
+ @PostMapping("/login")
+ public ResponseEntity login(@RequestBody AuthRequestDTO authRequestDTO) {
+ if (clientService.checkClientCredentials(authRequestDTO)) {
+ Client client = clientService.getClientByEmail(authRequestDTO.getEmail());
+ Long userId = client.getId();
+ String userRole = client.getRole().getRole();
+
+ String token = jwtUtil.generateToken(client.getEmail(), userRole, userId);
+
+ log.info("User logged in with {}", client.getEmail());
+ return ResponseEntity.status(HttpStatus.OK)
+ .body(new AuthResponseDTO(userId, userRole, token));
+ } else {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
+ }
+ }
+
+ @PostMapping("/register")
+ public ResponseEntity register(@RequestBody ClientRegistrationDTO clientDTO) {
+ if (clientService.getClientByEmail(clientDTO.getEmail()) != null) {
+ return ResponseEntity.status(HttpStatus.CONFLICT).build();
+ }
+
+ ClientDTO savedClient = clientService.registerClient(clientDTO);
+
+ String token = jwtUtil.generateToken(
+ savedClient.getEmail(),
+ savedClient.getRole(),
+ savedClient.getId()
+ );
+
+ log.info("New user registered with {}", savedClient.getEmail());
+
+ return ResponseEntity.status(HttpStatus.CREATED)
+ .body(new AuthResponseDTO(
+ savedClient.getId(),
+ savedClient.getRole(),
+ token
+ ));
+ }
+
+ @PostMapping("/logout")
+ public ResponseEntity logout(HttpServletRequest request) {
+ String authHeader = request.getHeader("Authorization");
+
+ if (authHeader != null && authHeader.startsWith("Bearer ")) {
+ String token = authHeader.substring(7);
+ jwtUtil.blacklistToken(token);
+ return ResponseEntity.ok(new RequestResponseDTO("Successfully logged out"));
+ }
+
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new RequestResponseDTO("Invalid token"));
+ }
+
+ @PostMapping("/google")
+ public ResponseEntity> authenticateWithGoogle(@RequestBody GoogleAuthRequestDTO dto) {
+ try {
+ String accessToken = dto.getGoogleToken();
+ String googleUserInfoUrl = "https://www.googleapis.com/oauth2/v3/userinfo";
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.setBearerAuth(accessToken);
+ HttpEntity entity = new HttpEntity<>(headers);
+
+ RestTemplate restTemplate = new RestTemplate();
+ ResponseEntity