Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auth role Integratefor User Service Communication in Authentication Service #3

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1eef0f6
models complete, invoice to be managed via service-to-service communi…
felixojiambo Dec 15, 2024
dae8caf
corrected imports
felixojiambo Dec 15, 2024
66ced93
interfaces to handle CRUD operations for each entity
felixojiambo Dec 15, 2024
07a2456
pom and properties
felixojiambo Dec 15, 2024
0580f81
Removed the Invoice entity from the Payment Service models.
felixojiambo Dec 15, 2024
7d76bd1
dtos
felixojiambo Dec 15, 2024
d657679
Define the contract for the Payment Service.
felixojiambo Dec 15, 2024
5df67e1
error response
felixojiambo Dec 15, 2024
ab1711a
exception hanler globally
felixojiambo Dec 15, 2024
af0c527
security config
felixojiambo Dec 15, 2024
ac402aa
common interface that all payment gateway clients will implement
felixojiambo Dec 16, 2024
2426b9a
dtos for payment gateways
felixojiambo Dec 16, 2024
f14bdea
create a concrete implementation of the PaymentGateway interface for …
felixojiambo Dec 16, 2024
bd9c41b
class maps the M-Pesa properties from application.yml to Java objects.
felixojiambo Dec 16, 2024
e0b9588
feat(payment-service): Implement CardPaymentRequest DTO with validati…
felixojiambo Dec 30, 2024
8798cac
PaymentRequest.builder()
felixojiambo Dec 30, 2024
88cf5f4
provide contructor
felixojiambo Dec 30, 2024
a4f582e
Remove the unused dummyPaymentRequest and directly construct the Paym…
felixojiambo Dec 30, 2024
37b9e00
ignore invoice service, require config later once service is up.
felixojiambo Dec 30, 2024
793eff4
correct Stripe SDK
felixojiambo Dec 31, 2024
dc4abba
feat(kafka): Add Kafka producer configuration
felixojiambo Dec 31, 2024
71841a5
kafka dependecy
felixojiambo Dec 31, 2024
665b535
feat(kafka): Implement Kafka producer service
felixojiambo Dec 31, 2024
f9d7490
feat(auth): Add user registration endpoint with Kafka integration
felixojiambo Dec 31, 2024
d175f02
feat(kafka): Add Kafka consumer configuration for authentication service
felixojiambo Dec 31, 2024
12fed9d
kafka dependecy
felixojiambo Dec 31, 2024
2fac503
Add an Endpoint to Fetch User by Email
felixojiambo Dec 31, 2024
20b59ad
Implement complete user management and authentication system:
felixojiambo Dec 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions authentication-service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>

</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down Expand Up @@ -79,6 +84,12 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<version>4.2.0-RC1</version>
<scope>compile</scope>
</dependency>


</dependencies>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.finpay.authentication.clients;
import com.finpay.authentication.dtos.UserDto;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.Optional;

@Component
public class UserServiceClient {

private final RestTemplate restTemplate;
private final String apiGatewayBaseUrl = "http://api-gateway"; // API Gateway base URL
private final String userServicePath = "/user-service/api/users"; // Path to User Service in the gateway

public UserServiceClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}

/**
* Fetch a user by email from the User Service via the API Gateway.
*
* @param email the email of the user
* @return an Optional containing UserDto if the user exists, or empty if not found
*/
public Optional<UserDto> getUserByEmail(String email) {
String url = UriComponentsBuilder.fromHttpUrl(apiGatewayBaseUrl + userServicePath)
.queryParam("email", email)
.toUriString();
try {
UserDto userDto = restTemplate.getForObject(url, UserDto.class);
return Optional.ofNullable(userDto);
} catch (Exception e) {
// Log error or handle exception as needed
return Optional.empty();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.finpay.authentication.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class AppConfig {

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package com.finpay.authentication.controllers;

import com.finpay.authentication.dtos.JwtResponse;
import com.finpay.authentication.dtos.LoginRequest;
import com.finpay.authentication.dtos.MessageResponse;
import com.finpay.authentication.dtos.SignupRequest;
import com.finpay.authentication.models.User;
import com.finpay.authentication.repository.UserRepository;
import com.finpay.authentication.clients.UserServiceClient;
import com.finpay.authentication.dtos.*;
import com.finpay.authentication.utils.JwtUtil;
import org.springframework.http.*;
import org.springframework.security.authentication.*;
Expand All @@ -14,67 +10,46 @@
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;

import java.util.UUID;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

private final AuthenticationManager authenticationManager;
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final JwtUtil jwtUtil;
private final UserServiceClient userServiceClient; // Add UserServiceClient

// Constructor-based injection
public AuthController(AuthenticationManager authenticationManager,
UserRepository userRepository,
PasswordEncoder passwordEncoder,
JwtUtil jwtUtil) {
JwtUtil jwtUtil,
UserServiceClient userServiceClient) {
this.authenticationManager = authenticationManager;
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.jwtUtil = jwtUtil;
}

@PostMapping("/register")
public ResponseEntity<?> registerUser(@RequestBody SignupRequest signupRequest) {
if (userRepository.existsByEmail(signupRequest.getEmail())) {
return ResponseEntity
.badRequest()
.body(new MessageResponse("Error: Email is already in use!"));
}

// Create new user
User user = User.builder()
.id(UUID.randomUUID())
.name(signupRequest.getName())
.email(signupRequest.getEmail())
.password(passwordEncoder.encode(signupRequest.getPassword()))
.build();

userRepository.save(user);

return ResponseEntity.ok(new MessageResponse("User registered successfully!"));
this.userServiceClient = userServiceClient; // Initialize UserServiceClient
}

@PostMapping("/login")
public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) {
public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) {
try {
// Authenticate user
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getEmail(),
loginRequest.getPassword()
)
);

// If authentication is successful
String jwt = jwtUtil.generateJwtToken(loginRequest.getEmail());

User user = userRepository.findByEmail(loginRequest.getEmail())
// Fetch user details from User Service
UserDto userDto = userServiceClient.getUserByEmail(loginRequest.getEmail())
.orElseThrow(() -> new UsernameNotFoundException("User not found"));

JwtResponse jwtResponse = new JwtResponse(jwt, user.getId(), user.getName(), user.getEmail());
// Generate JWT with email and roles
String jwt = jwtUtil.generateJwtToken(loginRequest.getEmail(), userDto.getRoles());

// Build JWT response
JwtResponse jwtResponse = new JwtResponse(jwt, userDto.getId(), userDto.getName(), userDto.getEmail());
return ResponseEntity.ok(jwtResponse);

} catch (BadCredentialsException e) {
Expand All @@ -83,4 +58,6 @@ public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest
.body(new MessageResponse("Error: Invalid email or password"));
}
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.finpay.authentication.dtos;

import lombok.Data;

import java.util.Set;
import java.util.UUID;

@Data
public class UserDto {
private UUID id;
private String email;
private String password;
private String name;
private Set<String> roles;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//package com.finpay.authentication.dtos;
//
//import lombok.Data;
//import java.util.UUID;
//
//@Data
//public class UserEvent {
// private UUID id;
// private String email;
// private String roles;
//}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.finpay.authentication.services.UserDetailsServiceImpl;
import com.finpay.authentication.utils.JwtUtil;
import org.springframework.security.authentication.*;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.filter.OncePerRequestFilter;
Expand All @@ -11,6 +12,9 @@
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
Expand Down Expand Up @@ -38,21 +42,18 @@ protected void doFilterInternal(HttpServletRequest request,
}

if (email != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(email);
if (jwtUtil.validateJwtToken(jwt)) {
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
authToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request)
);
SecurityContextHolder.getContext().setAuthentication(authToken);
}
Set<String> roles = jwtUtil.getRolesFromJwtToken(jwt); // Add method to extract roles
List<SimpleGrantedAuthority> authorities = roles.stream()
.map(SimpleGrantedAuthority::new)
.toList();

UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(email, null, authorities);

SecurityContextHolder.getContext().setAuthentication(authToken);
}

filterChain.doFilter(request, response);
}

}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,38 +1,34 @@
package com.finpay.authentication.services;

import com.finpay.authentication.models.User;
import com.finpay.authentication.repository.UserRepository;
import org.springframework.security.core.*;
import com.finpay.authentication.clients.UserServiceClient;
import com.finpay.authentication.dtos.UserDto;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.*;
import org.springframework.stereotype.Service;

import java.util.*;

@Service

public class UserDetailsServiceImpl implements UserDetailsService {

private final UserRepository userRepository;
private final UserServiceClient userServiceClient; // REST or gRPC client to fetch user data

// Constructor-based injection
public UserDetailsServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
public UserDetailsServiceImpl(UserServiceClient userServiceClient) {
this.userServiceClient = userServiceClient;
}

@Override
public UserDetails loadUserByUsername(String email)
throws UsernameNotFoundException {
User user = userRepository.findByEmail(email)
.orElseThrow(() ->
new UsernameNotFoundException("User Not Found with email: " + email));
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
UserDto userDto = userServiceClient.getUserByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("User Not Found with email: " + email));

// For simplicity, all users have ROLE_USER
List<GrantedAuthority> authorities =
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
List<SimpleGrantedAuthority> authorities = userDto.getRoles().stream()
.map(SimpleGrantedAuthority::new)
.toList();

return new org.springframework.security.core.userdetails.User(
user.getEmail(),
user.getPassword(),
userDto.getEmail(),
userDto.getPassword(),
authorities
);
}
Expand Down
Loading