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 claims = new HashMap<>(); claims.put("username", username);

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 getUserRoles(String token) { // Parse the token to extract the username or any other necessary information Claims claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody(); String username = claims.get("username", String.class);

// 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 entity = new HttpEntity<>(headers);

// Make the request to the GitHub API ResponseEntity response = restTemplate.exchange(apiUrl, HttpMethod.GET, entity, Map.class); if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) { Map responseBody = response.getBody(); return responseBody.get("login").toString(); } else { throw new RuntimeException("Failed to retrieve user information from GitHub"); } } }

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 roles = userManagementService.getUserRoles(token);

// 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 getUserRoles(String token); void storeToken(String username, String token); }

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 { User findByUsername(String username); User findByEmail(String email); List findByFirstName(String firstName); List findByLastName(String lastName); void deleteByUsername(String username); }

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

1 Expert Approved Answer
Step: 1 Unlock blur-text-image
Question Has Been Solved by an Expert!

Get step-by-step solutions from verified subject matter experts

Step: 2 Unlock
Step: 3 Unlock

Students Have Also Explored These Related Databases Questions!