Please bare with me as I am learning Spring Data REST as I go. Definitely feel free to suggest a safer approach if what I am proposing here is not the safest approach or even possible.
Problem
A user logs into my Spring Data REST API via Google OAuth2. After logging in, I need to get the user's ID, which is just the value of their primary key, from the User table. The reason I need the ID is to restrict access to endpoints such as /users/{id}. If a user's ID is 1, then he is only allowed to view /users/1, unless he is an Administrator.
Current Login Architecture
This portion of the application works as expected:
OAuth2AuthenticationSuccessHandler.java:
@Component("oauth2authSuccessHandler")
public class OAuth2AuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Autowired
private UserDataRestRepository userRepository;
@Autowired
private RandomStringGenerator randomStringGenerator;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
OAuth2AuthenticationToken authenticationToken = (OAuth2AuthenticationToken) authentication;
String email = authenticationToken.getPrincipal().getAttributes().get("email").toString();
if (!userRepository.existsByEmail(email)) {
// If email not found, create local user account.
String firstName = CaseUtils.toCamelCase(
authenticationToken.getPrincipal().getAttributes().get("given_name").toString().toLowerCase(),
true);
String lastName = CaseUtils.toCamelCase(
authenticationToken.getPrincipal().getAttributes().get("family_name").toString().toLowerCase(),
true);
// Generate temporary username.
BigInteger usernameSuffix = userRepository.getNextValSeqUserUsername();
String username = "User" + usernameSuffix;
// Generate temporary password.
String password = randomStringGenerator.generate(20).toUpperCase();
// Encode Password.
String encodedPassword = passwordEncoder.encode(password);
User newUser = new User();
newUser.setFirstName(firstName);
newUser.setLastName(lastName);
newUser.setUsername(username);
newUser.setUserSetUsername(false);
newUser.setEmail(email);
newUser.setPassword(encodedPassword);
newUser.setUserSetPassword(false);
newUser.setCreated(new Timestamp(System.currentTimeMillis()));
newUser.setDisabled(false);
newUser.setLocked(false);
userRepository.save(newUser);
}
// Redirect to root page.
redirectStrategy.sendRedirect(request, response, "/");
}
}
UserDataRestRepository.java:
@RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface UserDataRestRepository extends PagingAndSortingRepository<User, Integer>, CrudRepository<User, Integer> {
public boolean existsByEmail(String email);
@Query(value="SELECT NEXT VALUE FOR Seq_User_Username", nativeQuery=true)
public BigInteger getNextValSeqUserUsername();
}
OAuth2LoginSecurityConfig.java:
@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {
@Autowired
private AuthenticationSuccessHandler oauth2authSuccessHandler;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorize -> authorize
.anyRequest()
.authenticated()
)
.oauth2Login()
.successHandler(oauth2authSuccessHandler);
return http.build();
}
}
Database Tables
User:
Role:
UserRole:
Current Post-authentication Problems
Currently, after a user logs in via OAuth2, we do not have access to the user's ID (which is the value of their primary key in the User table) unless we access the Authentication
object, get the email address, then get the user's ID from the User table based on the email address. Basically: Select Id from User where Email = user@example.com
Proposed Login Architecture
I am wondering if the UserDetailsService can solve the problem, illustrated in green:
My train of thought is: by getting the user's info from the User table, then loading it into the UserDetailsService
, the entire application now has access to the user's ID (primary key) and all of the other info in that row via the UserDetailsService
.
Thanks for your help.