Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8d948b5
Added some things for better code smell
Flanderzz May 3, 2025
b6859df
Changed subject of jwt to be ID and not email
Flanderzz May 13, 2025
9dd6537
Merge branch 'master' of https://github.com/Teamsphereco/TeamSphere-A…
Flanderzz May 13, 2025
5fc08b5
Merge branch 'master' of https://github.com/Teamsphereco/TeamSphere-A…
Flanderzz Jun 17, 2025
e3699c6
Final touches for deleting a Message
Flanderzz Jun 20, 2025
1deba9f
adding null checking within validation
Flanderzz Jun 21, 2025
cb3af83
Hopefully fixing JWTTokenValidatorTest.java
Flanderzz Jul 7, 2025
bd5db65
Update JWTTokenProvider.java
Flanderzz Jul 7, 2025
702a12d
Update JWTTokenValidatorTest.java
Flanderzz Jul 7, 2025
c759034
Update JWTTokenValidatorTest.java
Flanderzz Jul 7, 2025
f4050e0
Update JWTTokenValidatorTest.java
Flanderzz Jul 7, 2025
cd7d230
Update JWTTokenValidatorTest.java
Flanderzz Jul 7, 2025
7d26435
Update JWTTokenValidatorTest.java
Flanderzz Jul 7, 2025
3c540da
fixing JWTTokenValidatorTest.java
Flanderzz Jul 7, 2025
9ad6b72
IHATEMOCKITO
Flanderzz Jul 7, 2025
633a638
IHATIEMOCKITOTYPEISSUESP2
Flanderzz Jul 7, 2025
90479fe
Update JWTTokenValidatorTest.java
Flanderzz Jul 7, 2025
3963abb
Update JWTTokenValidatorTest.java
Flanderzz Jul 7, 2025
7015c9f
Update JWTTokenValidatorTest.java
Flanderzz Jul 7, 2025
5d8c701
To fix code duplication by creating helper method to parse the claims
BravinR Jul 13, 2025
06f5217
To improve error handling in the JWT parsing logic
BravinR Jul 13, 2025
389d3d5
To remove unused import
BravinR Jul 13, 2025
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
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
package co.teamsphere.api.config;

import java.security.PrivateKey;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;

import co.teamsphere.api.config.properties.JwtProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.security.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;


import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.stereotype.Service;

import co.teamsphere.api.config.properties.JwtProperties;
import java.security.PrivateKey;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.UUID;

@Service
@Slf4j
Expand All @@ -29,32 +31,34 @@ public JWTTokenProvider(PrivateKey privateKey, JwtProperties jwtProperties) {

}

public String generateJwtToken(Authentication authentication) {
public String generateJwtToken(Authentication authentication, UUID userId) {
log.info("Generating JWT...");
var currentDate = new Date();

String authoritiesString = populateAuthorities(authentication.getAuthorities());

return Jwts.builder()
.setHeaderParam("typ", "JWT")
.setIssuer("Teamsphere.co")
.setSubject(authentication.getName())
.setSubject(userId.toString())
.setAudience(jwtProperties.getAudience())
.setIssuedAt(currentDate)
.setNotBefore(currentDate)
.setExpiration(new Date(currentDate.getTime()+86400000))
.claim("email", authentication.getName())
.claim("authorities", "ROLE_USER")
.claim("authorities", authoritiesString)
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
}

public String generateJwtTokenFromEmail(String email) {
public String generateJwtTokenFromEmail(String email, UUID userId) {
log.info("Generating JWT...");
var currentDate = new Date();

return Jwts.builder()
.setHeaderParam("typ", "JWT")
.setIssuer("Teamsphere.co")
.setSubject(email)
.setSubject(userId.toString())
.setAudience(jwtProperties.getAudience())
.setIssuedAt(currentDate)
.setNotBefore(currentDate)
Expand All @@ -66,24 +70,55 @@ public String generateJwtTokenFromEmail(String email) {
}

public String getEmailFromToken(String token) {
log.info("parsing claims ----------- ");

token = token.substring(7);

Claims claims= Jwts.parserBuilder()
.setSigningKey(privateKey)
.build()
.parseClaimsJws(token)
.getBody();

Claims claims = parseTokenForClaims(token);
return String.valueOf(claims.get("email"));
}

public UUID getIdFromToken(String token) {
Claims claims = parseTokenForClaims(token);
return UUID.fromString(claims.getSubject());
}

public String populateAuthorities(Collection<? extends GrantedAuthority> collection) {
var authoritieSet = new HashSet<String>();
var authoritiesSet = new HashSet<String>();
for(GrantedAuthority authority:collection) {
authoritieSet.add(authority.getAuthority());
authoritiesSet.add(authority.getAuthority());
}
return String.join(",", authoritieSet);
return String.join(",", authoritiesSet);
}

private Claims parseTokenForClaims(String token) {
log.info("Parsing claims for token...");

if (token == null || !token.startsWith("Bearer ")) {
log.error("Invalid token format: missing 'Bearer ' prefix or token is null");
throw new IllegalArgumentException("Invalid token format");
}

String actualToken = token.substring(7);

try {
return Jwts.parserBuilder()
.setSigningKey(privateKey)
.build()
.parseClaimsJws(actualToken)
.getBody();
} catch (ExpiredJwtException e) {
log.warn("Expired JWT token: {}", e.getMessage());
throw e; // Re-throw the specific ExpiredJwtException
} catch (MalformedJwtException e) {
log.warn("Invalid JWT token: {}", e.getMessage());
throw e; // Re-throw the specific MalformedJwtException
} catch (SignatureException e) {
log.warn("Invalid JWT signature: {}", e.getMessage());
throw e; // Re-throw the specific SignatureException
} catch (UnsupportedJwtException e) {
log.warn("Unsupported JWT token: {}", e.getMessage());
throw e; // Re-throw the specific UnsupportedJwtException
} catch (Exception e) {
log.error("Unexpected error parsing JWT token: {}", e.getMessage());
throw new RuntimeException("Error parsing JWT token", e); // Catch any other unexpected errors
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ protected void doFilterInternal(
@NotNull HttpServletResponse response,
@NotNull FilterChain filterChain
) throws ServletException, IOException {
String jwt =request.getHeader(JWTTokenConst.HEADER);
String jwt = request.getHeader(JWTTokenConst.HEADER);

if (jwt != null && jwt.startsWith("Bearer ")) {
try {
Expand All @@ -59,9 +59,13 @@ protected void doFilterInternal(
throw new JwtException("Invalid audience: " + audience);
}

String username = claim.getSubject();
String username = claim.get("email", String.class);
String authorities = claim.get("authorities", String.class);

if (username == null || authorities == null) {
throw new JwtException("Missing email or authorities in JWT claims");
}

List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList(authorities);

Authentication auth = new UsernamePasswordAuthenticationToken(username, null ,auths);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public ResponseEntity<?> refreshTest(@RequestBody RefreshTokenRequest request) t

log.info("Generating new JWT token");
var user = rToken.getUser();
String jwtToken = jwtTokenProvider.generateJwtTokenFromEmail(user.getEmail());
String jwtToken = jwtTokenProvider.generateJwtTokenFromEmail(user.getEmail(), user.getId());
String newRefreshToken = refreshTokenService.replaceRefreshToken(user.getEmail());
if (newRefreshToken == null) {
log.error("Error during refresh token replacement");
Expand Down Expand Up @@ -276,17 +276,18 @@ public ResponseEntity<AuthResponse> authenticateWithGoogleMethod(
log.info("New user created with email: {}", email);
}

// Just incase
if (googleUser == null) {
log.error("Error during Google authentication, user still came out as null");
return new ResponseEntity<>(new AuthResponse("This is still!", "", false), HttpStatus.INTERNAL_SERVER_ERROR);
log.error("Error during Google authentication, user still came out as null!");
throw new Exception("Error during Google authentication!");
}

// Load UserDetails and set authentication context
Authentication authentication = new UsernamePasswordAuthenticationToken(email, null);
SecurityContextHolder.getContext().setAuthentication(authentication);

// Generate JWT token
String token = jwtTokenProvider.generateJwtToken(authentication);
String token = jwtTokenProvider.generateJwtToken(authentication, googleUser.getId());
RefreshToken refreshToken = createRefreshToken(googleUser.getId().toString(), email);

AuthResponse authResponse = new AuthResponse(token, refreshToken.getRefreshToken(), true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import co.teamsphere.api.DTO.ChatDTO;
import co.teamsphere.api.DTO.ChatSummaryDTO;
import co.teamsphere.api.DTOmapper.ChatDTOMapper;
import co.teamsphere.api.config.JWTTokenProvider;
import co.teamsphere.api.exception.ChatException;
import co.teamsphere.api.exception.UserException;
import co.teamsphere.api.models.Chat;
Expand Down Expand Up @@ -42,16 +43,20 @@ public class ChatController {
private final ChatService chatService;

private final UserService userService;

private final ChatDTOMapper chatDTOMapper;

private final JWTTokenProvider jwtTokenProvider;

public ChatController(ChatService chatService,
UserService userService,
ChatDTOMapper chatDTOMapper
ChatDTOMapper chatDTOMapper,
JWTTokenProvider jwtTokenProvider
) {
this.chatService = chatService;
this.userService = userService;
this.chatDTOMapper = chatDTOMapper;
this.jwtTokenProvider = jwtTokenProvider;
}

@PostMapping("/single")
Expand All @@ -75,8 +80,8 @@ public ChatController(ChatService chatService,
public ResponseEntity<ChatDTO> creatChatHandler(@RequestBody SingleChatRequest singleChatRequest,
@RequestHeader("Authorization") String jwt) throws UserException {
log.info("single chat --------");
User reqUser = userService.findUserProfile(jwt);
Chat chat = chatService.createChat(reqUser.getId(),singleChatRequest.getUserId(),false);
UUID reqUserId = jwtTokenProvider.getIdFromToken(jwt);
Chat chat = chatService.createChat(reqUserId, singleChatRequest.getUserId(),false);
ChatDTO chatDto = chatDTOMapper.toChatDto(chat);
return new ResponseEntity<>(chatDto, HttpStatus.OK);
}
Expand All @@ -100,8 +105,8 @@ public ResponseEntity<ChatDTO> creatChatHandler(@RequestBody SingleChatRequest s
public ResponseEntity<ChatDTO> createGroupHandler(@RequestBody GroupChatRequest groupChatRequest,
@RequestHeader("Authorization") String jwt)
throws UserException {
User reqUser = userService.findUserProfile(jwt);
Chat chat = chatService.createGroup(groupChatRequest, reqUser.getId());
UUID reqUserId = jwtTokenProvider.getIdFromToken(jwt);
Chat chat = chatService.createGroup(groupChatRequest, reqUserId);
ChatDTO chatDto = chatDTOMapper.toChatDto(chat);
return new ResponseEntity<>(chatDto, HttpStatus.OK);
}
Expand Down Expand Up @@ -144,9 +149,11 @@ public ResponseEntity<ChatDTO> findChatByIdHandler(@PathVariable UUID chatId) th
@ApiResponse(responseCode = "403", description = "Unauthorized action")
})
public ResponseEntity<ChatDTO> addUserToGroupHandler(@PathVariable UUID chatId,
@PathVariable UUID userId)
@PathVariable UUID userId,
@RequestHeader("Authorization") String jwt)
throws UserException, ChatException {
Chat chat = chatService.addUserToGroup(userId, chatId);
User reqUser = userService.findUserProfile(jwt);
Chat chat = chatService.addUserToGroup(userId, chatId, reqUser);
ChatDTO chatDto = chatDTOMapper.toChatDto(chat);
return new ResponseEntity<>(chatDto, HttpStatus.OK);
}
Expand All @@ -169,10 +176,9 @@ public ResponseEntity<ChatDTO> addUserToGroupHandler(@PathVariable UUID chatId,
})
public ResponseEntity<ChatDTO> renameGroupHandler(@PathVariable UUID chatId,
@RequestBody RenameGroupChatRequest renameGroupRequest,
@RequestHeader("Authorization") String jwt)
throws ChatException, UserException {
User reqUser = userService.findUserProfile(jwt);
Chat chat = chatService.renameGroup(chatId, renameGroupRequest.getGroupName(), reqUser.getId());
@RequestHeader("Authorization") String jwt) throws ChatException, UserException {
UUID reqUserId = jwtTokenProvider.getIdFromToken(jwt);
Chat chat = chatService.renameGroup(chatId, renameGroupRequest.getGroupName(), reqUserId);
ChatDTO chatDto = chatDTOMapper.toChatDto(chat);
return new ResponseEntity<>(chatDto, HttpStatus.OK);
}
Expand All @@ -197,8 +203,8 @@ public ResponseEntity<ChatDTO> removeFromGroupHandler(@RequestHeader("Authorizat
@PathVariable UUID chatId,
@PathVariable UUID userId)
throws UserException, ChatException {
User reqUser=userService.findUserProfile(jwt);
Chat chat = chatService.removeFromGroup(chatId, userId, reqUser.getId());
UUID reqUser = jwtTokenProvider.getIdFromToken(jwt);
Chat chat = chatService.removeFromGroup(chatId, userId, reqUser);
ChatDTO chatDto = chatDTOMapper.toChatDto(chat);
return new ResponseEntity<>(chatDto, HttpStatus.OK);
}
Expand All @@ -220,8 +226,10 @@ public ResponseEntity<ChatDTO> removeFromGroupHandler(@RequestHeader("Authorizat
@ApiResponse(responseCode = "403", description = "Unauthorized action")
})
public ResponseEntity<ChatDTO> deleteChatHandler(@PathVariable UUID chatId,
@PathVariable UUID userId) throws ChatException, UserException{
Chat chat = chatService.deleteChat(chatId, userId);
@PathVariable UUID userId,
@RequestHeader("Authorization") String jwt) throws ChatException, UserException{
UUID reqUserId = jwtTokenProvider.getIdFromToken(jwt);
Chat chat = chatService.deleteChat(chatId, userId, reqUserId);
ChatDTO chatDto = chatDTOMapper.toChatDto(chat);
return new ResponseEntity<>(chatDto, HttpStatus.OK);
}
Expand All @@ -247,10 +255,10 @@ public ResponseEntity<List<ChatSummaryDTO>> getChatSummariesHandler(
@RequestParam(value = "size", defaultValue = "10") int size) throws ChatException {
try {
log.info("Fetching chat summaries for user");
User user = userService.findUserProfile(jwt);
UUID userId = jwtTokenProvider.getIdFromToken(jwt);
// Fetch chat summaries with pagination
List<ChatSummaryDTO> chatSummaries = chatService.getChatSummaries(user.getId(), page, size);
log.info("Retrieved {} chat summaries for user ID: {}", chatSummaries.size(), user.getId());
List<ChatSummaryDTO> chatSummaries = chatService.getChatSummaries(userId, page, size);
log.info("Retrieved {} chat summaries for user ID: {}", chatSummaries.size(), userId);
return new ResponseEntity<>(chatSummaries, HttpStatus.OK);
} catch (ChatException e) {
log.error("User error fetching chat summaries: {}", e.getMessage());
Expand Down
Loading