Question: Instructions for service that needs to be created: You need to build the OAuth 2.0 Authorization Code Grant Flow in accordance with RFC 6749. As
Instructions for service that needs to be created:
You need to build the OAuth 2.0 Authorization Code Grant Flow in accordance with RFC 6749.
As an Authorization Server you will be using GitHub, all other services will be your own, working on your localhost.
Need to create UserManagementService in such a way that it will generate a token based on data, obtained from the Authorization Provider (in this particular case it is GitHub) after a user tries to log in.
Once you have received the user data, the UserManagementService must generate a simple UUID token, correlated to the user logged in, and store that token inside the UMS database until the user logs in, or until the session expires (lets say 15 minutes).
In the next step, when the user tries to reach API of another service, the user will provide that token as part of the request and another service will ask the UserManagementService the following questions (through another call to UMS):
Whether that token valid (not expired);
If yes, which role is that token associated with.
At the end of this, depending on the UMSs response, the target service should either perform the request or return a 401 status.
I've already done a bunch of work on this project, but need a second set of eyes to see if there is anything missing to meet requirements and make this code that can be properly built - here is the code I've already written:
JwtUserManagementService
package com.mycompany.chirper;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID;
public class JwtUserManagementService implements UserManagementService {
private static final String SECRET_KEY = "TTuJSeezJG5UI3YwKXfDJUHkMH4bOOav"; private static final long TOKEN_EXPIRATION_TIME = 900000; // 15 minutes in milliseconds
private UserRepository userRepository;
public JwtUserManagementService(UserRepository userRepository) { this.userRepository = userRepository; }
@Override public String generateToken(String username) { Date now = new Date(); Date expiration = new Date(now.getTime() + TOKEN_EXPIRATION_TIME);
String token = UUID.randomUUID().toString();
Map
storeToken(username, token); // Store the token in the database
return Jwts.builder() .setClaims(claims) .setIssuedAt(now) .setExpiration(expiration) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); }
@Override public void storeToken(String username, String token) { // Implement the logic to store the token in the UMS database along with the user's information // For example, you can use a database repository to store the token and associate it with the user // Here's an example using Spring Data JPA: User user = userRepository.findByUsername(username); if (user != null) { user.setToken(token); userRepository.save(user); } else { throw new RuntimeException("User not found"); } }
@Override public boolean isTokenValid(String token) { try { Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token); return true; } catch (Exception e) { return false; } }
@Override public List
// Implement the logic to retrieve user roles from the UMS based on the username // For example, you can use a database repository to fetch the roles associated with the user // Here's an example using Spring Data JPA: User user = userRepository.findByUsername(username); if (user != null) { return user.getRoles(); } else { throw new RuntimeException("User not found for the given token"); } } }
OAuthController
package com.mycompany.chirper;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.client.RestTemplate;
@Controller public class OAuthController {
private final UserManagementService userManagementService; private final OAuth2AuthorizedClientService authorizedClientService;
// Constructor injection public OAuthController(UserManagementService userManagementService, OAuth2AuthorizedClientService authorizedClientService) { this.userManagementService = userManagementService; this.authorizedClientService = authorizedClientService; }
@GetMapping("/login/oauth2/code/github") public String handleGitHubCallback(@RequestParam("code") String code, OAuth2AuthenticationToken authentication) { // Exchange code for access token String accessToken = exchangeCodeForAccessToken(code, authentication);
// Extract user information from GitHub using the access token String username = extractUsernameFromGitHub(accessToken);
// Generate and store a token in UserManagementService String token = userManagementService.generateToken(username);
// Store the token in the UserManagementService database userManagementService.storeToken(username, token);
// Redirect or respond as needed return "redirect:/"; }
private String exchangeCodeForAccessToken(String code, OAuth2AuthenticationToken authentication) { String clientRegistrationId = authentication.getAuthorizedClientRegistrationId(); OAuth2AuthorizedClient authorizedClient = authorizedClientService.loadAuthorizedClient(clientRegistrationId, authentication.getName()); String tokenUri = authorizedClient.getClientRegistration().getProviderDetails().getTokenUri();
// Prepare the request to exchange code for access token RestTemplate restTemplate = new RestTemplate(); String requestBody = "code=" + code + "&client_id=" + authorizedClient.getClientRegistration().getClientId() + "&client_secret=" + authorizedClient.getClientRegistration().getClientSecret() + "&redirect_uri=" + authorizedClient.getClientRegistration().getRedirectUri() + "&grant_type=authorization_code";
// Make the request to the token endpoint AccessTokenResponse response = restTemplate.postForObject(tokenUri, requestBody, AccessTokenResponse.class); if (response != null) { return response.getAccessToken(); } else { throw new RuntimeException("Failed to exchange code for access token"); } }
private String extractUsernameFromGitHub(String accessToken) { // Prepare the request to retrieve user information from GitHub RestTemplate restTemplate = new RestTemplate(); String apiUrl = "GitHub API URL will go here"; String authorizationHeader = "Bearer " + accessToken;
// Set the authorization header HttpHeaders headers = new HttpHeaders(); headers.set("Authorization", authorizationHeader); HttpEntity
// Make the request to the GitHub API ResponseEntity
MyApiController
package com.mycompany.chirper;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController;
@RestController public class MyApiController {
private final UserManagementService userManagementService;
// Constructor injection public MyApiController(UserManagementService userManagementService) { this.userManagementService = userManagementService; }
@GetMapping("/api/user") @PreAuthorize("hasAuthority('ROLE_USER')") public String secureApiEndpoint(@RequestParam("token") String token) { // Check if the token is valid if (userManagementService.isTokenValid(token)) { // Get user roles from UMS List
// Check if the user has the required role if (roles.contains("ROLE_USER")) { // Your API logic return "Hello, authenticated user!"; } }
// If the token is not valid or the user doesn't have the required role // Return a 401 status or handle the unauthorized request accordingly return "Unauthorized"; } }
UserManagementService interface
package com.mycompany.chirper;
public interface UserManagementService { String generateToken(String username); boolean isTokenValid(String token); String getUsernameFromToken(String token); List
UserRepository interface
package com.mycompany.chirper;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository;
import java.util.List;
@Repository public interface UserRepository extends JpaRepository
Please let me know if I am missing anything to meet the specified requirements above, and any details on where I might be going wrong.
Step by Step Solution
There are 3 Steps involved in it
Get step-by-step solutions from verified subject matter experts
