|
13 | 13 | import org.springframework.security.crypto.password.PasswordEncoder; |
14 | 14 | import org.springframework.stereotype.Service; |
15 | 15 |
|
| 16 | +import java.security.NoSuchAlgorithmException; |
| 17 | +import java.security.SecureRandom; |
| 18 | +import java.util.Base64; |
| 19 | + |
16 | 20 | @Service |
17 | 21 | public class UserAuthenticationService implements UserDetailsService { |
18 | 22 | private final UserAccountRepository userAccountRepository; |
@@ -69,4 +73,57 @@ public void register(String firstName, String lastName, String emailAddress, Str |
69 | 73 | var passwordHash = passwordEncoder.encode(password); |
70 | 74 | studentRepository.insertStudent(firstName, lastName, emailAddress, passwordHash); |
71 | 75 | } |
| 76 | + |
| 77 | + /** |
| 78 | + * Generates a new API key for the current authenticated user. |
| 79 | + * @return the generated API key |
| 80 | + * @throws IllegalStateException if no user is authenticated |
| 81 | + */ |
| 82 | + public String generateApiKey() { |
| 83 | + AuthenticatedUser authenticatedUser = currentAuthenticatedUser(); |
| 84 | + if (authenticatedUser == null) { |
| 85 | + throw new IllegalStateException("No authenticated user found"); |
| 86 | + } |
| 87 | + |
| 88 | + // Generate a secure random API key with a prefix to identify it as an API key |
| 89 | + byte[] randomBytes = new byte[32]; |
| 90 | + try { |
| 91 | + SecureRandom.getInstanceStrong().nextBytes(randomBytes); |
| 92 | + } catch (NoSuchAlgorithmException e) { |
| 93 | + throw new IllegalStateException("Unable to generate an authentication token", e); |
| 94 | + } |
| 95 | + String apiKey = "chub_" + Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes); |
| 96 | + |
| 97 | + // Store the API key in the database |
| 98 | + userAccountRepository.updateApiKey(authenticatedUser.getUserId(), apiKey); |
| 99 | + |
| 100 | + return apiKey; |
| 101 | + } |
| 102 | + |
| 103 | + /** |
| 104 | + * Finds a user by their API key. |
| 105 | + * @param apiKey the API key |
| 106 | + * @return the authenticated user or null if not found |
| 107 | + */ |
| 108 | + public AuthenticatedUser findUserByApiKey(String apiKey) { |
| 109 | + UserAccountEntity userAccount = userAccountRepository.findByApiKey(apiKey); |
| 110 | + if (userAccount == null) { |
| 111 | + return null; |
| 112 | + } |
| 113 | + |
| 114 | + return buildAuthenticatedUser(userAccount); |
| 115 | + } |
| 116 | + |
| 117 | + private AuthenticatedUser buildAuthenticatedUser(UserAccountEntity userAccount) { |
| 118 | + return switch (userAccount.role()) { |
| 119 | + case student -> { |
| 120 | + StudentEntity student = studentRepository.findById(userAccount.userId()); |
| 121 | + yield new AuthenticatedUser(userAccount.userId(), student.firstName(), student.lastName(), userAccount.emailAddress(), userAccount.passwordHash(), userAccount.role()); |
| 122 | + } |
| 123 | + case instructor -> { |
| 124 | + InstructorEntity instructor = instructorRepository.findById(userAccount.userId()); |
| 125 | + yield new AuthenticatedUser(userAccount.userId(), instructor.firstName(), instructor.lastName(), userAccount.emailAddress(), userAccount.passwordHash(), userAccount.role()); |
| 126 | + } |
| 127 | + }; |
| 128 | + } |
72 | 129 | } |
0 commit comments